Bug fix: missing argument to gw_log-call.
[egate.git] / util / gw-db.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 utility
45  * Europagate, 1995
46  *
47  * $Log: gw-db.c,v $
48  * Revision 1.5  1996/01/24 17:00:12  adam
49  * Bug fix: missing argument to gw_log-call.
50  *
51  * Revision 1.4  1995/12/20  16:28:07  adam
52  * Extra parameter block to gw_db_open. If block is 0 gw_db_open
53  * returns NULL if lock couldn't be satisfied.
54  * Minor changes in iso2709.c.
55  *
56  * Revision 1.3  1995/05/16  09:40:53  adam
57  * LICENSE.
58  *
59  * Revision 1.2  1995/05/01  12:43:57  adam
60  * lgets function moved from kernel to util.
61  *
62  * Revision 1.1  1995/03/27  08:25:01  adam
63  * New module gip: Gateway IPc module.
64  * New module gw-db: Gateway hash-db module (user information table).
65  *
66  */
67
68 #include <stdio.h>
69 #include <stdlib.h>
70 #include <assert.h>
71 #include <string.h>
72 #include <unistd.h>
73 #include <fcntl.h>
74
75 #include <gw-log.h>
76 #include <gw-db.h>
77
78 #define FILE_HEAD_LEN 128
79 #define DB_HASH 1997
80
81 struct gw_db {
82     struct file_head {     /* file header info */
83         char magic[8];     /* start info */
84         int no_of_entries; /* no of entries in table */
85         int sequence_no;   /* number of next to come */
86     } head;
87     int *hash_array;       /* hash array pointers (DB_HASH in size) */
88     int fd;                /* descriptor of file */
89     int dirty;             /* any writes so far? */
90 };
91
92 struct db_bucket {         /* information about individual entries */
93     int  name_length;      /* length of name */
94     int  info_length;      /* length of information */
95     int  next;             /* next in hash chain - possibly 0 */
96     int  prev;             /* prev in hach chain - possibly 0 */
97 };
98
99 static char *mod = "gwdb";
100
101 /*
102  * write_head: write header of table
103  */
104 static int write_head (GW_DB db)
105 {
106     char file_head_buf[FILE_HEAD_LEN];
107     int r;
108
109     if (lseek (db->fd, 0L, SEEK_SET) == -1)
110         return -1;
111     memcpy (file_head_buf, &db->head, sizeof(db->head));
112     r = write (db->fd, file_head_buf, FILE_HEAD_LEN);
113     if (r == -1)
114         return -1;
115     r = write (db->fd, db->hash_array, DB_HASH * sizeof(*db->hash_array));
116     if (r == -1)
117         return -1;
118     return 0;
119 }
120
121 /*
122  * hash: calculate hash value
123  */
124 static unsigned hash (const char *name, int length)
125 {
126     unsigned l = 0;
127
128     while (--length >= 0)
129         l = l*65599 + *name++;
130     return l % DB_HASH;
131 }
132
133 /*
134  * lock_file: lock entire file 
135  */
136 static int lock_file (int fd, int block, int type)
137 {
138     struct flock area;
139     area.l_type = type;
140     area.l_whence = SEEK_SET;
141     area.l_start = 0L;
142     area.l_len = 0L;
143     return fcntl (fd, block ? F_SETLKW : F_SETLK, &area);
144 }
145
146
147 /*
148  * gw_db_lookup: table lookup
149  */
150 int gw_db_lookup (GW_DB db, const char *name, int name_length,
151                   void **buf, size_t *count)
152 {
153     struct db_bucket bucket;
154     unsigned l = hash (name, name_length);
155     char     *dbuf = NULL;
156     int      dsize = 0;
157     int      pos;
158     int      r;
159
160     for (pos = db->hash_array[l]; pos; pos = bucket.next)
161     {
162         int blen;
163
164         if (lseek (db->fd, pos, SEEK_SET) == -1)
165         {
166             free (dbuf);
167             return -1;
168         }
169         r = read (db->fd, &bucket, sizeof(bucket));
170         if (r == -1)
171         {
172             free (dbuf);
173             return -1;
174         }
175         if (r != sizeof(bucket))
176         {
177             free (dbuf);
178             return -2;
179         }
180         if (bucket.name_length <= 0 || bucket.info_length <= 0 ||
181             bucket.name_length >= 16384 || bucket.info_length >= 262144)
182         {
183             free (dbuf);
184             return -3;
185         }
186         if (bucket.name_length != name_length)
187             continue;
188         blen = bucket.name_length + bucket.info_length;
189         if (blen >= dsize)
190         {
191             dsize = blen + 1024;
192             free (dbuf);
193             if (!(dbuf = malloc (dsize)))
194                 return -1;
195         }
196         r = read (db->fd, dbuf, blen);
197         if (r == -1)
198         {
199             free (dbuf);
200             return -1;
201         }
202         else if (r < blen)
203         {
204             free (dbuf);
205             return -2;
206         }
207         if (memcmp (name, dbuf, name_length))
208             continue;
209
210         *count = bucket.info_length;
211         *buf = malloc (*count);
212         if (!*buf)
213         {
214             free (dbuf);
215             return -1;
216         }
217         memcpy (*buf, dbuf + name_length, *count);
218         free (dbuf);
219         return 1;
220     }
221     free (dbuf);
222     return 0;
223 }
224
225 /*
226  * gw_db_insert: table insertion
227  */
228 int gw_db_insert (GW_DB db, const char *name, int name_length,
229                   const void *buf, size_t count)
230 {
231     struct db_bucket n_bucket;
232     struct db_bucket bucket;
233     off_t    n_pos, r_pos;
234     unsigned l = hash (name, name_length);
235     int      pos = db->hash_array[l];
236     int      r;
237
238     db->dirty = 1;
239     (db->head.no_of_entries)++;
240     (db->head.sequence_no)++;
241     n_bucket.name_length = name_length;
242     n_bucket.info_length = count;
243     n_pos = lseek (db->fd, 0, SEEK_END);
244     if (n_pos == -1)
245         return -1;
246     n_bucket.next = pos;
247     db->hash_array[l] = n_pos;
248     n_bucket.prev = 0;
249
250     r = write (db->fd, &n_bucket, sizeof(n_bucket));
251     if (r == -1)
252         return -1;
253     else if (r < sizeof(n_bucket))
254         return -2;
255
256     r = write (db->fd, name, name_length);
257     if (r == -1)
258         return -1;
259     else if (r < name_length)
260         return -2;
261
262     r = write (db->fd, buf, count);
263     if (r == -1)
264         return -1;
265     else if (r < count)
266         return -2;
267
268     r_pos = lseek (db->fd, pos, SEEK_SET);
269     if (r_pos == -1)
270         return -1;
271     r = read (db->fd, &bucket, sizeof(bucket));
272     if (r == -1)
273         return -1;
274     else if (r < sizeof(bucket))
275         return -2;
276     bucket.prev = n_pos;
277
278     r_pos = lseek (db->fd, pos, SEEK_SET);
279     if (r_pos == -1)
280         return -1;
281     r = write (db->fd, &bucket, sizeof(bucket));
282     if (r == -1)
283         return -1;
284     else if (r < sizeof(bucket))
285         return -2;
286     return 0;
287 }
288
289 /*
290  * gw_db_free: release memory associated with table
291  */
292 static GW_DB gw_db_free (GW_DB db)
293 {
294     free (db->hash_array);
295     free (db);
296     return NULL;
297 }
298
299 /*
300  * gw_db_open: open and lock table for reading (and writing)
301  */
302 GW_DB gw_db_open (const char *fname, int write_flag, int block)
303 {
304     char file_head_buf[FILE_HEAD_LEN];
305     GW_DB db;
306     int r;
307
308     db = malloc (sizeof(*db));
309     if (!db)
310     {
311         gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "malloc");
312         exit (1);
313     }
314     db->dirty = 0;
315     if (!(db->hash_array = malloc (DB_HASH * sizeof(*db->hash_array))))
316     {
317         gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "malloc");
318         exit (1);
319     }
320     if (write_flag)
321     {
322         if ((db->fd = open (fname, O_RDWR|O_CREAT, 0666)) == -1)
323         {
324             gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, mod, "open %s", fname);
325             exit (1);
326         }
327     }
328     else
329     {
330         if ((db->fd = open (fname, O_RDONLY)) == -1)
331         {
332             gw_log (GW_LOG_WARN|GW_LOG_ERRNO, mod, " open %s", fname);
333             return gw_db_free (db);
334         }
335     }
336     if (block)
337         lock_file (db->fd, 1, write_flag ? F_WRLCK : F_RDLCK);
338     else
339     {
340         r = lock_file (db->fd, 0, write_flag ? F_WRLCK : F_RDLCK);
341         if (r == -1)
342         {
343             gw_log (GW_LOG_WARN|GW_LOG_ERRNO, mod, "flock %s", fname);
344             close (db->fd);
345             return gw_db_free (db);
346         }
347     }
348     r = read (db->fd, file_head_buf, FILE_HEAD_LEN);
349     if (r == -1)
350         return gw_db_free (db);
351     if (r < FILE_HEAD_LEN)
352     {
353         int i;
354
355         if (!write_flag)
356             return gw_db_free (db);
357         db->head.no_of_entries = 0;
358         db->head.sequence_no = 1;
359         for (i=0; i<DB_HASH; i++)
360             db->hash_array[i] = 0;
361         if (write_head (db))
362             return gw_db_free (db);
363     }
364     else
365     {
366         memcpy (&db->head, file_head_buf, sizeof(db->head));
367         r = read (db->fd, db->hash_array, sizeof(*db->hash_array)*DB_HASH);
368         if (r < sizeof(*db->hash_array)*DB_HASH)
369             return gw_db_free (db);
370     }
371     return db;
372 }
373
374 /*
375  * gw_db_close: close table and flush
376  */
377 int gw_db_close (GW_DB db)
378 {
379     int r = 0;
380
381     if (db->dirty) 
382         r = write_head (db);
383     lock_file (db->fd, 1, F_UNLCK);
384     if (close (db->fd) == -1)
385     {
386         gw_db_free (db);
387         return -1;
388     }
389     gw_db_free (db);
390     return r;
391 }
392
393 /*
394  * gw_db_no_ent: return number of entries in table
395  */
396 int gw_db_no_ent (GW_DB db)
397 {
398     return db->head.no_of_entries;
399 }
400
401 /*
402  * gw_db_seq_no: return sequence number
403  */
404 int gw_db_seq_no (GW_DB db)
405 {
406     return db->head.sequence_no;
407 }
408