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