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