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