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