Fixed possible buf in proto.c
[yaz-moved-to-github.git] / server / statserv.c
1 /*
2  * Copyright (C) 1994, Index Data I/S 
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: statserv.c,v $
7  * Revision 1.16  1995-04-10 10:23:40  quinn
8  * Some work to add scan and other things.
9  *
10  * Revision 1.15  1995/03/31  10:16:51  quinn
11  * Fixed logging.
12  *
13  * Revision 1.14  1995/03/31  09:18:58  quinn
14  * Added logging.
15  *
16  * Revision 1.13  1995/03/30  16:08:39  quinn
17  * Little mods.
18  *
19  * Revision 1.12  1995/03/30  13:29:02  quinn
20  * Smallish
21  *
22  * Revision 1.11  1995/03/30  12:18:17  quinn
23  * Fixed bug.
24  *
25  * Revision 1.10  1995/03/29  15:40:16  quinn
26  * Ongoing work. Statserv is now dynamic by default
27  *
28  * Revision 1.9  1995/03/27  08:34:30  quinn
29  * Added dynamic server functionality.
30  * Released bindings to session.c (is now redundant)
31  *
32  * Revision 1.8  1995/03/20  09:46:26  quinn
33  * Added osi support.
34  *
35  * Revision 1.7  1995/03/16  13:29:04  quinn
36  * Partitioned server.
37  *
38  * Revision 1.6  1995/03/15  15:18:52  quinn
39  * Little changes to better support nonblocking I/O
40  * Added backend.h
41  *
42  * Revision 1.5  1995/03/15  08:37:45  quinn
43  * Now we're pretty much set for nonblocking I/O.
44  *
45  * Revision 1.4  1995/03/14  16:59:48  quinn
46  * Bug-fixes
47  *
48  * Revision 1.3  1995/03/14  11:30:15  quinn
49  * Works better now.
50  *
51  * Revision 1.2  1995/03/14  10:28:03  quinn
52  * More work on demo server.
53  *
54  * Revision 1.1  1995/03/10  18:22:45  quinn
55  * The rudiments of an asynchronous server.
56  *
57  */
58
59 /*
60  * Simple, static server. I wouldn't advise a static server unless you
61  * really have to, but it's great for debugging memory management.  :)
62  */
63
64 #include <stdio.h>
65 #include <unistd.h>
66 #include <fcntl.h>
67 #include <sys/wait.h>
68 #include <signal.h>
69 #include <errno.h>
70
71 #include <options.h>
72 #include <eventl.h>
73 #include <session.h>
74 #include <eventl.h>
75 #include <comstack.h>
76 #include <tcpip.h>
77 #ifdef USE_XTIMOSI
78 #include <xmosi.h>
79 #endif
80 #include <dmalloc.h>
81 #include <log.h>
82
83 static char *me = "statserver";
84 int dynamic = 1;   /* fork on incoming connection */
85 static int loglevel = LOG_DEFAULT_LEVEL;
86 char *apdufile = 0;
87
88 #define DEFAULT_LISTENER "tcp:localhost:9999"
89
90 /*
91  * handle incoming connect requests.
92  * The dynamic mode is a bit tricky mostly because we want to avoid
93  * doing all of the listening and accepting in the parent - it's
94  * safer that way.
95  */
96 static void listener(IOCHAN h, int event)
97 {
98     COMSTACK line = (COMSTACK) iochan_getdata(h);
99     association *newas;
100     static int hand[2];
101     static int child = 0;
102     int res;
103
104     if (event == EVENT_INPUT)
105     {
106         if (dynamic && !child) 
107         {
108             int res;
109
110             if (pipe(hand) < 0)
111             {
112                 logf(LOG_FATAL|LOG_ERRNO, "pipe");
113                 exit(1);
114             }
115             if ((res = fork()) < 0)
116             {
117                 logf(LOG_FATAL|LOG_ERRNO, "fork");
118                 exit(1);
119             }
120             else if (res == 0) /* child */
121             {
122                 char nbuf[100];
123
124                 close(hand[0]);
125                 child = 1;
126                 sprintf(nbuf, "%s(%d)", me, getpid());
127                 log_init(loglevel, nbuf, 0);
128             }
129             else /* parent */
130             {
131                 close(hand[1]);
132                 /* wait for child to take the call */
133                 for (;;)
134                 {
135                     char dummy[1];
136                     int res;
137                     
138                     if ((res = read(hand[0], dummy, 1)) < 0 && errno != EINTR)
139                     {
140                         logf(LOG_FATAL|LOG_ERRNO, "handshake read");
141                         exit(1);
142                     }
143                     else if (res >= 0)
144                         break;
145                 }
146                 logf(LOG_DEBUG, "P: Child has taken the call");
147                 close(hand[0]);
148                 return;
149             }
150         }
151         if ((res = cs_listen(line, 0, 0)) < 0)
152         {
153             logf(LOG_FATAL, "cs_listen failed.");
154             return;
155         }
156         else if (res == 1)
157             return;
158         logf(LOG_DEBUG, "listen ok");
159         iochan_setevent(h, EVENT_OUTPUT);
160         iochan_setflags(h, EVENT_OUTPUT | EVENT_EXCEPT); /* set up for acpt */
161     }
162     /* in dynamic mode, only the child ever comes down here */
163     else if (event == EVENT_OUTPUT)
164     {
165         COMSTACK new_line;
166         IOCHAN new_chan;
167
168         if (!(new_line = cs_accept(line)))
169         {
170             logf(LOG_FATAL, "Accept failed.");
171             iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */
172             return;
173         }
174         logf(LOG_DEBUG, "accept ok");
175         if (dynamic)
176         {
177             IOCHAN pp;
178             /* close our half of the listener sockets */
179             for (pp = iochan_getchan(); pp; pp = iochan_getnext(pp))
180             {
181                 COMSTACK l = iochan_getdata(pp);
182                 cs_close(l);
183                 iochan_destroy(pp);
184             }
185             /* release dad */
186             logf(LOG_DEBUG, "Releasing parent");
187             close(hand[1]);
188         }
189         else
190             iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */
191
192         if (!(new_chan = iochan_create(cs_fileno(new_line), ir_session,
193             EVENT_INPUT)))
194         {
195             logf(LOG_FATAL, "Failed to create iochan");
196             exit(1);
197         }
198         if (!(newas = create_association(new_chan, new_line)))
199         {
200             logf(LOG_FATAL, "Failed to create new assoc.");
201             exit(1);
202         }
203         iochan_setdata(new_chan, newas);
204         logf(LOG_LOG, "accepted connection");
205     }
206     else
207     {
208         logf(LOG_FATAL, "Bad event on listener.");
209         exit(1);
210     }
211 }
212
213 /*
214  * Set up a listening endpoint, and give it to the event-handler.
215  */
216 static void add_listener(char *where, int what)
217 {
218     COMSTACK l;
219     CS_TYPE type;
220     char mode[100], addr[100];
221     void *ap;
222     IOCHAN lst;
223
224     if (!where || sscanf(where, "%[^:]:%s", mode, addr) != 2)
225     {
226         fprintf(stderr, "%s: Address format: ('tcp'|'osi')':'<address>.\n",
227             me);
228         exit(1);
229     }
230     if (!strcmp(mode, "tcp"))
231     {
232         if (!(ap = tcpip_strtoaddr(addr)))
233         {
234             fprintf(stderr, "Address resolution failed for TCP.\n");
235             exit(1);
236         }
237         type = tcpip_type;
238     }
239 #ifdef USE_XTIMOSI
240     else if (!strcmp(mode, "osi"))
241     {
242         if (!(ap = mosi_strtoaddr(addr)))
243         {
244             fprintf(stderr, "Address resolution failed for TCP.\n");
245             exit(1);
246         }
247         type = mosi_type;
248     }
249 #endif
250     else
251     {
252         fprintf(stderr, "You must specify either 'osi:' or 'tcp:'.\n");
253         exit(1);
254     }
255     logf(LOG_LOG, "Adding %s %s listener on %s",
256         dynamic ? "dynamic" : "static",
257         what == PROTO_SR ? "SR" : "Z3950", where);
258     if (!(l = cs_create(type, 0, what)))
259     {
260         logf(LOG_FATAL|LOG_ERRNO, "Failed to create listener");
261         exit(1);
262     }
263     if (cs_bind(l, ap, CS_SERVER) < 0)
264     {
265         logf(LOG_FATAL|LOG_ERRNO, "Failed to bind to %s", where);
266         exit(1);
267     }
268     if (!(lst = iochan_create(cs_fileno(l), listener, EVENT_INPUT |
269          EVENT_EXCEPT)))
270     {
271         logf(LOG_FATAL|LOG_ERRNO, "Failed to create IOCHAN-type");
272         exit(1);
273     }
274     iochan_setdata(lst, l);
275 }
276
277 static void catchchld(int num)
278 {
279     while (waitpid(-1, 0, WNOHANG) > 0);
280     signal(SIGCHLD, catchchld);
281 }
282
283 int statserv_main(int argc, char **argv)
284 {
285     int ret, listeners = 0;
286     char *arg;
287     int protocol = CS_Z3950;
288     char *logfile = 0;
289
290     me = argv[0];
291     while ((ret = options("a:szSl:v:", argv, argc, &arg)) != -2)
292         switch (ret)
293         {
294             case 0:
295                 add_listener(arg, protocol);
296                 listeners++;
297                 break;
298             case 'z': protocol = CS_Z3950; break;
299             case 's': protocol = CS_SR; break;
300             case 'S': dynamic = 0; break;
301             case 'l':
302                 logfile = arg;
303                 log_init(loglevel, me, logfile);
304                 break;
305             case 'v':
306                 loglevel = log_mask_str(arg);
307                 log_init(loglevel, me, logfile);
308                 break;
309             case 'a':
310                 apdufile = arg; break;
311             default:
312                 fprintf(stderr, "Usage: %s [ -v <loglevel> -l <logfile> -zsS <listener-addr> ... ]\n", me);
313                 exit(1);
314         }
315     if (dynamic)
316         signal(SIGCHLD, catchchld);
317     if (!listeners)
318         add_listener(DEFAULT_LISTENER, protocol);
319     logf(LOG_LOG, "Entering event loop.");
320             
321     return event_loop();
322 }