First work on Z39.50 persistence.
[egate.git] / kernel / main.c
1 /* Gateway kernel - Main
2  * Europagate, 1995
3  *
4  * $Log: main.c,v $
5  * Revision 1.16  1995/04/19 07:31:07  adam
6  * First work on Z39.50 persistence.
7  *
8  * Revision 1.15  1995/04/17  09:34:30  adam
9  * Timeout (idletime) adjustable. Minor changes in kernel.
10  *
11  * Revision 1.14  1995/03/28  11:42:34  adam
12  * First use of string-queue utility.
13  *
14  * Revision 1.13  1995/03/28  08:01:25  adam
15  * FIFO existence is used to test for a running kernel.
16  *
17  * Revision 1.12  1995/03/27  12:51:05  adam
18  * New log level in use: GW_LOG_ERRNO.
19  *
20  * Revision 1.11  1995/03/27  08:24:02  adam
21  * First use of gip interface and gw-db.
22  * First work on eti program.
23  *
24  * Revision 1.10  1995/03/01  14:32:25  adam
25  * Better diagnostics. Default is, that only one database selected when
26  * several are known.
27  *
28  * Revision 1.9  1995/02/23  08:32:17  adam
29  * Changed header.
30  *
31  * Revision 1.7  1995/02/22  15:22:33  adam
32  * Much more checking of run-time state. Show command never retrieves
33  * more records than indicated by the previous search request. Help
34  * command available. The maximum number of records retrieved can be
35  * controlled now.
36  *
37  * Revision 1.6  1995/02/22  08:51:34  adam
38  * Output function can be customized in fml, which is used to print
39  * the reply to reply_fd.
40  *
41  * Revision 1.5  1995/02/20  21:16:20  adam
42  * FML support. Bug fixes. Profile for drewdb.
43  *
44  * Revision 1.4  1995/02/17  17:06:16  adam
45  * Minor changes.
46  *
47  * Revision 1.3  1995/02/16  18:35:09  adam
48  * First use of Zdist library. Search requests are supported.
49  * Present requests are not supported yet.
50  *
51  * Revision 1.2  1995/02/16  13:21:00  adam
52  * Organization of resource files for targets and conversion
53  * language implemented.
54  *
55  * Revision 1.1  1995/02/15  17:45:29  adam
56  * First version of email gateway kernel. Email requests are read
57  * from stdin. The output is transferred to an MTA if 'From' is
58  * found in the header - or stdout if absent. No Z39.50 client is used.
59  *
60  */
61
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <string.h>
65 #include <assert.h>
66 #include <unistd.h>
67 #include <sys/types.h>
68 #include <sys/time.h>
69 #include <fcntl.h>
70
71 #include <gip.h>
72 #include <strqueue.h>
73 #include "kernel.h"
74
75 FILE *reply_fd = stdout;
76
77 struct gw_kernel_info info;
78
79 static void kernel_events (struct str_queue *queue, int userid)
80 {
81     char fifo_client_name[1024];
82     char fifo_server_name[1024];
83     char line_buf[1024];
84     GIP gip;
85     fd_set set_r;
86     int r, gip_fd;
87     struct timeval tv;
88     int timeout;
89     int continuation = 0;
90
91     timeout = atoi(gw_res_get (info.kernel_res, "gw.timeout", "600"));
92     gw_log (GW_LOG_DEBUG, KERNEL_LOG, "kernel event loop");
93
94     sprintf (fifo_client_name, "fifo.c.%d", userid);
95     sprintf (fifo_server_name, "fifo.s.%d", userid);
96
97     gip = gips_initialize (fifo_server_name);
98     gips_open (gip, fifo_client_name);
99     gip_fd = gip_infileno (gip);
100
101     while (1)
102     {
103         FD_ZERO (&set_r);
104         FD_SET (gip_fd, &set_r);
105         tv.tv_sec = timeout;
106         tv.tv_usec = 0;
107
108         gw_log (GW_LOG_DEBUG, KERNEL_LOG, "IPC select");
109         r = select (gip_fd+1, &set_r, NULL, NULL, &tv);
110         if (r == -1)
111         {
112             gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, KERNEL_LOG, "select");
113             exit (1);
114         }
115         if (r == 0)
116         {
117             gw_log (GW_LOG_STAT, KERNEL_LOG, "Timeout after %d seconds", 
118                     timeout);
119             if (info.zass)
120                 save_p_state (userid);
121             break;
122         }
123         if (FD_ISSET (gip_fd, &set_r))
124         {
125             char command[128], *cp;
126
127             if (!(lgets (command, 127, gip_fd)))
128             {
129                 gw_log (GW_LOG_WARN, KERNEL_LOG, "Unexpected close");
130                 break;
131             }
132             if ((cp = strchr (command, '\n')))
133                 *cp = '\0';
134             gw_log (GW_LOG_STAT, KERNEL_LOG, "IPC: %s", command);
135             if (!strcmp (command, "mail"))
136             {
137                 gw_log (GW_LOG_DEBUG, KERNEL_LOG, "Incoming mail...\n");
138                 while (lgets (line_buf, sizeof(line_buf)-1, gip_fd))
139                     str_queue_enq (queue, line_buf);
140                 urp_start (continuation, queue);
141                 if (!continuation)
142                     load_p_state (userid);      
143                 urp_command (queue);
144                 urp_end ();
145                 while (str_queue_deq (queue, 0, 0))
146                     ;
147             }
148             else if (!strcmp (command, "stop"))
149             {
150                 gw_log (GW_LOG_DEBUG, KERNEL_LOG, "stop");
151                 break;
152             }
153             else 
154             {
155                 gw_log (GW_LOG_WARN, KERNEL_LOG, "Unknown IPC: %s", command);
156             }
157             continuation = 1;
158         }
159     }
160     gips_close (gip);
161     gips_destroy (gip);
162     unlink (fifo_client_name);
163     unlink (fifo_server_name);
164 }
165
166 int main (int argc, char **argv)
167 {
168     int userid = -1;
169     struct str_queue *queue;
170
171     info.kernel_res = NULL;
172     info.default_res = "default.res";
173     info.override_res = NULL;
174     *info.target = 0;
175     info.lang = NULL;
176     info.bibset = NULL;
177     info.zass = NULL;
178     info.override_portno = NULL;
179     info.override_hostname = NULL;
180     info.databases = NULL;
181     info.database = NULL;
182     info.setno = -1;
183 #if USE_FML
184     info.fml = NULL;
185 #endif
186     info.sets = NULL;
187
188     gw_log_init (*argv);
189     info.kernel_res = gw_res_init ();
190     while (--argc > 0)
191     {
192         if (**++argv == '-')
193         {
194             switch (argv[0][1])
195             {
196             case 'H':
197                 fprintf (stderr, "kernel [option..] [resource]\n");
198                 fprintf (stderr, "If no resource file is given");
199                 fprintf (stderr, " default.res is used\n");
200                 fprintf (stderr, "Options:\n");
201                 fprintf (stderr, " -d           Enable debugging log\n");
202                 fprintf (stderr, " -t target    Open target immediately\n");
203                 fprintf (stderr, " -g lang      Set language\n");
204                 fprintf (stderr, " -o resource  Override with resource\n");
205                 fprintf (stderr, " -h host      Override host\n");
206                 fprintf (stderr, " -p port      Override port\n");
207                 fprintf (stderr, " -l log       Set Log file\n");
208                 fprintf (stderr, " -i id        Set IPC userid\n");
209                 exit (1);
210             case 'd':
211                 gw_log_level (GW_LOG_ALL);
212                 break;
213             case 't':
214                 if (argv[0][2])
215                     strcpy (info.target, argv[0]+2);
216                 else if (argc > 0)
217                 {
218                     --argc;
219                     strcpy (info.target, *++argv);
220                 }
221                 else
222                 {
223                     gw_log (GW_LOG_FATAL, KERNEL_LOG, "missing target name");
224                     exit (1);
225                 }
226                 break;
227             case 'g':
228                 if (argv[0][2])
229                     info.lang = argv[0]+2;
230                 else if (argc > 0)
231                 {
232                     --argc;
233                     info.lang = *++argv;
234                 }
235                 else
236                 {
237                     gw_log (GW_LOG_FATAL, KERNEL_LOG, "missing language name");
238                     exit (1);
239                 }
240                 break;
241             case 'o':
242                 if (argv[0][2])
243                     info.override_res = argv[0]+2;
244                 else if (argc > 0)
245                 {
246                     --argc;
247                     info.override_res = *++argv;
248                 }
249                 else
250                 {
251                     gw_log (GW_LOG_FATAL, KERNEL_LOG, "missing override name");
252                     exit (1);
253                 }
254                 break;
255             case 'p':
256                 if (argv[0][2])
257                     info.override_portno = argv[0]+2;
258                 else if (argc > 0)
259                 {
260                     --argc;
261                     info.override_portno = *++argv;
262                 }
263                 else
264                 {
265                     gw_log (GW_LOG_FATAL, KERNEL_LOG, "missing portno");
266                     exit (1);
267                 }
268                 break;
269             case 'h':
270                 if (argv[0][2])
271                     info.override_hostname = argv[0]+2;
272                 else if (argc > 0)
273                 {
274                     --argc;
275                     info.override_hostname = *++argv;
276                 }
277                 else
278                 {
279                     gw_log (GW_LOG_FATAL, KERNEL_LOG, "missing hostname");
280                     exit (1);
281                 }
282                 break;
283             case 'l':
284                 if (argv[0][2])
285                     gw_log_file (GW_LOG_ALL, argv[0]+2);
286                 else if (argc > 0)
287                 {
288                     --argc;
289                     gw_log_file (GW_LOG_ALL, *++argv);
290                 }
291                 else
292                 {
293                     gw_log (GW_LOG_FATAL, KERNEL_LOG, "missing log filename");
294                     exit (1);
295                 }
296                 break;
297             case 'i':
298                 if (argv[0][2])
299                     userid = atoi (argv[0]+2);
300                 else if (argc > 0)
301                 {
302                     --argc;
303                     userid = atoi (*++argv);
304                 }
305                 else
306                 {
307                     gw_log (GW_LOG_FATAL, KERNEL_LOG, "missing user id");
308                     exit (1);
309                 }
310                 break;
311             default:
312                 gw_log (GW_LOG_FATAL, KERNEL_LOG, "unknown option %s", *argv);
313                 exit (1);
314             }
315         }
316         else
317             info.default_res = *argv;
318     }
319     if (!(queue = str_queue_mk ()))
320     {
321         gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, KERNEL_LOG, "str_queue_mk");
322         exit (1);
323     }
324     if (userid != -1)
325     {
326         read_kernel_res ();
327         kernel_events (queue, userid);
328     }
329     else
330     {
331         char line_buf[512];
332         read_kernel_res ();
333         while (lgets (line_buf, sizeof(line_buf)-1, 0))
334             str_queue_enq (queue, line_buf);
335         urp_start (0, queue);
336         urp_command (queue);
337         urp_end ();
338     }
339     return 0;
340 }
341
342 struct gw_user_set *user_set_add (const char *name, int hits, 
343                                   const char *database, 
344                                   struct ccl_rpn_node *rpn,
345                                   int present_flag)
346 {
347     struct gw_user_set *s;
348
349     s = malloc (sizeof (*s));
350     assert (s);
351
352     s->name = gw_strdup (name);
353     s->hits = hits;
354     s->database = gw_strdup (database);
355     s->rpn = rpn;
356     s->present_flag = present_flag;
357     s->prev = info.sets;
358     info.sets = s;
359     return s;
360 }
361
362 void user_set_init (void)
363 {
364     struct gw_user_set *s, *s1;
365
366     for (s = info.sets; s; s = s1)
367     {
368         free (s->name);
369         free (s->database);
370         ccl_rpn_delete (s->rpn);
371         s1 = s->prev;
372         free (s);
373     }
374     info.sets = NULL;
375 }
376
377 struct gw_user_set *user_set_search (const char *name)
378 {
379     struct gw_user_set *s;
380
381     if (!name)
382         return info.sets;
383     for (s = info.sets; s; s = s->prev)
384         if (!strcmp (s->name, name))
385             return s;
386     return NULL;
387 }
388
389 #if USE_FML
390 static void fml_inf_write (int ch)
391 {
392     putc (ch, reply_fd);
393 }
394 static FILE *fml_inf;
395
396 static int fml_inf_read (void)
397 {
398     return getc (fml_inf);
399 }
400 #endif
401
402 void read_kernel_res (void)
403 {
404     char path_prefix[128];
405     char fname[160];
406     const char *v;
407     char *cp;
408     char resource_name[256];
409
410     user_set_init ();
411
412     if (info.bibset)
413         ccl_qual_rm (&info.bibset);
414     info.bibset = ccl_qual_mk ();
415
416     if (info.kernel_res)
417         gw_res_close (info.kernel_res);
418     info.kernel_res = gw_res_init ();
419
420     gw_log (GW_LOG_DEBUG, KERNEL_LOG, "reading kernel resource, default %s",
421             info.default_res);
422     if (*info.target)
423         gw_log (GW_LOG_DEBUG, KERNEL_LOG, "reading kernel resource, target %s",
424                 info.target);
425     if (info.lang)
426         gw_log (GW_LOG_DEBUG, KERNEL_LOG, "reading kernel resource, lang %s",
427                 info.lang);
428
429     if (gw_res_merge (info.kernel_res, info.default_res))
430     {
431         gw_log (GW_LOG_WARN, KERNEL_LOG, "Couldn't read resource file %s",
432                 info.default_res);
433         return;
434     }
435     strcpy (path_prefix, gw_res_get (info.kernel_res, "gw.path", "."));
436     
437     if (*info.target)
438     {
439         sprintf (resource_name, "gw.target.%s", info.target);
440         v = gw_res_get (info.kernel_res, resource_name, NULL);
441         if (v)
442         {
443             sprintf (fname, "%s/%s", path_prefix, v);
444             gw_res_merge (info.kernel_res, fname);
445         }
446     }
447     if (info.lang)
448     {
449         sprintf (resource_name, "gw.lang.%s", info.lang);
450         v = gw_res_get (info.kernel_res, resource_name, NULL);
451         if (v)
452         {
453             sprintf (fname, "%s/%s", path_prefix, v);
454             gw_res_merge (info.kernel_res, fname);
455         }
456     }
457     if (info.override_res)
458     {
459         sprintf (fname, "%s/%s", path_prefix, info.override_res);
460         gw_res_merge (info.kernel_res, fname);        
461     }
462     v = gw_res_get (info.kernel_res, "gw.bibset", NULL);
463     if (v)
464     {
465         FILE *bib_inf;
466
467         sprintf (fname, "%s/%s", path_prefix, v);
468         bib_inf = fopen (fname, "r");
469         if (!bib_inf)
470             gw_log (GW_LOG_WARN, KERNEL_LOG, "cannot open %s", fname);
471         else
472         {
473             gw_log (GW_LOG_DEBUG, KERNEL_LOG, "reading bib file %s", fname);
474             ccl_qual_file (info.bibset, bib_inf);
475             fclose (bib_inf);
476         }
477     }
478     sprintf (resource_name, "gw.target.%s", info.target);
479     if (*info.target && ! gw_res_get (info.kernel_res, resource_name, NULL))
480     {
481         /* target is there, and there is no sub-resource for it... */
482         char *split;
483
484         if ((split = strchr (info.target, ':')))
485             *split++ = '\0';
486         strncpy (info.hostname, info.target, sizeof(info.hostname)-1);
487         if (split)
488             info.port = atoi (split);
489         else
490             info.port = atoi (gw_res_get
491                               (info.kernel_res, "gw.portno", "210"));
492     }
493     else
494     {
495         strncpy (info.hostname, gw_res_get (info.kernel_res,
496                                             "gw.hostname", "localhost"),
497                  sizeof(info.hostname)-1);
498         info.port = atoi (gw_res_get (info.kernel_res,
499                                       "gw.portno", "210"));
500     }
501     if (info.databases)
502         free (info.databases);
503     if (info.database)
504         free (info.database);
505     v = gw_res_get (info.kernel_res, "gw.databases", "");
506     info.databases = gw_strdup (v);
507     for (cp = info.databases; (cp = strchr (cp, ' ')); cp++)
508         *cp = ',';
509     v = gw_res_get (info.kernel_res, "gw.database", "");
510     if (*v == '\0' && *info.databases)
511     {
512         int len;
513         cp = strchr (info.databases, ',');
514         
515         len = cp ? (cp-info.databases) : strlen (info.databases);
516         info.database = malloc (len+1);
517         assert (info.database);
518         memcpy (info.database, info.databases, len);
519         info.database[len] = '\0';
520     }
521     else
522     {
523         info.database = gw_strdup (v);
524         for (cp = info.database; (cp = strchr (cp, ' ')); cp++)
525             *cp = ',';
526     }
527     if (info.override_portno)
528         info.port = atoi (info.override_portno);
529     if (info.override_hostname)
530         strncpy (info.hostname, info.override_hostname,
531                  sizeof(info.hostname)-1);
532     v = gw_res_get (info.kernel_res, "gw.result.set", NULL);
533     info.setno = v ? -1 : 0;
534 #if USE_FML
535     if (!info.fml)
536     {
537         v = gw_res_get (info.kernel_res, "gw.fml", "default.fml");    
538         sprintf (fname, "%s/%s", path_prefix, v);
539         fml_inf = fopen (fname, "r");
540         if (!fml_inf)
541             gw_log (GW_LOG_WARN, KERNEL_LOG,
542                     "cannot open fml script %s", fname);
543         else
544         {
545             info.fml = fml_open ();
546             info.fml->read_func = fml_inf_read;
547             info.fml->write_func = fml_inf_write;
548             fml_preprocess (info.fml);
549             fml_exec (info.fml);
550             fclose (fml_inf);
551         }
552     }
553 #endif
554 }