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