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