Bug fixes. Better command line options.
[egate.git] / kernel / persist.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 - Z39.50 Persistence
45  * Europagate, 1995
46  *
47  * $Log: persist.c,v $
48  * Revision 1.9  1995/05/19 13:26:00  adam
49  * Bug fixes. Better command line options.
50  *
51  * Revision 1.8  1995/05/16  09:40:43  adam
52  * LICENSE. Setting of CCL token names (and/or/not/set) in read_kernel_res.
53  *
54  * Revision 1.7  1995/05/03  16:34:19  adam
55  * CCL def command, i.e. user definitions - saved as resource files.
56  *
57  * Revision 1.6  1995/05/03  07:37:44  adam
58  * CCL commands stop/continue implemented. New functions gw_res_{int,bool}
59  * are used when possible.
60  *
61  * Revision 1.5  1995/05/02  15:26:00  adam
62  * Monitor observes death of child (email kernel). The number
63  * of simultanous processes is controlled now. Email requests are
64  * queued if necessary. This scheme should only be forced if no kernels
65  * are idle.
66  *
67  * Revision 1.4  1995/04/20  16:10:46  adam
68  * Modified to work with non-blocking zass-api. Not using non-blocking
69  * facility yet.
70  *
71  * Revision 1.3  1995/04/19  13:19:09  adam
72  * New command: account - for authentication.
73  *
74  * Revision 1.2  1995/04/19  10:46:19  adam
75  * Persistency works much better now. New command: status - history-like
76  *
77  * Revision 1.1  1995/04/19  07:31:10  adam
78  * First work on Z39.50 persistence.
79  *
80  */
81
82 #include <stdio.h>
83 #include <stdlib.h>
84 #include <assert.h>
85 #include <ctype.h>
86 #include <string.h>
87 #include <unistd.h>
88 #include <fcntl.h>
89
90 #include "kernel.h"
91
92 static int fgetsx (char *buf, int size, FILE *inf)
93 {
94     char *cp;
95
96     if (!fgets (buf, size, inf))
97         return 0;
98     if ((cp = strchr (buf, '\n')))
99         *cp = '\0';
100     return 1;
101 }
102
103 static int set_change;
104
105 static int obtain_set (ZASS zass, struct gw_user_set *set)
106 {
107     const struct zass_searchent *p;
108
109     p = zass_search (zass, set->rpn, set->name, set->database, NULL);
110     if (!p)
111         return 2;
112     if (p->errcode != -1)
113         return 3;
114     if (p->num != set->hits)
115         set_change = 1;
116     set->present_flag = 1;
117     set->hits = p->num;
118     gw_log (GW_LOG_DEBUG, KERNEL_LOG, "Set %s researched", set->name);
119     return 0;
120 }
121
122 static int obtain_sets (ZASS zass, struct ccl_rpn_node *rpn, 
123                         struct gw_user_set *sets)
124 {
125     struct gw_user_set *set;
126     int r;
127
128     switch (rpn->kind)
129     {
130     case CCL_RPN_AND:
131     case CCL_RPN_OR:
132     case CCL_RPN_NOT:
133     case CCL_RPN_PROX:
134         if ((r=obtain_sets (zass, rpn->u.p[0], sets)))
135             return r;
136         return obtain_sets (zass, rpn->u.p[1], sets);
137     case CCL_RPN_TERM:
138         return 0;
139     case CCL_RPN_SET:
140         set = user_set_search (rpn->u.setname);
141         if (!set)
142         {
143             gw_log (GW_LOG_DEBUG, KERNEL_LOG, "Set %s not there at all",
144                     rpn->u.setname);
145             return 1;
146         }
147         if (set->present_flag)
148         {
149             gw_log (GW_LOG_DEBUG, KERNEL_LOG, "Set %s already there",
150                     rpn->u.setname);
151             return 0;
152         }
153     }
154     return obtain_set (zass, set);
155 }
156
157 const struct zass_searchent *zass_p_search (ZASS zass, 
158                         struct ccl_rpn_node *rpn, 
159                         const char *result_set,
160                         const char *database,
161                         struct gw_user_set *sets)
162 {
163     int r;
164
165     set_change = 0;
166     r = obtain_sets (zass, rpn, sets);
167     if (r)
168         return NULL;
169     return zass_search (zass, rpn, (char*) result_set, (char*) database, NULL);
170 }
171
172 const struct zass_presentent *zass_p_present (ZASS zass,
173                         const char *result_set, int offset, int number)
174 {
175     struct gw_user_set *set;
176
177     set = user_set_search (result_set);
178     if (!set)
179         return NULL;
180     if (!set->present_flag)
181     {
182         const struct zass_searchent *p;
183
184         p = zass_p_search (zass, set->rpn, result_set, set->database,
185                            info.sets);
186         if (!p)
187             return NULL;
188     }
189     return zass_present (zass, (char*) result_set, offset, number, NULL);
190 }
191
192 struct ccl_rpn_node *load_rpn (char *buf, FILE *inf)
193 {
194     struct ccl_rpn_node *rpn;
195     struct ccl_rpn_attr **attrp;
196     int type, value, no_read;
197     char *cp;
198
199     if (!fgetsx (buf, 1024, inf))
200         return NULL;
201     rpn = malloc (sizeof (*rpn));
202     if (!rpn)
203         return NULL;
204     switch (*buf)
205     {
206     case 'A':
207         rpn->kind = CCL_RPN_AND;
208         rpn->u.p[0] = load_rpn (buf, inf);
209         rpn->u.p[1] = load_rpn (buf, inf);
210         break;
211     case 'O':
212         rpn->kind = CCL_RPN_OR;
213         rpn->u.p[0] = load_rpn (buf, inf);
214         rpn->u.p[1] = load_rpn (buf, inf);
215         break;
216     case 'N':
217         rpn->kind = CCL_RPN_NOT;
218         rpn->u.p[0] = load_rpn (buf, inf);
219         rpn->u.p[1] = load_rpn (buf, inf);
220         break;
221     case 'P':
222         rpn->kind = CCL_RPN_PROX;
223         rpn->u.p[0] = load_rpn (buf, inf);
224         rpn->u.p[1] = load_rpn (buf, inf);
225         break;
226     case 'T':
227         rpn->kind = CCL_RPN_TERM;
228
229         rpn->u.t.term = gw_strdup (buf+2);
230         attrp = &rpn->u.t.attr_list;
231         if (!fgetsx (buf, 1024, inf))
232             return NULL;
233         cp = buf;
234         while (sscanf (cp, "%d %d%n", &type, &value, &no_read) > 1)
235         {
236             *attrp = malloc (sizeof(**attrp));
237             (*attrp)->type = type;
238             (*attrp)->value = value;
239             attrp = &(*attrp)->next;
240             cp += no_read; 
241         }
242         *attrp = NULL;
243         break;
244     case 'S':
245         rpn->kind = CCL_RPN_SET;
246         rpn->u.setname = gw_strdup (buf+2);
247         break;
248     default:
249         free (rpn);
250         return NULL;
251     }
252     return rpn;
253 }
254
255 int load_p_state (int userid)
256 {
257     FILE *inf;
258     char fname[128];
259     char fline[1025];
260     char database[1024];
261     char resultname[32];
262     int hits;
263     struct gw_user_set *set;
264
265     sprintf (fname, "user.%d.p", userid);
266
267     inf = fopen (fname, "r");
268     if (!inf)
269     {
270         gw_log (GW_LOG_WARN|GW_LOG_ERRNO, KERNEL_LOG, 
271                 "Couldn't open %s", fname);
272         return -1;
273     }
274     gw_log (GW_LOG_DEBUG, KERNEL_LOG, 
275        "Reading persistence file %s", fname);
276
277     if (!fgetsx (fline, 1024, inf))
278         return -1;
279     if (sscanf (fline, "%s", info.target) != 1)
280         *info.target = '\0';
281     read_kernel_res ();
282
283     if (!fgetsx (fline, 1024, inf))
284         return -1;
285     if (sscanf (fline, "%s", info.account) != 1)
286         *info.account = '\0';
287    
288     if (!fgetsx (fline, 1024, inf))
289         return -1;
290     free (info.database);
291     info.database = gw_strdup (fline);
292
293     if (!fgetsx (fline, 1024, inf))
294         return -1;
295     if (sscanf (fline, "%d", &info.setno) != 1)
296         return -1;
297     if (!fgetsx (fline, 1024, inf))
298         return -1;
299     if (sscanf (fline, "%d", &info.next_position) != 1)
300         return -1;
301     gw_log (GW_LOG_DEBUG, KERNEL_LOG, 
302            "Reading persistence file %s (2)", fname);
303     while (fgetsx (fline, 1024, inf))
304     {
305         gw_log (GW_LOG_DEBUG, KERNEL_LOG, 
306               "Reading persistence file %s (3)", fname);
307         if (sscanf (fline, "%s %d %s", resultname, &hits, database) != 3)
308             return -1;
309         if (!fgetsx (fline, 1024, inf))        /* search string */
310             return -1;
311         gw_log (GW_LOG_DEBUG, KERNEL_LOG,
312                 "Adding %s, %d hits, database %s",
313                 resultname, hits, database);
314         gw_log (GW_LOG_DEBUG, KERNEL_LOG, "Search string %s", fline);
315         set = user_set_add (resultname, hits, database, NULL, 0, fline);
316         set->rpn = load_rpn (fline, inf);
317 #if 0
318         ccl_pr_tree (set->rpn, stderr);
319 #endif
320         fgetsx (fline, 1024, inf);
321     }
322     fclose (inf);
323     gw_log (GW_LOG_DEBUG, KERNEL_LOG, "Finished reading %s", fname);
324     return 0;
325 }
326
327 static void save_rpn (struct ccl_rpn_node *rpn, FILE *of)
328 {
329     struct ccl_rpn_attr *attr;
330
331     switch (rpn->kind)
332     {
333     case CCL_RPN_AND:
334         fprintf (of, "A\n");
335         save_rpn (rpn->u.p[0], of);
336         save_rpn (rpn->u.p[1], of);
337         break;
338     case CCL_RPN_OR:
339         fprintf (of, "O\n");
340         save_rpn (rpn->u.p[0], of);
341         save_rpn (rpn->u.p[1], of);
342         break;
343     case CCL_RPN_NOT:
344         fprintf (of, "N\n");
345         save_rpn (rpn->u.p[0], of);
346         save_rpn (rpn->u.p[1], of);
347         break;
348     case CCL_RPN_PROX:
349         fprintf (of, "P\n");
350         save_rpn (rpn->u.p[0], of);
351         save_rpn (rpn->u.p[1], of);
352         break;
353     case CCL_RPN_TERM:
354         fprintf (of, "T %s\n", rpn->u.t.term);
355         for (attr = rpn->u.t.attr_list; attr; attr = attr->next)
356             fprintf (of, "%d %d ", attr->type, attr->value);
357         fprintf (of, "\n");
358         break;
359     case CCL_RPN_SET:
360         fprintf (of, "S %s\n", rpn->u.setname); 
361     }
362 }
363
364 static void save_sets (FILE *of, struct gw_user_set *sp)
365 {
366     if (!sp)
367         return;
368     save_sets (of, sp->prev);
369     fprintf (of, "%s %d %s\n%s\n", sp->name, sp->hits, sp->database,
370              sp->search_str);
371     save_rpn (sp->rpn, of);
372     fprintf (of, "X\n");
373 }
374
375 int save_p_state (int userid)
376 {
377     FILE *of;
378     char fname[128];
379
380     sprintf (fname, "user.%d.p", userid);
381
382     of = fopen (fname, "w");
383     if (!of)
384     {
385         gw_log (GW_LOG_WARN|GW_LOG_ERRNO, KERNEL_LOG, 
386                 "Couldn't open %s", fname);
387         return -1;
388     }
389     gw_log (GW_LOG_DEBUG, KERNEL_LOG, "Writing persistence file %s", fname);
390     fprintf (of, "%s\n%s\n%s\n%d\n%d\n", info.target, info.account,
391              info.database, info.setno, info.next_position);
392     save_sets (of, info.sets);
393     fclose (of);
394     return 0;
395 }
396
397 void del_p_state (int userid)
398 {
399     char fname[128];
400
401     sprintf (fname, "user.%d.p", userid);
402     unlink (fname);
403 }
404