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