Worked around bug #261 again. Added sleep for EAGAIN case
[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.13 2005-02-08 13:23:23 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 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     p->user = 0;
156
157     state->altbuf = 0;
158     state->altsize = state->altlen = 0;
159     state->towrite = state->written = -1;
160     if (protocol == PROTO_WAIS)
161         state->complete = completeWAIS;
162     else
163         state->complete = cs_complete_auto;
164
165     p->timeout = COMSTACK_DEFAULT_TIMEOUT;
166     TRC(fprintf(stderr, "Created new UNIX comstack\n"));
167
168     return p;
169 }
170
171
172 static int unix_strtoaddr_ex(const char *str, struct sockaddr_un *add)
173 {
174     char *cp;
175     if (!unix_init ())
176         return 0;
177     TRC(fprintf(stderr, "unix_strtoaddress: %s\n", str ? str : "NULL"));
178     add->sun_family = AF_UNIX;
179     strncpy(add->sun_path, str, sizeof(add->sun_path));
180     cp = strchr (add->sun_path, ':');
181     if (cp)
182         *cp = '\0';
183     return 1;
184 }
185
186 static void *unix_straddr(COMSTACK h, const char *str)
187 {
188     unix_state *sp = (unix_state *)h->cprivate;
189     char * s = strdup(str);
190     char * f = s;
191     const char * file = NULL;
192     char * eol;
193
194     sp->uid = sp->gid = sp->umask = -1;
195
196     if ((eol = strchr(s, ',')))
197     {
198         do
199         {
200             if ((eol = strchr(s, ',')))
201                 *eol++ = '\0';
202             if (sp->uid  == -1 && strncmp(s, "user=",  5) == 0)
203             {
204                 char * arg = s + 5;
205                 if (strspn(arg, "0123456789") == strlen(arg))
206                 {
207                     sp->uid = atoi(arg);
208                 }
209                 else
210                 {
211                     struct passwd * pw = getpwnam(arg);
212                     if(pw == NULL)
213                     {
214                         printf("No such user\n");
215                         free(f);
216                         return 0;
217                     }
218                     sp->uid = pw->pw_uid;
219                 }
220             }
221             else if (sp->gid == -1 && strncmp(s, "group=", 6) == 0)
222             {
223                 char * arg = s + 6;
224                 if (strspn(arg, "0123456789") == strlen(arg))
225                 {
226                     sp->gid = atoi(arg);
227                 }
228                 else
229                 {
230                     struct group * gr = getgrnam(arg);
231                     if (gr == NULL)
232                     {
233                         printf("No such group\n");
234                         free(f);
235                         return 0;
236                     }
237                     sp->gid = gr->gr_gid;
238                 }
239             }
240             else if (sp->umask == -1 && strncmp(s, "umask=", 6) == 0)
241             {
242                 char * end;
243                 char * arg = s + 6;
244                 
245                 sp->umask = strtol(arg, &end, 8);
246                 if (errno == EINVAL ||
247                     *end)
248                 {
249                     printf("Invalid umask\n");
250                     free(f);
251                     return 0;
252                 }
253             }
254             else if (file == NULL && strncmp(s, "file=", 5) == 0)
255             {
256                 char * arg = s + 5;
257                 file = arg;
258             }
259             else
260             {
261                 printf("invalid or double argument: %s\n", s);
262                 free(f);
263                 return 0;
264             }
265         } while((s = eol));
266     }
267     else
268     {
269         file = str;
270     }
271     if(! file)
272     {
273         errno = EINVAL;
274         return 0;
275     }
276
277     TRC(fprintf(stderr, "unix_straddr: %s\n", str ? str : "NULL"));
278
279     if (!unix_strtoaddr_ex (file, &sp->addr))
280     {
281         free(f);
282         return 0;
283     }
284     free(f);
285     return &sp->addr;
286 }
287
288 struct sockaddr_un *unix_strtoaddr(const char *str)
289 {
290     static struct sockaddr_un add;
291
292     TRC(fprintf(stderr, "unix_strtoaddr: %s\n", str ? str : "NULL"));
293
294     if (!unix_strtoaddr_ex (str, &add))
295         return 0;
296     return &add;
297 }
298
299 static int unix_more(COMSTACK h)
300 {
301     unix_state *sp = (unix_state *)h->cprivate;
302
303     return sp->altlen && (*sp->complete)((unsigned char *) sp->altbuf,
304                                          sp->altlen);
305 }
306
307 /*
308  * connect(2) will block (sometimes) - nothing we can do short of doing
309  * weird things like spawning subprocesses or threading or some weird junk
310  * like that.
311  */
312 static int unix_connect(COMSTACK h, void *address)
313 {
314     struct sockaddr_un *add = (struct sockaddr_un *)address;
315     int r;
316     int i;
317
318     TRC(fprintf(stderr, "unix_connect\n"));
319     h->io_pending = 0;
320     if (h->state != CS_ST_UNBND)
321     {
322         h->cerrno = CSOUTSTATE;
323         return -1;
324     }
325     for (i = 0; i<3; i++)
326     {
327         r = connect(h->iofile, (struct sockaddr *) add, SUN_LEN(add));
328         if (r < 0 && yaz_errno() == EAGAIN)
329         {
330 #if HAVE_USLEEP   
331             usleep(i*10000+1000); /* 1ms, 11ms, 21ms */
332 #else
333             sleep(1);
334 #endif
335             continue;
336         }
337         else
338             break;
339     }
340     if (r < 0)
341     {
342         if (yaz_errno() == EINPROGRESS)
343         {
344             h->event = CS_CONNECT;
345             h->state = CS_ST_CONNECTING;
346             h->io_pending = CS_WANT_WRITE;
347             return 1;
348         }
349         h->cerrno = CSYSERR;
350         return -1;
351     }
352     h->event = CS_CONNECT;
353     h->state = CS_ST_CONNECTING;
354
355     return unix_rcvconnect (h);
356 }
357
358 /*
359  * nop
360  */
361 static int unix_rcvconnect(COMSTACK h)
362 {
363     TRC(fprintf(stderr, "unix_rcvconnect\n"));
364
365     if (h->state == CS_ST_DATAXFER)
366         return 0;
367     if (h->state != CS_ST_CONNECTING)
368     {
369         h->cerrno = CSOUTSTATE;
370         return -1;
371     }
372     h->event = CS_DATA;
373     h->state = CS_ST_DATAXFER;
374     return 0;
375 }
376
377 static int unix_bind(COMSTACK h, void *address, int mode)
378 {
379     unix_state *sp = (unix_state *)h->cprivate;
380     struct sockaddr *addr = (struct sockaddr *)address;
381     const char * path = ((struct sockaddr_un *)addr)->sun_path;
382     struct stat stat_buf;
383
384     TRC (fprintf (stderr, "unix_bind\n"));
385
386     if(stat(path, &stat_buf) != -1) {
387         struct sockaddr_un socket_unix;
388         int socket_out = -1;
389         if(! S_ISSOCK(stat_buf.st_mode)) {
390             h->cerrno = CSYSERR;
391             yaz_set_errno(EEXIST); /* Not a socket (File exists) */
392             return -1;
393         }
394         if((socket_out = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
395             h->cerrno = CSYSERR;
396             return -1;
397         }
398         socket_unix.sun_family = AF_UNIX;
399         strncpy(socket_unix.sun_path, path, sizeof(socket_unix.sun_path));
400         if(connect(socket_out, (struct sockaddr *) &socket_unix, SUN_LEN(&socket_unix)) < 0) {
401             if(yaz_errno() == ECONNREFUSED) {
402                 TRC (fprintf (stderr, "Socket exists but nobody is listening\n"));
403             } else {
404                 h->cerrno = CSYSERR;
405                 return -1;
406             }
407         } else {
408             close(socket_out);
409             h->cerrno = CSYSERR;
410             yaz_set_errno(EADDRINUSE);
411             return -1;
412         }
413         unlink(path);
414     }
415
416     if (bind(h->iofile, (struct sockaddr *) addr, SUN_LEN((struct sockaddr_un *)addr)))
417     {
418         h->cerrno = CSYSERR;
419         return -1;
420     }
421     chown(path, sp->uid, sp->gid);
422     chmod(path, sp->umask != -1 ? sp->umask : 0666);
423     if (mode == CS_SERVER && listen(h->iofile, 3) < 0)
424     {
425         h->cerrno = CSYSERR;
426         return -1;
427     }
428     h->state = CS_ST_IDLE;
429     h->event = CS_LISTEN;
430     return 0;
431 }
432
433 static int unix_listen(COMSTACK h, char *raddr, int *addrlen,
434                     int (*check_ip)(void *cd, const char *a, int len, int t),
435                     void *cd)
436 {
437     struct sockaddr_un addr;
438     YAZ_SOCKLEN_T len = sizeof(addr);
439
440     TRC(fprintf(stderr, "unix_listen pid=%d\n", getpid()));
441     if (h->state != CS_ST_IDLE)
442     {
443         h->cerrno = CSOUTSTATE;
444         return -1;
445     }
446     h->newfd = accept(h->iofile, (struct sockaddr*)&addr, &len);
447     if (h->newfd < 0)
448     {
449         if (
450             yaz_errno() == EWOULDBLOCK
451 #ifdef EAGAIN
452 #if EAGAIN != EWOULDBLOCK
453             || yaz_errno() == EAGAIN
454 #endif
455 #endif
456             )
457             h->cerrno = CSNODATA;
458         else
459             h->cerrno = CSYSERR;
460         return -1;
461     }
462     if (addrlen && (size_t) (*addrlen) >= sizeof(struct sockaddr_un))
463         memcpy(raddr, &addr, *addrlen = sizeof(struct sockaddr_un));
464     else if (addrlen)
465         *addrlen = 0;
466     h->state = CS_ST_INCON;
467     return 0;
468 }
469
470 static COMSTACK unix_accept(COMSTACK h)
471 {
472     COMSTACK cnew;
473     unix_state *state, *st = (unix_state *)h->cprivate;
474
475     TRC(fprintf(stderr, "unix_accept\n"));
476     if (h->state == CS_ST_INCON)
477     {
478         if (!(cnew = (COMSTACK)xmalloc(sizeof(*cnew))))
479         {
480             h->cerrno = CSYSERR;
481             close(h->newfd);
482             h->newfd = -1;
483             return 0;
484         }
485         memcpy(cnew, h, sizeof(*h));
486         cnew->iofile = h->newfd;
487         cnew->io_pending = 0;
488         if (!(state = (unix_state *)
489               (cnew->cprivate = xmalloc(sizeof(unix_state)))))
490         {
491             h->cerrno = CSYSERR;
492             if (h->newfd != -1)
493             {
494                 close(h->newfd);
495                 h->newfd = -1;
496             }
497             return 0;
498         }
499         if (!(cnew->blocking&1) && 
500             (fcntl(cnew->iofile, F_SETFL, O_NONBLOCK) < 0)
501             )
502         {
503             h->cerrno = CSYSERR;
504             if (h->newfd != -1)
505             {
506                 close(h->newfd);
507                 h->newfd = -1;
508             }
509             xfree (cnew);
510             xfree (state);
511             return 0;
512         }
513         h->newfd = -1;
514         state->altbuf = 0;
515         state->altsize = state->altlen = 0;
516         state->towrite = state->written = -1;
517         state->complete = st->complete;
518         memcpy(&state->addr, &st->addr, sizeof(state->addr));
519         cnew->state = CS_ST_ACCEPT;
520         cnew->event = CS_NONE;
521         h->state = CS_ST_IDLE;
522
523         h = cnew;
524     }
525     if (h->state == CS_ST_ACCEPT)
526     {
527     }
528     else
529     {
530         h->cerrno = CSOUTSTATE;
531         return 0;
532     }
533     h->io_pending = 0;
534     h->state = CS_ST_DATAXFER;
535     h->event = CS_DATA;
536     return h;
537 }
538
539 #define CS_UNIX_BUFCHUNK 4096
540
541 /*
542  * Return: -1 error, >1 good, len of buffer, ==1 incomplete buffer,
543  * 0=connection closed.
544  */
545 static int unix_get(COMSTACK h, char **buf, int *bufsize)
546 {
547     unix_state *sp = (unix_state *)h->cprivate;
548     char *tmpc;
549     int tmpi, berlen, rest, req, tomove;
550     int hasread = 0, res;
551
552     TRC(fprintf(stderr, "unix_get: bufsize=%d\n", *bufsize));
553     if (sp->altlen) /* switch buffers */
554     {
555         TRC(fprintf(stderr, "  %d bytes in altbuf (0x%x)\n", sp->altlen,
556                     (unsigned) sp->altbuf));
557         tmpc = *buf;
558         tmpi = *bufsize;
559         *buf = sp->altbuf;
560         *bufsize = sp->altsize;
561         hasread = sp->altlen;
562         sp->altlen = 0;
563         sp->altbuf = tmpc;
564         sp->altsize = tmpi;
565     }
566     h->io_pending = 0;
567     while (!(berlen = (*sp->complete)((unsigned char *)*buf, hasread)))
568     {
569         if (!*bufsize)
570         {
571             if (!(*buf = (char *)xmalloc(*bufsize = CS_UNIX_BUFCHUNK)))
572                 return -1;
573         }
574         else if (*bufsize - hasread < CS_UNIX_BUFCHUNK)
575             if (!(*buf =(char *)xrealloc(*buf, *bufsize *= 2)))
576                 return -1;
577         res = recv(h->iofile, *buf + hasread, CS_UNIX_BUFCHUNK, 0);
578         TRC(fprintf(stderr, "  recv res=%d, hasread=%d\n", res, hasread));
579         if (res < 0)
580         {
581             if (yaz_errno() == EWOULDBLOCK
582 #ifdef EAGAIN
583 #if EAGAIN != EWOULDBLOCK
584                 || yaz_errno() == EAGAIN
585 #endif
586 #endif
587                 || yaz_errno() == EINPROGRESS
588                 )
589             {
590                 h->io_pending = CS_WANT_READ;
591                 break;
592             }
593             else if (yaz_errno() == 0)
594                 continue;
595             else
596                 return -1;
597         }
598         else if (!res)
599             return hasread;
600         hasread += res;
601     }
602     TRC (fprintf (stderr, "  Out of read loop with hasread=%d, berlen=%d\n",
603                   hasread, berlen));
604     /* move surplus buffer (or everything if we didn't get a BER rec.) */
605     if (hasread > berlen)
606     {
607         tomove = req = hasread - berlen;
608         rest = tomove % CS_UNIX_BUFCHUNK;
609         if (rest)
610             req += CS_UNIX_BUFCHUNK - rest;
611         if (!sp->altbuf)
612         {
613             if (!(sp->altbuf = (char *)xmalloc(sp->altsize = req)))
614                 return -1;
615         } else if (sp->altsize < req)
616             if (!(sp->altbuf =(char *)xrealloc(sp->altbuf, sp->altsize = req)))
617                 return -1;
618         TRC(fprintf(stderr, "  Moving %d bytes to altbuf(0x%x)\n", tomove,
619                     (unsigned) sp->altbuf));
620         memcpy(sp->altbuf, *buf + berlen, sp->altlen = tomove);
621     }
622     if (berlen < CS_UNIX_BUFCHUNK - 1)
623         *(*buf + berlen) = '\0';
624     return berlen ? berlen : 1;
625 }
626
627
628
629 /*
630  * Returns 1, 0 or -1
631  * In nonblocking mode, you must call again with same buffer while
632  * return value is 1.
633  */
634 static int unix_put(COMSTACK h, char *buf, int size)
635 {
636     int res;
637     struct unix_state *state = (struct unix_state *)h->cprivate;
638
639     TRC(fprintf(stderr, "unix_put: size=%d\n", size));
640     h->io_pending = 0;
641     h->event = CS_DATA;
642     if (state->towrite < 0)
643     {
644         state->towrite = size;
645         state->written = 0;
646     }
647     else if (state->towrite != size)
648     {
649         h->cerrno = CSWRONGBUF;
650         return -1;
651     }
652     while (state->towrite > state->written)
653     {
654         if ((res =
655              send(h->iofile, buf + state->written, size -
656                   state->written,
657 #ifdef MSG_NOSIGNAL
658                   MSG_NOSIGNAL
659 #else
660                   0
661 #endif
662                  )) < 0)
663         {
664             if (
665                 yaz_errno() == EWOULDBLOCK
666 #ifdef EAGAIN
667 #if EAGAIN != EWOULDBLOCK
668                 || yaz_errno() == EAGAIN
669 #endif
670 #endif
671                 )
672             {
673                 TRC(fprintf(stderr, "  Flow control stop\n"));
674                 h->io_pending = CS_WANT_WRITE;
675                 return 1;
676             }
677             h->cerrno = CSYSERR;
678             return -1;
679         }
680         state->written += res;
681         TRC(fprintf(stderr, "  Wrote %d, written=%d, nbytes=%d\n",
682                     res, state->written, size));
683     }
684     state->towrite = state->written = -1;
685     TRC(fprintf(stderr, "  Ok\n"));
686     return 0;
687 }
688
689 static int unix_close(COMSTACK h)
690 {
691     unix_state *sp = (struct unix_state *)h->cprivate;
692
693     TRC(fprintf(stderr, "unix_close\n"));
694     if (h->iofile != -1)
695     {
696         close(h->iofile);
697     }
698     if (sp->altbuf)
699         xfree(sp->altbuf);
700     xfree(sp);
701     xfree(h);
702     return 0;
703 }
704
705 static char *unix_addrstr(COMSTACK h)
706 {
707     unix_state *sp = (struct unix_state *)h->cprivate;
708     char *buf = sp->buf;
709     sprintf(buf, "unix:%s", sp->addr.sun_path);
710     return buf;
711 }
712
713 static int unix_set_blocking(COMSTACK p, int blocking)
714 {
715     unsigned long flag;
716
717     if (p->blocking == blocking)
718         return 1;
719     flag = fcntl(p->iofile, F_GETFL, 0);
720     if(!blocking)
721         flag = flag & ~O_NONBLOCK;
722     else
723         flag = flag | O_NONBLOCK;
724     if (fcntl(p->iofile, F_SETFL, flag) < 0)
725         return 0;
726     p->blocking = blocking;
727     return 1;
728 }
729 #endif /* WIN32 */