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