Removed away include of system headers in comstack.h. Instead
[yaz-moved-to-github.git] / src / unix.c
1 /*
2  * Copyright (C) 1995-2005, Index Data ApS
3  * See the file LICENSE for details.
4  *
5  * $Id: unix.c,v 1.9 2005-01-16 21:51:50 adam Exp $
6  * UNIX socket COMSTACK. By Morten Bøgeskov.
7  */
8 /**
9  * \file unix.c
10  * \brief Implements UNIX domain socket COMSTACK
11  */
12
13 #ifndef WIN32
14
15 #include <stdio.h>
16 #include <string.h>
17 #include <stdlib.h>
18 #include <errno.h>
19 #if HAVE_UNISTD_H
20 #include <unistd.h>
21 #endif
22 #include <fcntl.h>
23 #include <signal.h>
24
25 #include <grp.h>
26 #if HAVE_PWD_H
27 #include <pwd.h>
28 #endif
29 #if HAVE_SYS_TYPES_H
30 #include <sys/types.h>
31 #endif
32
33 #if HAVE_SYS_SOCKET_H
34 #include <sys/socket.h>
35 #endif
36 #if HAVE_SYS_STAT_H
37 #include <sys/stat.h>
38 #endif
39 #if HAVE_SYS_UN_H
40 #include <sys/un.h>
41 #endif
42
43 #include <yaz/unix.h>
44 #include <yaz/nmem.h>
45
46 #ifndef YAZ_SOCKLEN_T
47 #define YAZ_SOCKLEN_T int
48 #endif
49
50 static int unix_close(COMSTACK h);
51 static int unix_put(COMSTACK h, char *buf, int size);
52 static int unix_get(COMSTACK h, char **buf, int *bufsize);
53 static int unix_connect(COMSTACK h, void *address);
54 static int unix_more(COMSTACK h);
55 static int unix_rcvconnect(COMSTACK h);
56 static int unix_bind(COMSTACK h, void *address, int mode);
57 static int unix_listen(COMSTACK h, char *raddr, int *addrlen,
58                 int (*check_ip)(void *cd, const char *a, int len, int type),
59                 void *cd);
60 static int unix_set_blocking(COMSTACK p, int blocking);
61
62 static COMSTACK unix_accept(COMSTACK h);
63 static char *unix_addrstr(COMSTACK h);
64 static void *unix_straddr(COMSTACK h, const char *str);
65
66 #ifndef SUN_LEN
67 #define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \
68                       + strlen ((ptr)->sun_path))
69 #endif
70 #if 0
71 #define TRC(x) x
72 #else
73 #define TRC(X)
74 #endif
75
76 /* this state is used for both SSL and straight TCP/IP */
77 typedef struct unix_state
78 {
79     char *altbuf; /* alternate buffer for surplus data */
80     int altsize;  /* size as xmalloced */
81     int altlen;   /* length of data or 0 if none */
82
83     int written;  /* -1 if we aren't writing */
84     int towrite;  /* to verify against user input */
85     int (*complete)(const unsigned char *buf, int len); /* length/comple. */
86     struct sockaddr_un addr;  /* returned by cs_straddr */
87     int uid;
88     int gid;
89     int umask;
90     char buf[128]; /* returned by cs_addrstr */
91 } unix_state;
92
93 static int unix_init (void)
94 {
95     return 1;
96 }
97
98 /*
99  * This function is always called through the cs_create() macro.
100  * s >= 0: socket has already been established for us.
101  */
102 COMSTACK unix_type(int s, int blocking, int protocol, void *vp)
103 {
104     COMSTACK p;
105     unix_state *state;
106     int new_socket;
107
108     if (!unix_init ())
109         return 0;
110     if (s < 0)
111     {
112         if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
113             return 0;
114         new_socket = 1;
115     }
116     else
117         new_socket = 0;
118     if (!(p = (struct comstack *)xmalloc(sizeof(struct comstack))))
119         return 0;
120     if (!(state = (struct unix_state *)(p->cprivate =
121                                         xmalloc(sizeof(unix_state)))))
122         return 0;
123
124     if (!((p->blocking = blocking)&1))
125     {
126         if (fcntl(s, F_SETFL, O_NONBLOCK) < 0)
127             return 0;
128 #ifndef MSG_NOSIGNAL
129         signal (SIGPIPE, SIG_IGN);
130 #endif
131     }
132
133     p->io_pending = 0;
134     p->iofile = s;
135     p->type = unix_type;
136     p->protocol = (enum oid_proto) protocol;
137
138     p->f_connect = unix_connect;
139     p->f_rcvconnect = unix_rcvconnect;
140     p->f_get = unix_get;
141     p->f_put = unix_put;
142     p->f_close = unix_close;
143     p->f_more = unix_more;
144     p->f_bind = unix_bind;
145     p->f_listen = unix_listen;
146     p->f_accept = unix_accept;
147     p->f_addrstr = unix_addrstr;
148     p->f_straddr = unix_straddr;
149     p->f_set_blocking = unix_set_blocking;
150
151     p->state = new_socket ? CS_ST_UNBND : CS_ST_IDLE; /* state of line */
152     p->event = CS_NONE;
153     p->cerrno = 0;
154     p->stackerr = 0;
155
156     state->altbuf = 0;
157     state->altsize = state->altlen = 0;
158     state->towrite = state->written = -1;
159     if (protocol == PROTO_WAIS)
160         state->complete = completeWAIS;
161     else
162         state->complete = cs_complete_auto;
163
164     p->timeout = COMSTACK_DEFAULT_TIMEOUT;
165     TRC(fprintf(stderr, "Created new UNIX comstack\n"));
166
167     return p;
168 }
169
170
171 static int unix_strtoaddr_ex(const char *str, struct sockaddr_un *add)
172 {
173     char *cp;
174     if (!unix_init ())
175         return 0;
176     TRC(fprintf(stderr, "unix_strtoaddress: %s\n", str ? str : "NULL"));
177     add->sun_family = AF_UNIX;
178     strncpy(add->sun_path, str, sizeof(add->sun_path));
179     cp = strchr (add->sun_path, ':');
180     if (cp)
181         *cp = '\0';
182     return 1;
183 }
184
185 static void *unix_straddr(COMSTACK h, const char *str)
186 {
187     unix_state *sp = (unix_state *)h->cprivate;
188     char * s = strdup(str);
189     char * f = s;
190     const char * file = NULL;
191     char * eol;
192
193     sp->uid = sp->gid = sp->umask = -1;
194
195     if ((eol = strchr(s, ',')))
196     {
197         do
198         {
199             if ((eol = strchr(s, ',')))
200                 *eol++ = '\0';
201             if (sp->uid  == -1 && strncmp(s, "user=",  5) == 0)
202             {
203                 char * arg = s + 5;
204                 if (strspn(arg, "0123456789") == strlen(arg))
205                 {
206                     sp->uid = atoi(arg);
207                 }
208                 else
209                 {
210                     struct passwd * pw = getpwnam(arg);
211                     if(pw == NULL)
212                     {
213                         printf("No such user\n");
214                         free(f);
215                         return 0;
216                     }
217                     sp->uid = pw->pw_uid;
218                 }
219             }
220             else if (sp->gid == -1 && strncmp(s, "group=", 6) == 0)
221             {
222                 char * arg = s + 6;
223                 if (strspn(arg, "0123456789") == strlen(arg))
224                 {
225                     sp->gid = atoi(arg);
226                 }
227                 else
228                 {
229                     struct group * gr = getgrnam(arg);
230                     if (gr == NULL)
231                     {
232                         printf("No such group\n");
233                         free(f);
234                         return 0;
235                     }
236                     sp->gid = gr->gr_gid;
237                 }
238             }
239             else if (sp->umask == -1 && strncmp(s, "umask=", 6) == 0)
240             {
241                 char * end;
242                 char * arg = s + 6;
243                 
244                 sp->umask = strtol(arg, &end, 8);
245                 if (errno == EINVAL ||
246                     *end)
247                 {
248                     printf("Invalid umask\n");
249                     free(f);
250                     return 0;
251                 }
252             }
253             else if (file == NULL && strncmp(s, "file=", 5) == 0)
254             {
255                 char * arg = s + 5;
256                 file = arg;
257             }
258             else
259             {
260                 printf("invalid or double argument: %s\n", s);
261                 free(f);
262                 return 0;
263             }
264         } while((s = eol));
265     }
266     else
267     {
268         file = str;
269     }
270     if(! file)
271     {
272         errno = EINVAL;
273         return 0;
274     }
275
276     TRC(fprintf(stderr, "unix_straddr: %s\n", str ? str : "NULL"));
277
278     if (!unix_strtoaddr_ex (file, &sp->addr))
279     {
280         free(f);
281         return 0;
282     }
283     free(f);
284     return &sp->addr;
285 }
286
287 struct sockaddr_un *unix_strtoaddr(const char *str)
288 {
289     static struct sockaddr_un add;
290
291     TRC(fprintf(stderr, "unix_strtoaddr: %s\n", str ? str : "NULL"));
292
293     if (!unix_strtoaddr_ex (str, &add))
294         return 0;
295     return &add;
296 }
297
298 static int unix_more(COMSTACK h)
299 {
300     unix_state *sp = (unix_state *)h->cprivate;
301
302     return sp->altlen && (*sp->complete)((unsigned char *) sp->altbuf,
303                                          sp->altlen);
304 }
305
306 /*
307  * connect(2) will block (sometimes) - nothing we can do short of doing
308  * weird things like spawning subprocesses or threading or some weird junk
309  * like that.
310  */
311 static int unix_connect(COMSTACK h, void *address)
312 {
313     struct sockaddr_un *add = (struct sockaddr_un *)address;
314     int r;
315
316     TRC(fprintf(stderr, "unix_connect\n"));
317     h->io_pending = 0;
318     if (h->state != CS_ST_UNBND)
319     {
320         h->cerrno = CSOUTSTATE;
321         return -1;
322     }
323     r = connect(h->iofile, (struct sockaddr *) add, SUN_LEN(add));
324     if (r < 0)
325     {
326         if (yaz_errno() == EINPROGRESS)
327         {
328             h->event = CS_CONNECT;
329             h->state = CS_ST_CONNECTING;
330             h->io_pending = CS_WANT_WRITE|CS_WANT_READ;
331             return 1;
332         }
333         h->cerrno = CSYSERR;
334         return -1;
335     }
336     h->event = CS_CONNECT;
337     h->state = CS_ST_CONNECTING;
338
339     return unix_rcvconnect (h);
340 }
341
342 /*
343  * nop
344  */
345 static int unix_rcvconnect(COMSTACK h)
346 {
347     TRC(fprintf(stderr, "unix_rcvconnect\n"));
348
349     if (h->state == CS_ST_DATAXFER)
350         return 0;
351     if (h->state != CS_ST_CONNECTING)
352     {
353         h->cerrno = CSOUTSTATE;
354         return -1;
355     }
356     h->event = CS_DATA;
357     h->state = CS_ST_DATAXFER;
358     return 0;
359 }
360
361 static int unix_bind(COMSTACK h, void *address, int mode)
362 {
363     unix_state *sp = (unix_state *)h->cprivate;
364     struct sockaddr *addr = (struct sockaddr *)address;
365     const char * path = ((struct sockaddr_un *)addr)->sun_path;
366     struct stat stat_buf;
367
368     TRC (fprintf (stderr, "unix_bind\n"));
369
370     if(stat(path, &stat_buf) != -1) {
371         struct sockaddr_un socket_unix;
372         int socket_out = -1;
373         if(! S_ISSOCK(stat_buf.st_mode)) {
374             h->cerrno = CSYSERR;
375             yaz_set_errno(EEXIST); /* Not a socket (File exists) */
376             return -1;
377         }
378         if((socket_out = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
379             h->cerrno = CSYSERR;
380             return -1;
381         }
382         socket_unix.sun_family = AF_UNIX;
383         strncpy(socket_unix.sun_path, path, sizeof(socket_unix.sun_path));
384         if(connect(socket_out, (struct sockaddr *) &socket_unix, SUN_LEN(&socket_unix)) < 0) {
385             if(yaz_errno() == ECONNREFUSED) {
386                 TRC (fprintf (stderr, "Socket exists but nobody is listening\n"));
387             } else {
388                 h->cerrno = CSYSERR;
389                 return -1;
390             }
391         } else {
392             close(socket_out);
393             h->cerrno = CSYSERR;
394             yaz_set_errno(EADDRINUSE);
395             return -1;
396         }
397         unlink(path);
398     }
399
400     if (bind(h->iofile, (struct sockaddr *) addr, SUN_LEN((struct sockaddr_un *)addr)))
401     {
402         h->cerrno = CSYSERR;
403         return -1;
404     }
405     chown(path, sp->uid, sp->gid);
406     chmod(path, sp->umask != -1 ? sp->umask : 0666);
407     if (mode == CS_SERVER && listen(h->iofile, 3) < 0)
408     {
409         h->cerrno = CSYSERR;
410         return -1;
411     }
412     h->state = CS_ST_IDLE;
413     h->event = CS_LISTEN;
414     return 0;
415 }
416
417 static int unix_listen(COMSTACK h, char *raddr, int *addrlen,
418                     int (*check_ip)(void *cd, const char *a, int len, int t),
419                     void *cd)
420 {
421     struct sockaddr_un addr;
422     YAZ_SOCKLEN_T len = sizeof(addr);
423
424     TRC(fprintf(stderr, "unix_listen pid=%d\n", getpid()));
425     if (h->state != CS_ST_IDLE)
426     {
427         h->cerrno = CSOUTSTATE;
428         return -1;
429     }
430     h->newfd = accept(h->iofile, (struct sockaddr*)&addr, &len);
431     if (h->newfd < 0)
432     {
433         if (
434             yaz_errno() == EWOULDBLOCK
435 #ifdef EAGAIN
436 #if EAGAIN != EWOULDBLOCK
437             || yaz_errno() == EAGAIN
438 #endif
439 #endif
440             )
441             h->cerrno = CSNODATA;
442         else
443             h->cerrno = CSYSERR;
444         return -1;
445     }
446     if (addrlen && (size_t) (*addrlen) >= sizeof(struct sockaddr_un))
447         memcpy(raddr, &addr, *addrlen = sizeof(struct sockaddr_un));
448     else if (addrlen)
449         *addrlen = 0;
450     h->state = CS_ST_INCON;
451     return 0;
452 }
453
454 static COMSTACK unix_accept(COMSTACK h)
455 {
456     COMSTACK cnew;
457     unix_state *state, *st = (unix_state *)h->cprivate;
458
459     TRC(fprintf(stderr, "unix_accept\n"));
460     if (h->state == CS_ST_INCON)
461     {
462         if (!(cnew = (COMSTACK)xmalloc(sizeof(*cnew))))
463         {
464             h->cerrno = CSYSERR;
465             close(h->newfd);
466             h->newfd = -1;
467             return 0;
468         }
469         memcpy(cnew, h, sizeof(*h));
470         cnew->iofile = h->newfd;
471         cnew->io_pending = 0;
472         if (!(state = (unix_state *)
473               (cnew->cprivate = xmalloc(sizeof(unix_state)))))
474         {
475             h->cerrno = CSYSERR;
476             if (h->newfd != -1)
477             {
478                 close(h->newfd);
479                 h->newfd = -1;
480             }
481             return 0;
482         }
483         if (!(cnew->blocking&1) && 
484             (fcntl(cnew->iofile, F_SETFL, O_NONBLOCK) < 0)
485             )
486         {
487             h->cerrno = CSYSERR;
488             if (h->newfd != -1)
489             {
490                 close(h->newfd);
491                 h->newfd = -1;
492             }
493             xfree (cnew);
494             xfree (state);
495             return 0;
496         }
497         h->newfd = -1;
498         state->altbuf = 0;
499         state->altsize = state->altlen = 0;
500         state->towrite = state->written = -1;
501         state->complete = st->complete;
502         memcpy(&state->addr, &st->addr, sizeof(state->addr));
503         cnew->state = CS_ST_ACCEPT;
504         cnew->event = CS_NONE;
505         h->state = CS_ST_IDLE;
506
507         h = cnew;
508     }
509     if (h->state == CS_ST_ACCEPT)
510     {
511     }
512     else
513     {
514         h->cerrno = CSOUTSTATE;
515         return 0;
516     }
517     h->io_pending = 0;
518     h->state = CS_ST_DATAXFER;
519     h->event = CS_DATA;
520     return h;
521 }
522
523 #define CS_UNIX_BUFCHUNK 4096
524
525 /*
526  * Return: -1 error, >1 good, len of buffer, ==1 incomplete buffer,
527  * 0=connection closed.
528  */
529 static int unix_get(COMSTACK h, char **buf, int *bufsize)
530 {
531     unix_state *sp = (unix_state *)h->cprivate;
532     char *tmpc;
533     int tmpi, berlen, rest, req, tomove;
534     int hasread = 0, res;
535
536     TRC(fprintf(stderr, "unix_get: bufsize=%d\n", *bufsize));
537     if (sp->altlen) /* switch buffers */
538     {
539         TRC(fprintf(stderr, "  %d bytes in altbuf (0x%x)\n", sp->altlen,
540                     (unsigned) sp->altbuf));
541         tmpc = *buf;
542         tmpi = *bufsize;
543         *buf = sp->altbuf;
544         *bufsize = sp->altsize;
545         hasread = sp->altlen;
546         sp->altlen = 0;
547         sp->altbuf = tmpc;
548         sp->altsize = tmpi;
549     }
550     h->io_pending = 0;
551     while (!(berlen = (*sp->complete)((unsigned char *)*buf, hasread)))
552     {
553         if (!*bufsize)
554         {
555             if (!(*buf = (char *)xmalloc(*bufsize = CS_UNIX_BUFCHUNK)))
556                 return -1;
557         }
558         else if (*bufsize - hasread < CS_UNIX_BUFCHUNK)
559             if (!(*buf =(char *)xrealloc(*buf, *bufsize *= 2)))
560                 return -1;
561         res = recv(h->iofile, *buf + hasread, CS_UNIX_BUFCHUNK, 0);
562         TRC(fprintf(stderr, "  recv res=%d, hasread=%d\n", res, hasread));
563         if (res < 0)
564         {
565             if (yaz_errno() == EWOULDBLOCK
566 #ifdef EAGAIN
567 #if EAGAIN != EWOULDBLOCK
568                 || yaz_errno() == EAGAIN
569 #endif
570 #endif
571                 || yaz_errno() == EINPROGRESS
572                 )
573             {
574                 h->io_pending = CS_WANT_READ;
575                 break;
576             }
577             else if (yaz_errno() == 0)
578                 continue;
579             else
580                 return -1;
581         }
582         else if (!res)
583             return hasread;
584         hasread += res;
585     }
586     TRC (fprintf (stderr, "  Out of read loop with hasread=%d, berlen=%d\n",
587                   hasread, berlen));
588     /* move surplus buffer (or everything if we didn't get a BER rec.) */
589     if (hasread > berlen)
590     {
591         tomove = req = hasread - berlen;
592         rest = tomove % CS_UNIX_BUFCHUNK;
593         if (rest)
594             req += CS_UNIX_BUFCHUNK - rest;
595         if (!sp->altbuf)
596         {
597             if (!(sp->altbuf = (char *)xmalloc(sp->altsize = req)))
598                 return -1;
599         } else if (sp->altsize < req)
600             if (!(sp->altbuf =(char *)xrealloc(sp->altbuf, sp->altsize = req)))
601                 return -1;
602         TRC(fprintf(stderr, "  Moving %d bytes to altbuf(0x%x)\n", tomove,
603                     (unsigned) sp->altbuf));
604         memcpy(sp->altbuf, *buf + berlen, sp->altlen = tomove);
605     }
606     if (berlen < CS_UNIX_BUFCHUNK - 1)
607         *(*buf + berlen) = '\0';
608     return berlen ? berlen : 1;
609 }
610
611
612
613 /*
614  * Returns 1, 0 or -1
615  * In nonblocking mode, you must call again with same buffer while
616  * return value is 1.
617  */
618 static int unix_put(COMSTACK h, char *buf, int size)
619 {
620     int res;
621     struct unix_state *state = (struct unix_state *)h->cprivate;
622
623     TRC(fprintf(stderr, "unix_put: size=%d\n", size));
624     h->io_pending = 0;
625     h->event = CS_DATA;
626     if (state->towrite < 0)
627     {
628         state->towrite = size;
629         state->written = 0;
630     }
631     else if (state->towrite != size)
632     {
633         h->cerrno = CSWRONGBUF;
634         return -1;
635     }
636     while (state->towrite > state->written)
637     {
638         if ((res =
639              send(h->iofile, buf + state->written, size -
640                   state->written,
641 #ifdef MSG_NOSIGNAL
642                   MSG_NOSIGNAL
643 #else
644                   0
645 #endif
646                  )) < 0)
647         {
648             if (
649                 yaz_errno() == EWOULDBLOCK
650 #ifdef EAGAIN
651 #if EAGAIN != EWOULDBLOCK
652                 || yaz_errno() == EAGAIN
653 #endif
654 #endif
655                 )
656             {
657                 TRC(fprintf(stderr, "  Flow control stop\n"));
658                 h->io_pending = CS_WANT_WRITE;
659                 return 1;
660             }
661             h->cerrno = CSYSERR;
662             return -1;
663         }
664         state->written += res;
665         TRC(fprintf(stderr, "  Wrote %d, written=%d, nbytes=%d\n",
666                     res, state->written, size));
667     }
668     state->towrite = state->written = -1;
669     TRC(fprintf(stderr, "  Ok\n"));
670     return 0;
671 }
672
673 static int unix_close(COMSTACK h)
674 {
675     unix_state *sp = (struct unix_state *)h->cprivate;
676
677     TRC(fprintf(stderr, "unix_close\n"));
678     if (h->iofile != -1)
679     {
680         close(h->iofile);
681     }
682     if (sp->altbuf)
683         xfree(sp->altbuf);
684     xfree(sp);
685     xfree(h);
686     return 0;
687 }
688
689 static char *unix_addrstr(COMSTACK h)
690 {
691     unix_state *sp = (struct unix_state *)h->cprivate;
692     char *buf = sp->buf;
693     sprintf(buf, "unix:%s", sp->addr.sun_path);
694     return buf;
695 }
696
697 static int unix_set_blocking(COMSTACK p, int blocking)
698 {
699     unsigned long flag;
700
701     if (p->blocking == blocking)
702         return 1;
703     flag = fcntl(p->iofile, F_GETFL, 0);
704     if(!blocking)
705         flag = flag & ~O_NONBLOCK;
706     else
707         flag = flag | O_NONBLOCK;
708     if (fcntl(p->iofile, F_SETFL, flag) < 0)
709         return 0;
710     p->blocking = blocking;
711     return 1;
712 }
713 #endif /* WIN32 */