]> arthur.barton.de Git - netatalk.git/blob - etc/cnid_dbd/dbif.c
add process id in syslog and small clean up.
[netatalk.git] / etc / cnid_dbd / dbif.c
1 /*
2  * $Id: dbif.c,v 1.1.4.15 2004-04-29 18:09:16 lenneis Exp $
3  *
4  * Copyright (C) Joerg Lenneis 2003
5  * All Rights Reserved.  See COPYING.
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif /* HAVE_CONFIG_H */
11
12 #include <stdio.h>
13 #include <errno.h>
14 #ifdef HAVE_SYS_TYPES_H
15 #include <sys/types.h>
16 #endif /* HAVE_SYS_TYPES_H */
17 #include <string.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <sys/cdefs.h>
21 #include <unistd.h>
22 #include <atalk/logger.h>
23 #include <db.h>
24 #include "db_param.h"
25 #include "dbif.h"
26
27 #define DB_ERRLOGFILE "db_errlog"
28
29
30 static DB_ENV *db_env = NULL;
31 static DB_TXN *db_txn = NULL;
32 static FILE   *db_errlog = NULL;
33
34 #ifdef CNID_BACKEND_DBD_TXN
35 #define DBOPTIONS    (DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN)
36 #else
37 #define DBOPTIONS    (DB_CREATE | DB_INIT_CDB | DB_INIT_MPOOL | DB_PRIVATE) 
38 #endif
39
40 static struct db_table {
41      char            *name;
42      DB              *db;
43      u_int32_t       general_flags;
44      DBTYPE          type;
45 } db_table[] =
46 {
47      { "cnid2.db",       NULL,      0, DB_BTREE},
48      { "devino.db",      NULL,      0, DB_BTREE},
49      { "didname.db",     NULL,      0, DB_BTREE},
50 };
51
52 static char *old_dbfiles[] = {"cnid.db", NULL};
53
54 extern int didname(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey);
55 extern int devino(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey);
56
57 /* --------------- */
58 static int  db_compat_associate (DB *p, DB *s,
59                    int (*callback)(DB *, const DBT *,const DBT *, DBT *),
60                    u_int32_t flags)
61 {
62 #if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 1)
63     return p->associate(p, db_txn, s, callback, flags);
64 #else
65 #if (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR == 0)
66     return p->associate(p,       s, callback, flags);
67 #else
68     return 0;
69 #endif
70 #endif
71 }
72
73 /* --------------- */
74 static int db_compat_open(DB *db, char *file, char *name, DBTYPE type, int mode)
75 {
76     int ret;
77
78 #if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 1)
79     ret = db->open(db, db_txn, file, name, type, DB_CREATE, mode); 
80 #else
81     ret = db->open(db,       file, name, type, DB_CREATE, mode); 
82 #endif
83
84     if (ret) {
85         LOG(log_error, logtype_cnid, "error opening database %s: %s", name, db_strerror(ret));
86         return -1;
87     } else {
88         return 0;
89     }
90 }
91
92 /* --------------- */
93 static int upgrade_required()
94 {
95     int i;
96     int found = 0;
97     struct stat st;
98     
99     for (i = 0; old_dbfiles[i] != NULL; i++) {
100         if ( !(stat(old_dbfiles[i], &st) < 0) ) {
101             found++;
102             continue;
103         }
104         if (errno != ENOENT) {
105             LOG(log_error, logtype_cnid, "cnid_open: Checking %s gave %s", old_dbfiles[i], strerror(errno));
106             found++;
107         }
108     }
109     return found;
110 }
111
112 /* --------------- */
113 int dbif_stamp(void *buffer, int size)
114 {
115     struct stat st;
116     int         rc;
117
118     if (size < 8)
119         return -1;
120
121     if ((rc = stat(db_table[0].name, &st)) < 0) {
122         LOG(log_error, logtype_cnid, "error stating database %s: %s", db_table[0].name, db_strerror(rc));
123         return -1;
124     }
125     memset(buffer, 0, size);
126     memcpy(buffer, &st.st_ctime, sizeof(st.st_ctime));
127
128     return 0;
129 }
130
131 /* --------------- */
132 /*
133  *  We assume our current directory is already the BDB homedir. Otherwise
134  *  opening the databases will not work as expected. If we use transactions,
135  *  dbif_env_init(), dbif_close() and dbif_stamp() are the only interface
136  *  functions that can be called without a valid transaction handle in db_txn.
137  */
138 int dbif_env_init(struct db_param *dbp)
139 {
140     int ret;
141 #ifdef CNID_BACKEND_DBD_TXN
142     char **logfiles = NULL;
143     char **file;
144 #endif
145
146     /* Refuse to do anything if this is an old version of the CNID database */
147     if (upgrade_required()) {
148         LOG(log_error, logtype_cnid, "Found version 1 of the CNID database. Please upgrade to version 2");
149         return -1;
150     }
151
152     if ((db_errlog = fopen(DB_ERRLOGFILE, "a")) == NULL)
153         LOG(log_warning, logtype_cnid, "error creating/opening DB errlogfile: %s", strerror(errno));
154
155 #ifdef CNID_BACKEND_DBD_TXN
156     if ((ret = db_env_create(&db_env, 0))) {
157         LOG(log_error, logtype_cnid, "error creating DB environment: %s", 
158             db_strerror(ret));
159         db_env = NULL;
160         return -1;
161     }    
162     if (db_errlog != NULL)
163         db_env->set_errfile(db_env, db_errlog); 
164     db_env->set_verbose(db_env, DB_VERB_RECOVERY, 1);
165     db_env->set_verbose(db_env, DB_VERB_CHKPOINT, 1);
166     if ((ret = db_env->open(db_env, ".", DBOPTIONS | DB_PRIVATE | DB_RECOVER, 0))) {
167         LOG(log_error, logtype_cnid, "error opening DB environment: %s", 
168             db_strerror(ret));
169         db_env->close(db_env, 0);
170         db_env = NULL;
171         return -1;
172     }
173
174     if (db_errlog != NULL)
175         fflush(db_errlog);
176
177     if ((ret = db_env->close(db_env, 0))) {
178         LOG(log_error, logtype_cnid, "error closing DB environment after recovery: %s", 
179             db_strerror(ret));
180         db_env = NULL;
181         return -1;
182     }
183 #endif
184     if ((ret = db_env_create(&db_env, 0))) {
185         LOG(log_error, logtype_cnid, "error creating DB environment after recovery: %s",
186             db_strerror(ret));
187         db_env = NULL;
188         return -1;
189     }
190     if ((ret = db_env->set_cachesize(db_env, 0, 1024 * dbp->cachesize, 0))) {
191         LOG(log_error, logtype_cnid, "error setting DB environment cachesize to %i: %s",
192             dbp->cachesize, db_strerror(ret));
193         db_env->close(db_env, 0);
194         db_env = NULL;
195         return -1;
196     }
197     
198     if (db_errlog != NULL)
199         db_env->set_errfile(db_env, db_errlog);
200     if ((ret = db_env->open(db_env, ".", DBOPTIONS , 0))) {
201         LOG(log_error, logtype_cnid, "error opening DB environment after recovery: %s",
202             db_strerror(ret));
203         db_env->close(db_env, 0);
204         db_env = NULL;
205         return -1;      
206     }
207
208 #ifdef CNID_BACKEND_DBD_TXN
209     if (dbp->nosync && (ret = db_env->set_flags(db_env, DB_TXN_NOSYNC, 1))) {
210         LOG(log_error, logtype_cnid, "error setting TXN_NOSYNC flag: %s",
211             db_strerror(ret));
212         db_env->close(db_env, 0);
213         db_env = NULL;
214         return -1;
215     }
216     if (dbp->logfile_autoremove && db_env->log_archive(db_env, &logfiles, 0)) {
217         LOG(log_error, logtype_cnid, "error getting list of stale logfiles: %s",
218             db_strerror(ret));
219         db_env->close(db_env, 0);
220         db_env = NULL;
221         return -1;
222     }
223     if (logfiles != NULL) {
224         for (file = logfiles; *file != NULL; file++) {
225             if (unlink(*file) < 0)
226                 LOG(log_warning, logtype_cnid, "Error removing stale logfile %s: %s", *file, strerror(errno));
227         }
228         free(logfiles);
229     }
230 #endif
231     return 0;
232 }
233
234 /* --------------- */
235 int dbif_open(struct db_param *dbp)
236 {
237     int ret;
238     int i;
239
240     for (i = 0; i != DBIF_DB_CNT; i++) {
241         if ((ret = db_create(&(db_table[i].db), db_env, 0))) {
242             LOG(log_error, logtype_cnid, "error creating handle for database %s: %s", 
243                 db_table[i].name, db_strerror(ret));
244             return -1;
245         }
246         if (db_table[i].general_flags) { 
247             if ((ret = db_table[i].db->set_flags(db_table[i].db, db_table[i].general_flags))) {
248                 LOG(log_error, logtype_cnid, "error setting flags for database %s: %s", 
249                     db_table[i].name, db_strerror(ret));
250                 return -1;
251             }
252         }
253 #if 0
254 #ifndef CNID_BACKEND_DBD_TXN
255         if ((ret = db_table[i].db->set_cachesize(db_table[i].db, 0, 1024 * dbp->cachesize, 0))) {
256             LOG(log_error, logtype_cnid, "error setting DB cachesize to %i for database %s: %s",
257                 dbp->cachesize, db_table[i].name, db_strerror(ret));
258             return -1;
259         }
260 #endif /* CNID_BACKEND_DBD_TXN */
261 #endif
262         if (db_compat_open(db_table[i].db, db_table[0].name, db_table[i].name, db_table[i].type, 0664) < 0)
263             return -1;
264         if (db_errlog != NULL)
265             db_table[i].db->set_errfile(db_table[i].db, db_errlog);
266     }
267     
268     /* TODO: Implement CNID DB versioning info on new databases. */
269     /* TODO: Make transaction support a runtime option. */
270     /* Associate the secondary with the primary. */
271     if ((ret = db_compat_associate(db_table[0].db, db_table[DBIF_IDX_DIDNAME].db, didname, 0)) != 0) {
272         LOG(log_error, logtype_cnid, "Failed to associate didname database: %s",db_strerror(ret));
273         return -1;
274     }
275  
276     if ((ret = db_compat_associate(db_table[0].db, db_table[DBIF_IDX_DEVINO].db, devino, 0)) != 0) {
277         LOG(log_error, logtype_cnid, "Failed to associate devino database: %s",db_strerror(ret));
278         return -1;
279     }
280
281     return 0;
282 }
283
284 /* ------------------------ */
285 int dbif_close()
286 {
287     int i;
288     int ret;
289     int err = 0;
290      
291     for (i = DBIF_DB_CNT -1; i >= 0; i--) {
292         if (db_table[i].db != NULL && (ret = db_table[i].db->close(db_table[i].db, 0))) {
293             LOG(log_error, logtype_cnid, "error closing database %s: %s", db_table[i].name, db_strerror(ret));
294             err++;
295         }
296     }
297     if (db_env != NULL && (ret = db_env->close(db_env, 0))) { 
298         LOG(log_error, logtype_cnid, "error closing DB environment: %s", db_strerror(ret));
299         err++;
300     }
301     if (db_errlog != NULL && fclose(db_errlog) == EOF) {
302         LOG(log_error, logtype_cnid, "error closing DB logfile: %s", strerror(errno));
303         err++;
304     }
305     if (err)
306         return -1;
307     else
308         return 0;
309 }
310
311 /*
312  *  The following three functions are wrappers for DB->get(), DB->put() and
313  *  DB->del(). We define them here because we want access to the db_txn
314  *  transaction handle and the database handles limited to the functions in this
315  *  file. A consequence is that there is always only one transaction in
316  *  progress. For nontransactional access db_txn is NULL. All three return -1 on
317  *  error. dbif_get()/dbif_del return 1 if the key was found and 0
318  *  otherwise. dbif_put() returns 0 if key/val was successfully updated and 1 if
319  *  the DB_NOOVERWRITE flag was specified and the key already exists.
320  *  
321  *  All return codes other than DB_NOTFOUND and DB_KEYEXIST from the DB->()
322  *  functions are not expected and therefore error conditions.
323  */
324
325 int dbif_get(const int dbi, DBT *key, DBT *val, u_int32_t flags)
326 {
327     int ret;
328     DB *db = db_table[dbi].db;
329
330     ret = db->get(db, db_txn, key, val, flags);
331      
332     if (ret == DB_NOTFOUND)
333         return 0;
334     if (ret) {
335         LOG(log_error, logtype_cnid, "error retrieving value from %s: %s", db_table[dbi].name, db_strerror(errno));
336         return -1;
337     } else 
338         return 1;
339 }
340
341 /* search by secondary return primary */
342 int dbif_pget(const int dbi, DBT *key, DBT *pkey, DBT *val, u_int32_t flags)
343 {
344     int ret;
345     DB *db = db_table[dbi].db;
346
347     ret = db->pget(db, db_txn, key, pkey, val, flags);
348
349 #if DB_VERSION_MAJOR >= 4
350     if (ret == DB_NOTFOUND || ret == DB_SECONDARY_BAD) {
351 #else
352     if (ret == DB_NOTFOUND) {
353 #endif     
354         return 0;
355     }
356     if (ret) {
357         LOG(log_error, logtype_cnid, "error retrieving value from %s: %s", db_table[dbi].name, db_strerror(errno));
358         return -1;
359     } else 
360         return 1;
361 }
362
363 /* -------------------------- */
364 int dbif_put(const int dbi, DBT *key, DBT *val, u_int32_t flags)
365 {
366     int ret;
367     DB *db = db_table[dbi].db;
368
369     ret = db->put(db, db_txn, key, val, flags);
370      
371     if (ret) {
372         if ((flags & DB_NOOVERWRITE) && ret == DB_KEYEXIST) {
373             return 1;
374         } else {
375             LOG(log_error, logtype_cnid, "error setting key/value in %s: %s", db_table[dbi].name, db_strerror(errno));
376             return -1;
377         }
378     } else
379         return 0;
380 }
381
382 int dbif_del(const int dbi, DBT *key, u_int32_t flags)
383 {
384     int ret;
385     DB *db = db_table[dbi].db;
386
387     ret = db->del(db, db_txn, key, flags);
388
389 #if DB_VERSION_MAJOR > 3
390     if (ret == DB_NOTFOUND || ret == DB_SECONDARY_BAD)
391 #else
392     if (ret == DB_NOTFOUND)
393 #endif
394         return 0;
395     if (ret) {
396         LOG(log_error, logtype_cnid, "error deleting key/value from %s: %s", db_table[dbi].name, db_strerror(errno));
397         return -1;
398     } else
399         return 1;
400 }
401
402 #ifdef CNID_BACKEND_DBD_TXN
403
404 int dbif_txn_begin()
405 {
406     int ret;
407 #if DB_VERSION_MAJOR >= 4
408     ret = db_env->txn_begin(db_env, NULL, &db_txn, 0);
409 #else     
410     ret = txn_begin(db_env, NULL, &db_txn, 0);
411 #endif
412     if (ret) {
413         LOG(log_error, logtype_cnid, "error starting transaction: %s", db_strerror(errno));
414         return -1;
415     } else 
416         return 0;
417 }
418
419 int dbif_txn_commit()
420 {
421     int ret;
422 #if DB_VERSION_MAJOR >= 4
423     ret = db_txn->commit(db_txn, 0);
424 #else
425     ret = txn_commit(db_txn, 0);
426 #endif
427     if (ret) {
428         LOG(log_error, logtype_cnid, "error committing transaction: %s", db_strerror(errno));
429         return -1;
430     } else 
431         return 0;
432 }
433
434 int dbif_txn_abort()
435 {
436     int ret;
437 #if DB_VERSION_MAJOR >= 4
438     ret = db_txn->abort(db_txn);
439 #else
440     ret = txn_abort(db_txn);
441 #endif
442     if (ret) {
443         LOG(log_error, logtype_cnid, "error aborting transaction: %s", db_strerror(errno));
444         return -1;
445     } else
446         return 0;
447 }
448
449 int dbif_txn_checkpoint(u_int32_t kbyte, u_int32_t min, u_int32_t flags)
450 {
451     int ret;
452 #if DB_VERSION_MAJOR >= 4
453     ret = db_env->txn_checkpoint(db_env, kbyte, min, flags);
454 #else 
455     ret = txn_checkpoint(db_env, kbyte, min, flags);
456 #endif
457     if (ret) {
458         LOG(log_error, logtype_cnid, "error checkpointing transaction susystem: %s", db_strerror(errno));
459         return -1;
460     } else 
461         return 0;
462 }
463
464 #else
465
466 int dbif_sync()
467 {
468     int i;
469     int ret;
470     int err = 0;
471      
472     for (i = 0; i != /* DBIF_DB_CNT*/ 1; i++) {
473         if ((ret = db_table[i].db->sync(db_table[i].db, 0))) {
474             LOG(log_error, logtype_cnid, "error syncing database %s: %s", db_table[i].name, db_strerror(ret));
475             err++;
476         }
477     }
478  
479     if (err)
480         return -1;
481     else
482         return 0;
483 }
484
485 #endif /* CNID_BACKEND_DBD_TXN */
486