Towards 2.1.40.
[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.17 2006-09-06 15:01:53 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_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 unsigned char *buf, int len); /* length/comple. */
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));
189     cp = strchr (add->sun_path, ':');
190     if (cp)
191         *cp = '\0';
192     return 1;
193 }
194
195 static void *unix_straddr1(COMSTACK h, const char *str, char *f)
196 {
197     unix_state *sp = (unix_state *)h->cprivate;
198     char * s = f;
199     const char * file = NULL;
200     char * eol;
201
202     sp->uid = sp->gid = sp->umask = -1;
203
204     if ((eol = strchr(s, ',')))
205     {
206         do
207         {
208             if ((eol = strchr(s, ',')))
209                 *eol++ = '\0';
210             if (sp->uid  == -1 && strncmp(s, "user=",  5) == 0)
211             {
212                 char * arg = s + 5;
213                 if (strspn(arg, "0123456789") == strlen(arg))
214                 {
215                     sp->uid = atoi(arg);
216                 }
217                 else
218                 {
219                     struct passwd * pw = getpwnam(arg);
220                     if(pw == NULL)
221                     {
222                         printf("No such user\n");
223                         return 0;
224                     }
225                     sp->uid = pw->pw_uid;
226                 }
227             }
228             else if (sp->gid == -1 && strncmp(s, "group=", 6) == 0)
229             {
230                 char * arg = s + 6;
231                 if (strspn(arg, "0123456789") == strlen(arg))
232                 {
233                     sp->gid = atoi(arg);
234                 }
235                 else
236                 {
237                     struct group * gr = getgrnam(arg);
238                     if (gr == NULL)
239                     {
240                         printf("No such group\n");
241                         return 0;
242                     }
243                     sp->gid = gr->gr_gid;
244                 }
245             }
246             else if (sp->umask == -1 && strncmp(s, "umask=", 6) == 0)
247             {
248                 char * end;
249                 char * arg = s + 6;
250                 
251                 sp->umask = strtol(arg, &end, 8);
252                 if (errno == EINVAL ||
253                     *end)
254                 {
255                     printf("Invalid umask\n");
256                     return 0;
257                 }
258             }
259             else if (file == NULL && strncmp(s, "file=", 5) == 0)
260             {
261                 char * arg = s + 5;
262                 file = arg;
263             }
264             else
265             {
266                 printf("invalid or double argument: %s\n", s);
267                 return 0;
268             }
269         } while((s = eol));
270     }
271     else
272     {
273         file = str;
274     }
275     if(! file)
276     {
277         errno = EINVAL;
278         return 0;
279     }
280
281     TRC(fprintf(stderr, "unix_straddr: %s\n", str ? str : "NULL"));
282
283     if (!unix_strtoaddr_ex (file, &sp->addr))
284         return 0;
285     return &sp->addr;
286 }
287
288 static void *unix_straddr(COMSTACK h, const char *str)
289 {
290     char *f = xstrdup(str);
291     void *vp = unix_straddr1(h, str, f);
292     xfree(f);
293     return vp;
294 }
295
296 struct sockaddr_un *unix_strtoaddr(const char *str)
297 {
298     static struct sockaddr_un add;
299
300     TRC(fprintf(stderr, "unix_strtoaddr: %s\n", str ? str : "NULL"));
301
302     if (!unix_strtoaddr_ex (str, &add))
303         return 0;
304     return &add;
305 }
306
307 static int unix_more(COMSTACK h)
308 {
309     unix_state *sp = (unix_state *)h->cprivate;
310
311     return sp->altlen && (*sp->complete)((unsigned char *) sp->altbuf,
312                                          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));
409         if(connect(socket_out, (struct sockaddr *) &socket_unix, SUN_LEN(&socket_unix)) < 0) {
410             if(yaz_errno() == ECONNREFUSED) {
411                 TRC (fprintf (stderr, "Socket exists but nobody is listening\n"));
412             } else {
413                 h->cerrno = CSYSERR;
414                 return -1;
415             }
416         } else {
417             close(socket_out);
418             h->cerrno = CSYSERR;
419             yaz_set_errno(EADDRINUSE);
420             return -1;
421         }
422         unlink(path);
423     }
424
425     if (bind(h->iofile, (struct sockaddr *) addr, SUN_LEN((struct sockaddr_un *)addr)))
426     {
427         h->cerrno = CSYSERR;
428         return -1;
429     }
430     chown(path, sp->uid, sp->gid);
431     chmod(path, sp->umask != -1 ? sp->umask : 0666);
432     if (mode == CS_SERVER && listen(h->iofile, 100) < 0)
433     {
434         h->cerrno = CSYSERR;
435         return -1;
436     }
437     h->state = CS_ST_IDLE;
438     h->event = CS_LISTEN;
439     return 0;
440 }
441
442 static int unix_listen(COMSTACK h, char *raddr, int *addrlen,
443                     int (*check_ip)(void *cd, const char *a, int len, int t),
444                     void *cd)
445 {
446     struct sockaddr_un addr;
447     YAZ_SOCKLEN_T len = sizeof(addr);
448
449     TRC(fprintf(stderr, "unix_listen pid=%d\n", getpid()));
450     if (h->state != CS_ST_IDLE)
451     {
452         h->cerrno = CSOUTSTATE;
453         return -1;
454     }
455     h->newfd = accept(h->iofile, (struct sockaddr*)&addr, &len);
456     if (h->newfd < 0)
457     {
458         if (
459             yaz_errno() == EWOULDBLOCK
460 #ifdef EAGAIN
461 #if EAGAIN != EWOULDBLOCK
462             || yaz_errno() == EAGAIN
463 #endif
464 #endif
465             )
466             h->cerrno = CSNODATA;
467         else
468             h->cerrno = CSYSERR;
469         return -1;
470     }
471     if (addrlen && (size_t) (*addrlen) >= sizeof(struct sockaddr_un))
472         memcpy(raddr, &addr, *addrlen = sizeof(struct sockaddr_un));
473     else if (addrlen)
474         *addrlen = 0;
475     h->state = CS_ST_INCON;
476     return 0;
477 }
478
479 static COMSTACK unix_accept(COMSTACK h)
480 {
481     COMSTACK cnew;
482     unix_state *state, *st = (unix_state *)h->cprivate;
483
484     TRC(fprintf(stderr, "unix_accept\n"));
485     if (h->state == CS_ST_INCON)
486     {
487         if (!(cnew = (COMSTACK)xmalloc(sizeof(*cnew))))
488         {
489             h->cerrno = CSYSERR;
490             close(h->newfd);
491             h->newfd = -1;
492             return 0;
493         }
494         memcpy(cnew, h, sizeof(*h));
495         cnew->iofile = h->newfd;
496         cnew->io_pending = 0;
497         if (!(state = (unix_state *)
498               (cnew->cprivate = xmalloc(sizeof(unix_state)))))
499         {
500             h->cerrno = CSYSERR;
501             if (h->newfd != -1)
502             {
503                 close(h->newfd);
504                 h->newfd = -1;
505             }
506             return 0;
507         }
508         if (!(cnew->flags&CS_FLAGS_BLOCKING) && 
509             (fcntl(cnew->iofile, F_SETFL, O_NONBLOCK) < 0)
510             )
511         {
512             h->cerrno = CSYSERR;
513             if (h->newfd != -1)
514             {
515                 close(h->newfd);
516                 h->newfd = -1;
517             }
518             xfree (cnew);
519             xfree (state);
520             return 0;
521         }
522         h->newfd = -1;
523         state->altbuf = 0;
524         state->altsize = state->altlen = 0;
525         state->towrite = state->written = -1;
526         state->complete = st->complete;
527         memcpy(&state->addr, &st->addr, sizeof(state->addr));
528         cnew->state = CS_ST_ACCEPT;
529         cnew->event = CS_NONE;
530         h->state = CS_ST_IDLE;
531
532         h = cnew;
533     }
534     if (h->state == CS_ST_ACCEPT)
535     {
536     }
537     else
538     {
539         h->cerrno = CSOUTSTATE;
540         return 0;
541     }
542     h->io_pending = 0;
543     h->state = CS_ST_DATAXFER;
544     h->event = CS_DATA;
545     return h;
546 }
547
548 #define CS_UNIX_BUFCHUNK 4096
549
550 /*
551  * Return: -1 error, >1 good, len of buffer, ==1 incomplete buffer,
552  * 0=connection closed.
553  */
554 static int unix_get(COMSTACK h, char **buf, int *bufsize)
555 {
556     unix_state *sp = (unix_state *)h->cprivate;
557     char *tmpc;
558     int tmpi, berlen, rest, req, tomove;
559     int hasread = 0, res;
560
561     TRC(fprintf(stderr, "unix_get: bufsize=%d\n", *bufsize));
562     if (sp->altlen) /* switch buffers */
563     {
564         TRC(fprintf(stderr, "  %d bytes in altbuf (0x%x)\n", sp->altlen,
565                     (unsigned) sp->altbuf));
566         tmpc = *buf;
567         tmpi = *bufsize;
568         *buf = sp->altbuf;
569         *bufsize = sp->altsize;
570         hasread = sp->altlen;
571         sp->altlen = 0;
572         sp->altbuf = tmpc;
573         sp->altsize = tmpi;
574     }
575     h->io_pending = 0;
576     while (!(berlen = (*sp->complete)((unsigned char *)*buf, hasread)))
577     {
578         if (!*bufsize)
579         {
580             if (!(*buf = (char *)xmalloc(*bufsize = CS_UNIX_BUFCHUNK)))
581                 return -1;
582         }
583         else if (*bufsize - hasread < CS_UNIX_BUFCHUNK)
584             if (!(*buf =(char *)xrealloc(*buf, *bufsize *= 2)))
585                 return -1;
586         res = recv(h->iofile, *buf + hasread, CS_UNIX_BUFCHUNK, 0);
587         TRC(fprintf(stderr, "  recv res=%d, hasread=%d\n", res, hasread));
588         if (res < 0)
589         {
590             if (yaz_errno() == EWOULDBLOCK
591 #ifdef EAGAIN
592 #if EAGAIN != EWOULDBLOCK
593                 || yaz_errno() == EAGAIN
594 #endif
595 #endif
596                 || yaz_errno() == EINPROGRESS
597                 )
598             {
599                 h->io_pending = CS_WANT_READ;
600                 break;
601             }
602             else if (yaz_errno() == 0)
603                 continue;
604             else
605                 return -1;
606         }
607         else if (!res)
608             return hasread;
609         hasread += res;
610     }
611     TRC (fprintf (stderr, "  Out of read loop with hasread=%d, berlen=%d\n",
612                   hasread, berlen));
613     /* move surplus buffer (or everything if we didn't get a BER rec.) */
614     if (hasread > berlen)
615     {
616         tomove = req = hasread - berlen;
617         rest = tomove % CS_UNIX_BUFCHUNK;
618         if (rest)
619             req += CS_UNIX_BUFCHUNK - rest;
620         if (!sp->altbuf)
621         {
622             if (!(sp->altbuf = (char *)xmalloc(sp->altsize = req)))
623                 return -1;
624         } else if (sp->altsize < req)
625             if (!(sp->altbuf =(char *)xrealloc(sp->altbuf, sp->altsize = req)))
626                 return -1;
627         TRC(fprintf(stderr, "  Moving %d bytes to altbuf(0x%x)\n", tomove,
628                     (unsigned) sp->altbuf));
629         memcpy(sp->altbuf, *buf + berlen, sp->altlen = tomove);
630     }
631     if (berlen < CS_UNIX_BUFCHUNK - 1)
632         *(*buf + berlen) = '\0';
633     return berlen ? berlen : 1;
634 }
635
636
637
638 /*
639  * Returns 1, 0 or -1
640  * In nonblocking mode, you must call again with same buffer while
641  * return value is 1.
642  */
643 static int unix_put(COMSTACK h, char *buf, int size)
644 {
645     int res;
646     struct unix_state *state = (struct unix_state *)h->cprivate;
647
648     TRC(fprintf(stderr, "unix_put: size=%d\n", size));
649     h->io_pending = 0;
650     h->event = CS_DATA;
651     if (state->towrite < 0)
652     {
653         state->towrite = size;
654         state->written = 0;
655     }
656     else if (state->towrite != size)
657     {
658         h->cerrno = CSWRONGBUF;
659         return -1;
660     }
661     while (state->towrite > state->written)
662     {
663         if ((res =
664              send(h->iofile, buf + state->written, size -
665                   state->written,
666 #ifdef MSG_NOSIGNAL
667                   MSG_NOSIGNAL
668 #else
669                   0
670 #endif
671                  )) < 0)
672         {
673             if (
674                 yaz_errno() == EWOULDBLOCK
675 #ifdef EAGAIN
676 #if EAGAIN != EWOULDBLOCK
677                 || yaz_errno() == EAGAIN
678 #endif
679 #endif
680                 )
681             {
682                 TRC(fprintf(stderr, "  Flow control stop\n"));
683                 h->io_pending = CS_WANT_WRITE;
684                 return 1;
685             }
686             h->cerrno = CSYSERR;
687             return -1;
688         }
689         state->written += res;
690         TRC(fprintf(stderr, "  Wrote %d, written=%d, nbytes=%d\n",
691                     res, state->written, size));
692     }
693     state->towrite = state->written = -1;
694     TRC(fprintf(stderr, "  Ok\n"));
695     return 0;
696 }
697
698 static int unix_close(COMSTACK h)
699 {
700     unix_state *sp = (struct unix_state *)h->cprivate;
701
702     TRC(fprintf(stderr, "unix_close\n"));
703     if (h->iofile != -1)
704     {
705         close(h->iofile);
706     }
707     if (sp->altbuf)
708         xfree(sp->altbuf);
709     xfree(sp);
710     xfree(h);
711     return 0;
712 }
713
714 static char *unix_addrstr(COMSTACK h)
715 {
716     unix_state *sp = (struct unix_state *)h->cprivate;
717     char *buf = sp->buf;
718     sprintf(buf, "unix:%s", sp->addr.sun_path);
719     return buf;
720 }
721
722 static int unix_set_blocking(COMSTACK p, int flags)
723 {
724     unsigned long flag;
725
726     if (p->flags == flags)
727         return 1;
728     flag = fcntl(p->iofile, F_GETFL, 0);
729     if (flags & CS_FLAGS_BLOCKING)
730         flag = flag & ~O_NONBLOCK;
731     else
732         flag = flag | O_NONBLOCK;
733     if (fcntl(p->iofile, F_SETFL, flag) < 0)
734         return 0;
735     p->flags = flags;
736     return 1;
737 }
738 #endif /* WIN32 */
739 /*
740  * Local variables:
741  * c-basic-offset: 4
742  * indent-tabs-mode: nil
743  * End:
744  * vim: shiftwidth=4 tabstop=8 expandtab
745  */
746