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