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