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