1 /* $Id: ovdb.c 10513 2021-01-15 22:34:56Z iulius $
2 *
3 * ovdb.c
4 * ovdb 2.00
5 * Overview storage using Berkeley DB 4.4 or higher
6 *
7 * 2004-02-17 : Need to track search cursors, since it's possible that
8 * ovdb_closesearch does not get called. We now close
9 * any cursors still open in ovdb_close, or they'd be in
10 * the database indefinitely causing deadlocks.
11 * 2002-08-13 : Change BOOL to bool, remove portability to < 2.4.
12 * 2002-08-11 : Cleaned up use of sprintf and fixed a bunch of warnings.
13 * 2002-02-28 : Update getartinfo for the overview API change in 2.4. This
14 * breaks compatibility with INN 2.3.x....
15 * 2000-12-12 : Add support for Berkeley DB DB_SYSTEM_MEM option, controlled
16 * : by ovdb.conf 'useshm' and 'shmkey'
17 * 2000-11-27 : Update for DB 3.2.x compatibility
18 * 2000-11-13 : New 'readserver' feature
19 * 2000-10-10 : ovdb_search now closes the cursor right after the last
20 * record is read.
21 * 2000-10-05 : artnum member of struct datakey changed from ARTNUM to u_int32_t.
22 * OS's where sizeof(long)==8 will have to rebuild their databases
23 * after this update.
24 * 2000-10-05 : from Dan Riley: struct datakey needs to be zero'd, for
25 * 64-bit OSs where the struct has internal padding bytes.
26 * 2000-09-29 : ovdb_expiregroup can now fix incorrect counts; use new
27 * inn/version.h so can have same ovdb.c for 2.3.0, 2.3.1, and 2.4
28 * 2000-09-28 : low mark in ovdb_expiregroup still wasn't right
29 * 2000-09-27 : Further improvements to ovdb_expiregroup: restructured the
30 * loop; now updates groupinfo as it goes along rather than
31 * counting records at the end, which prevents a possible
32 * deadlock.
33 * 2000-09-19 : *lo wasn't being set in ovdb_expiregroup
34 * 2000-09-15 : added ovdb_check_user(); tweaked some error msgs; fixed an
35 * improper use of RENEW
36 * 2000-08-28 : New major release: version 2.00 (beta)
37 * + "groupsbyname" and "groupstats" databases replaced with "groupinfo".
38 * + ovdb_recover, ovdb_upgrade, and dbprocs are now deprecated; their
39 * functionality is now in ovdb_init and ovdb_monitor.
40 * + ovdb_init can upgrade a database from the old version of ovdb to
41 * work with this version.
42 * + Rewrote ovdb_expiregroup(); it can now re-write OV data rather
43 * than simply deleting old keys (which can leave 'holes' that result
44 * in inefficient disk-space use).
45 * + Add "nocompact" to ovdb.conf, which controls whether ovdb_expiregroup()
46 * rewrites OV data.
47 * + No longer needs the Berkeley DB tools db_archive, db_checkpoint, and
48 * db_deadlock. That functionality is now in ovdb_monitor.
49 * + ovdb_open() won't succeed if ovdb_monitor is not running. This will
50 * prevent the problems that happen if the database is not regularly
51 * checkpointed and deadlock-tested.
52 * + Internal group IDs (32-bit ints) are now reused.
53 * + Add "maxlocks" to ovdb.conf, which will set the DB lk_max parameter.
54 * + Pull "test" code out into ovdb_stat. ovdb_stat will also provide
55 * functionality similar to the Berkeley DB "db_stat" command.
56 * + Update docs: write man pages for the new ovdb_* commands; update
57 * ovdb.pod
58 *
59 * 2000-07-11 : fix possible alignment problem; add test code
60 * 2000-07-07 : bugfix: timestamp handling
61 * 2000-06-10 : Modified groupnum() interface; fix ovdb_add() to return false
62 * for certain groupnum() errors
63 * 2000-06-08 : Added Berkeley DB 3.1.x compatibility
64 * 2000-04-09 : Tweak some default parameters; store aliased group info
65 * 2000-03-29 : Add DB_RMW flag to the 'get' of get-modify-put sequences
66 * 2000-02-17 : Update expire behavior to be consistent with current
67 * ov3 and buffindexed
68 * 2000-01-13 : Fix to make compatible with unmodified nnrpd/article.c
69 * 2000-01-04 : Added data versioning
70 * 1999-12-20 : Added Berkeley DB 3.x compatibility
71 * 1999-12-06 : First Release -- H. Kehoe <hakehoe@avalon.net>
72 */
73
74 #include "config.h"
75 #include "clibrary.h"
76 #include "portable/socket.h"
77 #include <errno.h>
78 #include <fcntl.h>
79 #ifdef HAVE_LIMITS_H
80 # include <limits.h>
81 #endif
82 #include <signal.h>
83 #ifdef HAVE_SYS_SELECT_H
84 # include <sys/select.h>
85 #endif
86 #ifdef HAVE_ZLIB
87 # include <zlib.h>
88 # define MAX_UNZIP_SZ 100000
89 # define COMPRESS_MIN 600
90 #endif
91
92 #ifdef HAVE_SYS_TIME_H
93 # include <sys/time.h>
94 #endif
95 #include <time.h>
96
97 #include "conffile.h"
98 #include "inn/fdflag.h"
99 #include "inn/innconf.h"
100 #include "inn/libinn.h"
101 #include "inn/messages.h"
102 #include "inn/newsuser.h"
103 #include "inn/paths.h"
104 #include "inn/storage.h"
105
106 #ifdef HAVE_UNIX_DOMAIN_SOCKETS
107 # include "portable/socket-unix.h"
108 #endif
109
110 #include "inn/ov.h"
111 #include "ovinterface.h"
112 #include "ovdb.h"
113 #include "ovdb-private.h"
114 #ifndef HAVE_BDB
115
116 /* Provide stub functions if we don't have db */
117
ovdb_open(int mode UNUSED)118 bool ovdb_open(int mode UNUSED)
119 {
120 warn("OVDB: ovdb support not enabled");
121 return false;
122 }
123
ovdb_groupstats(const char * group UNUSED,int * lo UNUSED,int * hi UNUSED,int * count UNUSED,int * flag UNUSED)124 bool ovdb_groupstats(const char *group UNUSED, int *lo UNUSED, int *hi UNUSED, int *count UNUSED, int *flag UNUSED)
125 { return false; }
126
ovdb_groupadd(const char * group UNUSED,ARTNUM lo UNUSED,ARTNUM hi UNUSED,char * flag UNUSED)127 bool ovdb_groupadd(const char *group UNUSED, ARTNUM lo UNUSED, ARTNUM hi UNUSED, char *flag UNUSED)
128 { return false; }
129
ovdb_groupdel(const char * group UNUSED)130 bool ovdb_groupdel(const char *group UNUSED)
131 { return false; }
132
ovdb_add(const char * group UNUSED,ARTNUM artnum UNUSED,TOKEN token UNUSED,char * data UNUSED,int len UNUSED,time_t arrived UNUSED,time_t expires UNUSED)133 bool ovdb_add(const char *group UNUSED, ARTNUM artnum UNUSED, TOKEN token UNUSED, char *data UNUSED, int len UNUSED, time_t arrived UNUSED, time_t expires UNUSED)
134 { return false; }
135
ovdb_cancel(const char * group UNUSED,ARTNUM artnum UNUSED)136 bool ovdb_cancel(const char *group UNUSED, ARTNUM artnum UNUSED)
137 { return false; }
138
ovdb_opensearch(const char * group UNUSED,int low UNUSED,int high UNUSED)139 void *ovdb_opensearch(const char *group UNUSED, int low UNUSED, int high UNUSED)
140 { return NULL; }
141
ovdb_search(void * handle UNUSED,ARTNUM * artnum UNUSED,char ** data UNUSED,int * len UNUSED,TOKEN * token UNUSED,time_t * arrived UNUSED)142 bool ovdb_search(void *handle UNUSED, ARTNUM *artnum UNUSED, char **data UNUSED, int *len UNUSED, TOKEN *token UNUSED, time_t *arrived UNUSED)
143 { return false; }
144
ovdb_closesearch(void * handle UNUSED)145 void ovdb_closesearch(void *handle UNUSED) { }
146
ovdb_getartinfo(const char * group UNUSED,ARTNUM artnum UNUSED,TOKEN * token UNUSED)147 bool ovdb_getartinfo(const char *group UNUSED, ARTNUM artnum UNUSED, TOKEN *token UNUSED)
148 { return false; }
149
ovdb_expiregroup(const char * group UNUSED,int * lo UNUSED,struct history * h UNUSED)150 bool ovdb_expiregroup(const char *group UNUSED, int *lo UNUSED, struct history *h UNUSED)
151 { return false; }
152
ovdb_ctl(OVCTLTYPE type UNUSED,void * val UNUSED)153 bool ovdb_ctl(OVCTLTYPE type UNUSED, void *val UNUSED)
154 { return false; }
155
ovdb_close(void)156 void ovdb_close(void) { }
157
158 #else /* HAVE_BDB */
159
160 #define EXPIREGROUP_TXN_SIZE 100
161 #define DELETE_TXN_SIZE 500
162
163 int ovdb_data_ver = 0;
164 struct ovdb_conf ovdb_conf;
165 DB_ENV *OVDBenv = NULL;
166
167 static int OVDBmode;
168 static bool Cutofflow;
169 static DB **dbs = NULL;
170 static int oneatatime = 0;
171 static int current_db = -1;
172 static time_t eo_start = 0;
173 static int clientmode = 0;
174
175 static DB *groupinfo = NULL;
176 static DB *groupaliases = NULL;
177
178 #define OVDBtxn_nosync 1
179 #define OVDBnumdbfiles 2
180 #define OVDBpagesize 3
181 #define OVDBcachesize 4
182 #define OVDBminkey 5
183 #define OVDBmaxlocks 6
184 #define OVDBnocompact 7
185 #define OVDBreadserver 8
186 #define OVDBnumrsprocs 9
187 #define OVDBmaxrsconn 10
188 #define OVDBuseshm 11
189 #define OVDBshmkey 12
190 #define OVDBcompress 13
191 #define OVDBncache 14
192
193 static CONFTOKEN toks[] = {
194 { OVDBtxn_nosync, (char *) "txn_nosync" },
195 { OVDBnumdbfiles, (char *) "numdbfiles" },
196 { OVDBpagesize, (char *) "pagesize" },
197 { OVDBcachesize, (char *) "cachesize" },
198 { OVDBminkey, (char *) "minkey" },
199 { OVDBmaxlocks, (char *) "maxlocks" },
200 { OVDBnocompact, (char *) "nocompact" },
201 { OVDBreadserver, (char *) "readserver" },
202 { OVDBnumrsprocs, (char *) "numrsprocs" },
203 { OVDBmaxrsconn, (char *) "maxrsconn" },
204 { OVDBuseshm, (char *) "useshm" },
205 { OVDBshmkey, (char *) "shmkey" },
206 { OVDBcompress, (char *) "compress" },
207 { OVDBncache, (char *) "ncache" },
208 { 0, NULL }
209 };
210
211 #define _PATH_OVDBCONF "ovdb.conf"
212
213 /*********** readserver functions ***********/
214
215 static int clientfd = -1;
216
217 /* read client send and receive functions. */
218
219 static int
csend(const void * data,int n)220 csend(const void *data, int n)
221 {
222 ssize_t status;
223
224 if (n == 0)
225 return 0;
226 status = xwrite(clientfd, data, n);
227 if (status < 0)
228 syswarn("OVDB: rc: cant write");
229 return status;
230 }
231
crecv(void * data,int n)232 static int crecv(void *data, int n)
233 {
234 int r, p = 0;
235
236 if(n == 0)
237 return 0;
238
239 while(p < n) {
240 r = read(clientfd, (char *)data + p, n - p);
241 if(r <= 0) {
242 if(r < 0 && errno == EINTR)
243 continue;
244 syswarn("OVDB: rc: cant read");
245 clientfd = -1;
246 exit(1);
247 }
248 p+= r;
249 }
250 return p;
251 }
252
253 /* Attempt to connect to the readserver. If anything fails, we
254 return -1 so that ovdb_open can open the database directly. */
255
client_connect(void)256 static int client_connect(void)
257 {
258 ssize_t r;
259 size_t p = 0;
260 char *path;
261 #ifdef HAVE_UNIX_DOMAIN_SOCKETS
262 struct sockaddr_un sa;
263 #else
264 struct sockaddr_in sa;
265 #endif
266 char banner[sizeof(OVDB_SERVER_BANNER)];
267 fd_set fds;
268 struct timeval timeout;
269
270 #ifdef HAVE_UNIX_DOMAIN_SOCKETS
271 clientfd = socket(AF_UNIX, SOCK_STREAM, 0);
272 #else
273 clientfd = socket(AF_INET, SOCK_STREAM, 0);
274 #endif
275 if(clientfd < 0) {
276 syswarn("OVDB: socket");
277 return -1;
278 }
279
280 memset(&sa, 0, sizeof sa);
281 #ifdef HAVE_UNIX_DOMAIN_SOCKETS
282 sa.sun_family = AF_UNIX;
283 path = concatpath(innconf->pathrun, OVDB_SERVER_SOCKET);
284 strlcpy(sa.sun_path, path, sizeof(sa.sun_path));
285 free(path);
286 r = connect(clientfd, (struct sockaddr *) &sa, SUN_LEN(&sa));
287 #else
288 sa.sin_family = AF_INET;
289 sa.sin_port = 0;
290 sa.sin_addr.s_addr = htonl(0x7f000001UL);
291 bind(clientfd, (struct sockaddr *) &sa, sizeof sa);
292 sa.sin_port = htons(OVDB_SERVER_PORT);
293 r = connect(clientfd, (struct sockaddr *) &sa, sizeof sa);
294 #endif
295 if(r != 0) {
296 syswarn("OVDB: rc: cant connect to server");
297 close(clientfd);
298 clientfd = -1;
299 return -1;
300 }
301
302 while(p < sizeof(OVDB_SERVER_BANNER)) {
303 FD_ZERO(&fds);
304 FD_SET(clientfd, &fds);
305 timeout.tv_sec = 30;
306 timeout.tv_usec = 0;
307
308 r = select(clientfd+1, &fds, NULL, NULL, &timeout);
309
310 if(r < 0) {
311 if(errno == EINTR)
312 continue;
313 syswarn("OVDB: select");
314 close(clientfd);
315 clientfd = -1;
316 return -1;
317 }
318 if(r == 0) {
319 warn("OVDB: rc: timeout waiting for server");
320 close(clientfd);
321 clientfd = -1;
322 return -1;
323 }
324
325 r = read(clientfd, banner + p, sizeof(OVDB_SERVER_BANNER) - p);
326 if(r <= 0) {
327 if(r < 0 && errno == EINTR)
328 continue;
329 syswarn("OVDB: rc: cant read");
330 close(clientfd);
331 clientfd = -1;
332 return -1;
333 }
334 p+= r;
335 }
336
337 if(memcmp(banner, OVDB_SERVER_BANNER, sizeof(OVDB_SERVER_BANNER))) {
338 warn("OVDB: rc: unknown reply from server");
339 close(clientfd);
340 clientfd = -1;
341 return -1;
342 }
343 return 0;
344 }
345
346 static void
client_disconnect(void)347 client_disconnect(void)
348 {
349 struct rs_cmd rs;
350
351 if (clientfd != -1) {
352 rs.what = CMD_QUIT;
353 csend(&rs, sizeof(rs));
354 }
355 clientfd = -1;
356 }
357
358
359 /*********** internal functions ***********/
360
conf_bool_val(char * str,bool * value)361 static bool conf_bool_val(char *str, bool *value)
362 {
363 if(strcasecmp(str, "on") == 0
364 || strcasecmp(str, "true") == 0
365 || strcasecmp(str, "yes") == 0) {
366 *value = true;
367 return true;
368 }
369 if(strcasecmp(str, "off") == 0
370 || strcasecmp(str, "false") == 0
371 || strcasecmp(str, "no") == 0) {
372 *value = false;
373 return true;
374 }
375 return false;
376 }
377
conf_long_val(char * str,long * value)378 static bool conf_long_val(char *str, long *value)
379 {
380 long v;
381
382 errno = 0;
383 v = strtol(str, NULL, 10);
384 if(v == 0 && errno != 0) {
385 return false;
386 }
387 *value = v;
388 return true;
389 }
390
read_ovdb_conf(void)391 void read_ovdb_conf(void)
392 {
393 static int confread = 0;
394 int done = 0;
395 char *path;
396 CONFFILE *f;
397 CONFTOKEN *tok;
398 bool b;
399 long l;
400
401 if(confread)
402 return;
403
404 /* defaults */
405 ovdb_conf.home = innconf->pathoverview;
406 ovdb_conf.txn_nosync = 1;
407 ovdb_conf.numdbfiles = 32;
408 ovdb_conf.pagesize = 8192;
409 ovdb_conf.cachesize = 8000 * 1024;
410 ovdb_conf.ncache = 1;
411 ovdb_conf.minkey = 0;
412 ovdb_conf.maxlocks = 4000;
413 ovdb_conf.nocompact = 1;
414 ovdb_conf.readserver = 1;
415 ovdb_conf.numrsprocs = 5;
416 ovdb_conf.maxrsconn = 0;
417 ovdb_conf.useshm = 0;
418 ovdb_conf.shmkey = 6400;
419 ovdb_conf.compress = 0;
420
421 path = concatpath(innconf->pathetc, _PATH_OVDBCONF);
422 f = CONFfopen(path);
423 free(path);
424
425 if(f) {
426 while(!done && (tok = CONFgettoken(toks, f))) {
427 switch(tok->type) {
428 case OVDBtxn_nosync:
429 tok = CONFgettoken(0, f);
430 if(!tok) {
431 done = 1;
432 continue;
433 }
434 if(conf_bool_val(tok->name, &b)) {
435 ovdb_conf.txn_nosync = b;
436 }
437 break;
438 case OVDBnumdbfiles:
439 tok = CONFgettoken(0, f);
440 if(!tok) {
441 done = 1;
442 continue;
443 }
444 if(conf_long_val(tok->name, &l) && l > 0) {
445 ovdb_conf.numdbfiles = l;
446 }
447 break;
448 case OVDBpagesize:
449 tok = CONFgettoken(0, f);
450 if(!tok) {
451 done = 1;
452 continue;
453 }
454 if(conf_long_val(tok->name, &l) && l > 0) {
455 ovdb_conf.pagesize = l;
456 }
457 break;
458 case OVDBcachesize:
459 tok = CONFgettoken(0, f);
460 if(!tok) {
461 done = 1;
462 continue;
463 }
464 if(conf_long_val(tok->name, &l) && l > 0) {
465 ovdb_conf.cachesize = l * 1024;
466 }
467 break;
468 case OVDBncache:
469 tok = CONFgettoken(0, f);
470 if(!tok) {
471 done = 1;
472 continue;
473 }
474 if(conf_long_val(tok->name, &l) && l > 0) {
475 ovdb_conf.ncache = l;
476 }
477 break;
478 case OVDBminkey:
479 tok = CONFgettoken(0, f);
480 if(!tok) {
481 done = 1;
482 continue;
483 }
484 if(conf_long_val(tok->name, &l) && l > 1) {
485 ovdb_conf.minkey = l;
486 }
487 break;
488 case OVDBmaxlocks:
489 tok = CONFgettoken(0, f);
490 if(!tok) {
491 done = 1;
492 continue;
493 }
494 if(conf_long_val(tok->name, &l) && l > 0) {
495 ovdb_conf.maxlocks = l;
496 }
497 break;
498 case OVDBnocompact:
499 tok = CONFgettoken(0, f);
500 if(!tok) {
501 done = 1;
502 continue;
503 }
504 if(conf_long_val(tok->name, &l) && l >= 0) {
505 ovdb_conf.nocompact = l;
506 }
507 break;
508 case OVDBreadserver:
509 tok = CONFgettoken(0, f);
510 if(!tok) {
511 done = 1;
512 continue;
513 }
514 if(conf_bool_val(tok->name, &b)) {
515 ovdb_conf.readserver = b;
516 }
517 break;
518 case OVDBnumrsprocs:
519 tok = CONFgettoken(0, f);
520 if(!tok) {
521 done = 1;
522 continue;
523 }
524 if(conf_long_val(tok->name, &l) && l > 0) {
525 ovdb_conf.numrsprocs = l;
526 }
527 break;
528 case OVDBmaxrsconn:
529 tok = CONFgettoken(0, f);
530 if(!tok) {
531 done = 1;
532 continue;
533 }
534 if(conf_long_val(tok->name, &l) && l >= 0) {
535 ovdb_conf.maxrsconn = l;
536 }
537 break;
538 case OVDBuseshm:
539 tok = CONFgettoken(0, f);
540 if(!tok) {
541 done = 1;
542 continue;
543 }
544 if(conf_bool_val(tok->name, &b)) {
545 ovdb_conf.useshm = b;
546 }
547 break;
548 case OVDBshmkey:
549 tok = CONFgettoken(0, f);
550 if(!tok) {
551 done = 1;
552 continue;
553 }
554 if(conf_long_val(tok->name, &l) && l >= 0) {
555 ovdb_conf.shmkey = l;
556 }
557 break;
558 case OVDBcompress:
559 #ifdef HAVE_ZLIB
560 tok = CONFgettoken(0, f);
561 if(!tok) {
562 done = 1;
563 continue;
564 }
565 if(conf_bool_val(tok->name, &b)) {
566 ovdb_conf.compress = b;
567 }
568 #endif
569 break;
570 }
571 }
572 CONFfclose(f);
573 }
574
575 /* If user did not specify minkey, choose one based on pagesize */
576 if(ovdb_conf.minkey == 0) {
577 if(ovdb_conf.compress) {
578 ovdb_conf.minkey = ovdb_conf.pagesize / 1500;
579 } else {
580 ovdb_conf.minkey = ovdb_conf.pagesize / 2600;
581 }
582 if(ovdb_conf.minkey < 2)
583 ovdb_conf.minkey = 2;
584 }
585
586 confread = 1;
587 }
588
589 /* Function that db will use to report errors */
OVDBerror(const DB_ENV * dbenv UNUSED,const char * db_errpfx UNUSED,const char * buffer)590 static void OVDBerror(const DB_ENV *dbenv UNUSED, const char *db_errpfx UNUSED, const char *buffer)
591 {
592 warn("OVDB: %s", buffer);
593 }
594
595 static u_int32_t _db_flags = 0;
596
open_db_file(int which)597 static int open_db_file(int which)
598 {
599 int ret;
600 char name[16];
601 DB_TXN *tid;
602
603 if(dbs[which] != NULL)
604 return 0;
605
606 snprintf(name, sizeof(name), "ov%05d", which);
607
608 ret = db_create(&(dbs[which]), OVDBenv, 0);
609 if (ret != 0)
610 return ret;
611
612 if(ovdb_conf.minkey > 0)
613 (dbs[which])->set_bt_minkey(dbs[which], ovdb_conf.minkey);
614 if(ovdb_conf.pagesize > 0)
615 (dbs[which])->set_pagesize(dbs[which], ovdb_conf.pagesize);
616
617 TXN_START_NORETRY(t_open_db_file, tid);
618 ret = (dbs[which])->open(dbs[which], tid, name, NULL, DB_BTREE, _db_flags,
619 0666);
620 if (ret == 0)
621 TXN_COMMIT(t_open_db_file, tid);
622 if (ret != 0) {
623 (dbs[which])->close(dbs[which], 0);
624 dbs[which] = NULL;
625 return ret;
626 }
627 return 0;
628 }
629
close_db_file(int which)630 static void close_db_file(int which)
631 {
632 if(which == -1 || dbs[which] == NULL)
633 return;
634
635 dbs[which]->close(dbs[which], 0);
636 dbs[which] = NULL;
637 }
638
which_db(const char * group)639 static int which_db(const char *group)
640 {
641 HASH grouphash;
642 unsigned int i;
643
644 grouphash = Hash(group, strlen(group));
645 memcpy(&i, &grouphash, sizeof(i));
646 return i % ovdb_conf.numdbfiles;
647 }
648
get_db_bynum(int which)649 static DB *get_db_bynum(int which)
650 {
651 int ret;
652 if(which >= ovdb_conf.numdbfiles)
653 return NULL;
654 if(oneatatime) {
655 if(which != current_db && current_db != -1)
656 close_db_file(current_db);
657
658 ret = open_db_file(which);
659 if (ret != 0)
660 warn("OVDB: open_db_file failed: %s", db_strerror(ret));
661
662 current_db = which;
663 }
664 return(dbs[which]);
665 }
666
667
668 int
ovdb_getgroupinfo(const char * group,struct groupinfo * gi,int ignoredeleted,DB_TXN * tid,int getflags)669 ovdb_getgroupinfo(const char *group, struct groupinfo *gi, int ignoredeleted,
670 DB_TXN *tid, int getflags)
671 {
672 int ret;
673 DBT key, val;
674
675 if(group == NULL) /* just in case */
676 return DB_NOTFOUND;
677
678 memset(&key, 0, sizeof key);
679 memset(&val, 0, sizeof val);
680
681 key.data = (char *) group;
682 key.size = strlen(group);
683 val.data = gi;
684 val.ulen = sizeof(struct groupinfo);
685 val.flags = DB_DBT_USERMEM;
686
687 ret = groupinfo->get(groupinfo, tid, &key, &val, getflags);
688 if (ret != 0)
689 return ret;
690
691 if(val.size != sizeof(struct groupinfo)) {
692 warn("OVDB: wrong size for %s groupinfo (%u)", group, val.size);
693 return DB_NOTFOUND;
694 }
695
696 if(ignoredeleted && (gi->status & GROUPINFO_DELETED))
697 return DB_NOTFOUND;
698
699 return 0;
700 }
701
702 #define GROUPID_MAX_FREELIST 10240
703 #define GROUPID_MIN_FREELIST 100
704
705 /* allocate a new group ID and return in gno */
706 /* must be used in a transaction */
groupid_new(group_id_t * gno,DB_TXN * tid)707 static int groupid_new(group_id_t *gno, DB_TXN *tid)
708 {
709 DBT key, val;
710 int ret, n;
711 group_id_t newgno, *freelist, one;
712
713 memset(&key, 0, sizeof key);
714 memset(&val, 0, sizeof val);
715
716 key.data = (char *) "!groupid_freelist";
717 key.size = sizeof("!groupid_freelist");
718
719 ret = groupinfo->get(groupinfo, tid, &key, &val, DB_RMW);
720 if (ret != 0) {
721 if(ret == DB_NOTFOUND) {
722 val.size = sizeof(group_id_t);
723 val.data = &one;
724 one = 1;
725 } else {
726 return ret;
727 }
728 }
729
730 if(val.size % sizeof(group_id_t)) {
731 warn("OVDB: invalid size (%d) for !groupid_freelist", val.size);
732 return EINVAL;
733 }
734
735 n = val.size / sizeof(group_id_t);
736 freelist = xmalloc(n * sizeof(group_id_t));
737 memcpy(freelist, val.data, val.size);
738 if(n <= GROUPID_MIN_FREELIST ) {
739 newgno = freelist[n-1];
740 (freelist[n-1])++;
741 val.data = freelist;
742 } else {
743 newgno = freelist[0];
744 val.data = &(freelist[1]);
745 val.size -= sizeof(group_id_t);
746 }
747
748 ret = groupinfo->put(groupinfo, tid, &key, &val, 0);
749 if (ret != 0) {
750 free(freelist);
751 return ret;
752 }
753
754 free(freelist);
755 *gno = newgno;
756 return 0;
757 }
758
759 /* mark given group ID as "unused" */
760 /* must be used in a transaction */
groupid_free(group_id_t gno,DB_TXN * tid)761 static int groupid_free(group_id_t gno, DB_TXN *tid)
762 {
763 DBT key, val;
764 int ret, n, i;
765 group_id_t *freelist;
766
767 memset(&key, 0, sizeof key);
768 memset(&val, 0, sizeof val);
769
770 key.data = (char *) "!groupid_freelist";
771 key.size = sizeof("!groupid_freelist");
772
773 ret = groupinfo->get(groupinfo, tid, &key, &val, DB_RMW);
774 if (ret != 0) {
775 return ret;
776 }
777
778 if(val.size % sizeof(group_id_t)) {
779 warn("OVDB: invalid size (%d) for !groupid_freelist", val.size);
780 return EINVAL;
781 }
782
783 n = val.size / sizeof(group_id_t);
784 if(n > GROUPID_MAX_FREELIST)
785 return 0;
786 freelist = xmalloc((n + 1) * sizeof(group_id_t));
787 memcpy(freelist, val.data, val.size);
788
789 if(gno >= freelist[n-1]) { /* shouldn't happen */
790 free(freelist);
791 return 0;
792 }
793 for(i = 0; i < n-1; i++) {
794 if(gno == freelist[i]) { /* already on freelist */
795 free(freelist);
796 return 0;
797 }
798 }
799
800 freelist[n] = freelist[n-1];
801 freelist[n-1] = gno;
802 val.data = freelist;
803 val.size += sizeof(group_id_t);
804
805 ret = groupinfo->put(groupinfo, tid, &key, &val, 0);
806
807 free(freelist);
808 return ret;
809 }
810
811 /* Must be called outside of a transaction because it makes its own
812 transactions */
delete_all_records(int whichdb,group_id_t gno)813 static int delete_all_records(int whichdb, group_id_t gno)
814 {
815 DB *db;
816 DBC *dbcursor;
817 DBT key, val;
818 struct datakey dk;
819 int count;
820 int ret = 0;
821 DB_TXN *tid;
822
823 memset(&key, 0, sizeof key);
824 memset(&val, 0, sizeof val);
825 memset(&dk, 0, sizeof dk);
826
827 db = get_db_bynum(whichdb);
828 if(db == NULL)
829 return DB_NOTFOUND;
830
831 dk.groupnum = gno;
832 dk.artnum = 0;
833
834 while(1) {
835 TXN_START(t_del, tid);
836
837 /* get a cursor to traverse the ov records and delete them */
838 ret = db->cursor(db, tid, &dbcursor, 0);
839 switch(ret)
840 {
841 case 0:
842 break;
843 case TRYAGAIN:
844 TXN_RETRY(t_del, tid);
845 default:
846 TXN_ABORT(t_del, tid);
847 warn("OVDB: delete_all_records: db->cursor: %s", db_strerror(ret));
848 return ret;
849 }
850
851 key.data = &dk;
852 key.size = sizeof dk;
853 val.flags = DB_DBT_PARTIAL;
854
855 for(count = 0; count < DELETE_TXN_SIZE; count++) {
856 ret = dbcursor->c_get(dbcursor, &key, &val,
857 count ? DB_NEXT : DB_SET_RANGE);
858 switch (ret)
859 {
860 case 0:
861 break;
862 case DB_NOTFOUND:
863 dbcursor->c_close(dbcursor);
864 TXN_COMMIT(t_del, tid);
865 return 0;
866 case TRYAGAIN:
867 dbcursor->c_close(dbcursor);
868 TXN_RETRY(t_del, tid);
869 default:
870 warn("OVDB: delete_all_records: DBcursor->c_get: %s",
871 db_strerror(ret));
872 dbcursor->c_close(dbcursor);
873 TXN_ABORT(t_del, tid);
874 return ret;
875 }
876
877 if(key.size == sizeof dk
878 && memcmp(key.data, &gno, sizeof gno)) {
879 break;
880 }
881
882 ret = dbcursor->c_del(dbcursor, 0);
883 switch (ret) {
884 case 0:
885 case DB_KEYEMPTY:
886 break;
887 case TRYAGAIN:
888 dbcursor->c_close(dbcursor);
889 TXN_RETRY(t_del, tid);
890 default:
891 warn("OVDB: delete_all_records: DBcursor->c_del: %s",
892 db_strerror(ret));
893 dbcursor->c_close(dbcursor);
894 TXN_ABORT(t_del, tid);
895 return ret;
896 }
897 }
898 dbcursor->c_close(dbcursor);
899 TXN_COMMIT(t_del, tid);
900 if(count < DELETE_TXN_SIZE) {
901 break;
902 }
903 }
904 return 0;
905 }
906
907 /* Make a temporary groupinfo key using the given db number and group ID.
908 Must be called in a transaction */
909 static int
mk_temp_groupinfo(int whichdb,group_id_t gno,DB_TXN * tid)910 mk_temp_groupinfo(int whichdb, group_id_t gno, DB_TXN *tid)
911 {
912 char keystr[1 + sizeof gno];
913 DBT key, val;
914 struct groupinfo gi;
915 int ret;
916
917 memset(&key, 0, sizeof key);
918 memset(&val, 0, sizeof val);
919 memset(&gi, 0, sizeof gi);
920
921 keystr[0] = 0;
922 memcpy(keystr + 1, &gno, sizeof gno);
923
924 gi.current_db = whichdb;
925 gi.current_gid = gno;
926 gi.status = GROUPINFO_DELETED;
927
928 key.data = keystr;
929 key.size = sizeof keystr;
930 val.data = &gi;
931 val.size = sizeof gi;
932
933 ret = groupinfo->put(groupinfo, tid, &key, &val, 0);
934 switch (ret) {
935 case 0:
936 break;
937 default:
938 warn("OVDB: mk_temp_groupinfo: groupinfo->put: %s", db_strerror(ret));
939 /* fallthrough */
940 case TRYAGAIN:
941 return ret;
942 }
943
944 return 0;
945 }
946
947 /* Delete a temporary groupinfo key created by mk_temp_groupid, then
948 frees the group id.
949 Must NOT be called within a transaction. */
950 static int
rm_temp_groupinfo(group_id_t gno)951 rm_temp_groupinfo(group_id_t gno)
952 {
953 char keystr[1 + sizeof gno];
954 DB_TXN *tid;
955 DBT key;
956 int ret = 0;
957
958 memset(&key, 0, sizeof key);
959
960 keystr[0] = 0;
961 memcpy(keystr + 1, &gno, sizeof gno);
962
963 key.data = keystr;
964 key.size = sizeof keystr;
965
966 TXN_START(t_tmp, tid);
967
968 ret = groupinfo->del(groupinfo, tid, &key, 0);
969 switch(ret) {
970 case 0:
971 case DB_NOTFOUND:
972 break;
973 case TRYAGAIN:
974 TXN_RETRY(t_tmp, tid);
975 default:
976 TXN_ABORT(t_tmp, tid);
977 warn("OVDB: rm_temp_groupinfo: groupinfo->del: %s", db_strerror(ret));
978 return ret;
979 }
980
981 switch(groupid_free(gno, tid)) {
982 case 0:
983 break;
984 case TRYAGAIN:
985 TXN_RETRY(t_tmp, tid);
986 default:
987 TXN_ABORT(t_tmp, tid);
988 warn("OVDB: rm_temp_groupinfo: groupid_free: %s", db_strerror(ret));
989 return ret;
990 }
991
992 TXN_COMMIT(t_tmp, tid);
993 return 0;
994 }
995
996 /* This function deletes overview records for deleted or forgotton groups */
997 /* argument: 0 = process deleted groups 1 = process forgotton groups */
delete_old_stuff(int forgotton)998 static bool delete_old_stuff(int forgotton)
999 {
1000 DBT key, val;
1001 DBC *cursor;
1002 DB_TXN *tid;
1003 struct groupinfo gi;
1004 char **dellist = NULL;
1005 size_t *dellistsz = NULL;
1006 int listlen, listcount = 0;
1007 int i, ret = 0;
1008
1009 TXN_START(t_dellist, tid);
1010 if (dellist != NULL) {
1011 for (i = 0; i < listcount; ++i)
1012 free(dellist[i]);
1013 free(dellist);
1014 }
1015 if (dellistsz != NULL)
1016 free(dellistsz);
1017 listlen = 20;
1018 listcount = 0;
1019 dellist = xmalloc(listlen * sizeof(char *));
1020 dellistsz = xmalloc(listlen * sizeof(size_t));
1021
1022 memset(&key, 0, sizeof key);
1023 memset(&val, 0, sizeof val);
1024
1025 val.data = &gi;
1026 val.ulen = val.dlen = sizeof gi;
1027 val.flags = DB_DBT_USERMEM | DB_DBT_PARTIAL;
1028
1029 /* get a cursor to traverse all of the groupinfo records */
1030 ret = groupinfo->cursor(groupinfo, tid, &cursor, 0);
1031 if (ret != 0) {
1032 warn("OVDB: delete_old_stuff: groupinfo->cursor: %s",
1033 db_strerror(ret));
1034 free(dellist);
1035 free(dellistsz);
1036 return false;
1037 }
1038
1039 while((ret = cursor->c_get(cursor, &key, &val, DB_NEXT)) == 0) {
1040 if(key.size == sizeof("!groupid_freelist") &&
1041 !strcmp("!groupid_freelist", key.data))
1042 continue;
1043 if(val.size != sizeof(struct groupinfo)) {
1044 warn("OVDB: delete_old_stuff: wrong size for groupinfo record");
1045 continue;
1046 }
1047 if((!forgotton && (gi.status & GROUPINFO_DELETED))
1048 || (forgotton && (gi.expired < eo_start))) {
1049 dellist[listcount] = xmalloc(key.size);
1050 memcpy(dellist[listcount], key.data, key.size);
1051 dellistsz[listcount] = key.size;
1052 listcount++;
1053 if(listcount >= listlen) {
1054 listlen += 20;
1055 dellist = xrealloc(dellist, listlen * sizeof(char *));
1056 dellistsz = xrealloc(dellistsz, listlen * sizeof(size_t));
1057 }
1058 }
1059 }
1060 cursor->c_close(cursor);
1061 switch (ret) {
1062 case 0:
1063 case DB_NOTFOUND:
1064 TXN_COMMIT(t_dellist, tid);
1065 break;
1066 case TRYAGAIN:
1067 TXN_RETRY(t_dellist, tid);
1068 default:
1069 TXN_ABORT(t_dellist, tid);
1070 warn("OVDB: delete_old_stuff: cursor->c_get: %s", db_strerror(ret));
1071 for (i = 0; i < listcount; ++i)
1072 free(dellist[i]);
1073 free(dellist);
1074 free(dellistsz);
1075 return false;
1076 }
1077
1078 for(i = 0; i < listcount; i++) {
1079 TXN_START(t_dos, tid);
1080
1081 /* Can't use ovdb_getgroupinfo here */
1082 key.data = dellist[i];
1083 key.size = dellistsz[i];
1084 val.data = &gi;
1085 val.ulen = sizeof(struct groupinfo);
1086 val.flags = DB_DBT_USERMEM;
1087
1088 ret = groupinfo->get(groupinfo, tid, &key, &val, 0);
1089
1090 switch (ret)
1091 {
1092 case 0:
1093 break;
1094 case TRYAGAIN:
1095 TXN_RETRY(t_dos, tid);
1096 case DB_NOTFOUND:
1097 TXN_ABORT(t_dos, tid);
1098 continue;
1099 default:
1100 warn("OVDB: delete_old_stuff: groupinfo->get: %s",
1101 db_strerror(ret));
1102 TXN_ABORT(t_dos, tid);
1103 continue;
1104 }
1105 if(val.size != sizeof(struct groupinfo)) {
1106 TXN_ABORT(t_dos, tid);
1107 continue;
1108 }
1109
1110 /* This if() is true if this ISN'T a key created by mk_temp_groupinfo */
1111 if(*((char *)key.data) != 0 || key.size != 1 + sizeof(group_id_t)) {
1112
1113 switch(mk_temp_groupinfo(gi.current_db, gi.current_gid, tid)) {
1114 case 0:
1115 break;
1116 case TRYAGAIN:
1117 TXN_RETRY(t_dos, tid);
1118 default:
1119 TXN_ABORT(t_dos, tid);
1120 continue;
1121 }
1122 if(gi.status & GROUPINFO_MOVING) {
1123 switch(mk_temp_groupinfo(gi.new_db, gi.new_gid, tid)) {
1124 case 0:
1125 break;
1126 case TRYAGAIN:
1127 TXN_RETRY(t_dos, tid);
1128 default:
1129 TXN_ABORT(t_dos, tid);
1130 continue;
1131 }
1132 }
1133
1134 /* delete the corresponding groupaliases record (if exists) */
1135 switch(groupaliases->del(groupaliases, tid, &key, 0)) {
1136 case 0:
1137 case DB_NOTFOUND:
1138 break;
1139 case TRYAGAIN:
1140 TXN_RETRY(t_dos, tid);
1141 default:
1142 TXN_ABORT(t_dos, tid);
1143 continue;
1144 }
1145
1146 switch(groupinfo->del(groupinfo, tid, &key, 0)) {
1147 case 0:
1148 case DB_NOTFOUND:
1149 break;
1150 case TRYAGAIN:
1151 TXN_RETRY(t_dos, tid);
1152 default:
1153 TXN_ABORT(t_dos, tid);
1154 continue;
1155 }
1156 }
1157
1158 TXN_COMMIT(t_dos, tid);
1159
1160 if(delete_all_records(gi.current_db, gi.current_gid) == 0) {
1161 rm_temp_groupinfo(gi.current_gid);
1162 }
1163 if(gi.status & GROUPINFO_MOVING) {
1164 if(delete_all_records(gi.new_db, gi.new_gid) == 0) {
1165 rm_temp_groupinfo(gi.new_gid);
1166 }
1167 }
1168 }
1169
1170 for(i = 0; i < listcount; i++)
1171 free(dellist[i]);
1172 free(dellist);
1173 free(dellistsz);
1174 return true;
1175 }
1176
count_records(struct groupinfo * gi)1177 static int count_records(struct groupinfo *gi)
1178 {
1179 int ret;
1180 DB *db;
1181 DBC *cursor;
1182 DBT key, val;
1183 struct datakey dk;
1184 u_int32_t artnum, newlow = 0;
1185
1186 memset(&key, 0, sizeof key);
1187 memset(&val, 0, sizeof val);
1188 memset(&dk, 0, sizeof dk);
1189
1190 db = get_db_bynum(gi->current_db);
1191 if(db == NULL)
1192 return DB_NOTFOUND;
1193
1194 dk.groupnum = gi->current_gid;
1195 dk.artnum = 0;
1196 key.data = &dk;
1197 key.size = key.ulen = sizeof dk;
1198 key.flags = DB_DBT_USERMEM;
1199 val.flags = DB_DBT_PARTIAL;
1200
1201 gi->count = 0;
1202
1203 ret = db->cursor(db, NULL, &cursor, 0);
1204 if (ret)
1205 return ret;
1206
1207 ret = cursor->c_get(cursor, &key, &val, DB_SET_RANGE);
1208 switch (ret)
1209 {
1210 case 0:
1211 case DB_NOTFOUND:
1212 break;
1213 default:
1214 cursor->c_close(cursor);
1215 return ret;
1216 }
1217 while(ret == 0 && key.size == sizeof(dk) && dk.groupnum == gi->current_gid) {
1218 artnum = ntohl(dk.artnum);
1219 if(newlow == 0 || newlow > artnum)
1220 newlow = artnum;
1221 if(artnum > gi->high)
1222 gi->high = artnum;
1223 gi->count++;
1224
1225 ret = cursor->c_get(cursor, &key, &val, DB_NEXT);
1226 }
1227 cursor->c_close(cursor);
1228 if(gi->count == 0)
1229 gi->low = gi->high + 1;
1230 else
1231 gi->low = newlow;
1232
1233 if(ret == DB_NOTFOUND)
1234 return 0;
1235 return ret;
1236 }
1237
1238
1239 /*
1240 * Locking: OVopen() calls ovdb_getlock(OVDB_LOCK_NORMAL). This
1241 * aquires a read (shared) lock on the lockfile. Multiple processes
1242 * can have this file locked at the same time. That way, if there
1243 * are any processes that are using the database, the lock file will
1244 * have one or more shared locks on it.
1245 *
1246 * ovdb_init, when starting, calls ovdb_getlock(OVDB_LOCK_EXCLUSIVE).
1247 * This tries to get a write (exclusive) lock, which will fail if
1248 * anyone has a shared lock. This way, ovdb_init can tell if there
1249 * are any processes using the database. If not, and the excl. lock
1250 * succeeds, ovdb_init is free to do DB_RUNRECOVER.
1251 *
1252 * ovdb_getlock() in the "normal" lock mode calls ovdb_check_monitor,
1253 * which looks for the OVDB_MONITOR_PIDFILE. If said file does not
1254 * exist, or the PID in it does not exist, it will fail. This will
1255 * prevent OVopen() from opening the database if ovdb_monitor is not running.
1256 *
1257 * The OVDB_LOCK_ADMIN mode is used by ovdb_monitor to get a shared lock
1258 * without testing the pidfile.
1259 */
1260 static int lockfd = -1;
ovdb_getlock(int mode)1261 bool ovdb_getlock(int mode)
1262 {
1263 if(lockfd == -1) {
1264 char *lockfn = concatpath(innconf->pathrun, OVDB_LOCKFN);
1265 lockfd = open(lockfn,
1266 mode == OVDB_LOCK_NORMAL ? O_RDWR : O_CREAT|O_RDWR, 0660);
1267 if(lockfd == -1) {
1268 free(lockfn);
1269 if(errno == ENOENT)
1270 warn("OVDB: can not open database unless ovdb_monitor is"
1271 " running");
1272 return false;
1273 }
1274 fdflag_close_exec(lockfd, true);
1275 free(lockfn);
1276 } else
1277 return true;
1278
1279 if(mode == OVDB_LOCK_NORMAL) {
1280 if(!ovdb_check_pidfile(OVDB_MONITOR_PIDFILE)) {
1281 warn("OVDB: can not open database unless ovdb_monitor is"
1282 " running");
1283 return false;
1284 }
1285 }
1286 if(mode == OVDB_LOCK_EXCLUSIVE) {
1287 if(!inn_lock_file(lockfd, INN_LOCK_WRITE, false)) {
1288 close(lockfd);
1289 lockfd = -1;
1290 return false;
1291 }
1292 return true;
1293 } else {
1294 if(!inn_lock_file(lockfd, INN_LOCK_READ, false)) {
1295 close(lockfd);
1296 lockfd = -1;
1297 return false;
1298 }
1299 return true;
1300 }
1301 }
1302
ovdb_releaselock(void)1303 bool ovdb_releaselock(void)
1304 {
1305 bool r;
1306 if(lockfd == -1)
1307 return true;
1308 r = inn_lock_file(lockfd, INN_LOCK_UNLOCK, false);
1309 close(lockfd);
1310 lockfd = -1;
1311 return r;
1312 }
1313
ovdb_check_pidfile(const char * file)1314 bool ovdb_check_pidfile(const char *file)
1315 {
1316 int f, pid;
1317 char buf[SMBUF];
1318 char *pidfn = concatpath(innconf->pathrun, file);
1319
1320 f = open(pidfn, O_RDONLY);
1321 if(f == -1) {
1322 if(errno != ENOENT)
1323 syswarn("OVDB: can't open %s", pidfn);
1324 free(pidfn);
1325 return false;
1326 }
1327 memset(buf, 0, SMBUF);
1328 if(read(f, buf, SMBUF-1) < 0) {
1329 syswarn("OVDB: can't read from %s", pidfn);
1330 free(pidfn);
1331 close(f);
1332 return false;
1333 }
1334 close(f);
1335 free(pidfn);
1336 pid = atoi(buf);
1337 if(pid <= 1) {
1338 return false;
1339 }
1340 if(kill(pid, 0) < 0 && errno == ESRCH) {
1341 return false;
1342 }
1343 return true;
1344 }
1345
1346 /* Make sure the effective uid is that of the runasuser user. */
ovdb_check_user(void)1347 bool ovdb_check_user(void)
1348 {
1349 static int result = -1;
1350
1351 if(result == -1) {
1352 int rv;
1353 uid_t uid;
1354
1355 rv = get_news_uid_gid(&uid, false, false);
1356
1357 if (rv != 0) {
1358 syswarn("OVDB: can't resolve runasuser user to a UID");
1359 return false;
1360 }
1361 result = (uid == geteuid());
1362 }
1363 return result;
1364 }
1365
check_version(void)1366 static int check_version(void)
1367 {
1368 int ret;
1369 DB *vdb;
1370 DBT key, val;
1371 u_int32_t dv;
1372
1373 /* open version db */
1374 ret = db_create(&vdb, OVDBenv, 0);
1375 if (ret != 0) {
1376 warn("OVDB: open: db_create: %s", db_strerror(ret));
1377 return ret;
1378 }
1379 ret = vdb->open(vdb, NULL, "version", NULL, DB_BTREE, _db_flags, 0666);
1380 if (ret != 0) {
1381 vdb->close(vdb, 0);
1382 warn("OVDB: open: version->open: %s", db_strerror(ret));
1383 return ret;
1384 }
1385 memset(&key, 0, sizeof key);
1386 memset(&val, 0, sizeof val);
1387 key.data = (char *) "dataversion";
1388 key.size = sizeof("dataversion");
1389 ret = vdb->get(vdb, NULL, &key, &val, 0);
1390 if (ret != 0) {
1391 if(ret != DB_NOTFOUND) {
1392 warn("OVDB: open: can't retrieve version: %s", db_strerror(ret));
1393 vdb->close(vdb, 0);
1394 return ret;
1395 }
1396 }
1397 if(ret == DB_NOTFOUND || val.size != sizeof dv) {
1398 dv = ovdb_conf.compress ? DATA_VERSION_COMPRESS : DATA_VERSION;
1399 if(!(OVDBmode & OV_WRITE)) {
1400 vdb->close(vdb, 0);
1401 return EACCES;
1402 }
1403 val.data = &dv;
1404 val.size = sizeof dv;
1405 ret = vdb->put(vdb, NULL, &key, &val, 0);
1406 if (ret != 0) {
1407 warn("OVDB: open: can't store version: %s", db_strerror(ret));
1408 vdb->close(vdb, 0);
1409 return ret;
1410 }
1411 } else
1412 memcpy(&dv, val.data, sizeof dv);
1413
1414 if(dv > DATA_VERSION_COMPRESS) {
1415 warn("OVDB: can't open database: unknown version %d", dv);
1416 vdb->close(vdb, 0);
1417 return EINVAL;
1418 }
1419 if(dv < DATA_VERSION) {
1420 warn("OVDB: database is an old version, please run ovdb_init");
1421 vdb->close(vdb, 0);
1422 return EINVAL;
1423 }
1424 #ifndef HAVE_ZLIB
1425 if(dv == DATA_VERSION_COMPRESS) {
1426 warn("OVDB: database is compressed but INN was not built with zlib");
1427 vdb->close(vdb, 0);
1428 return EINVAL;
1429 }
1430 #endif
1431 if(ovdb_conf.compress && dv == DATA_VERSION && (OVDBmode & OV_WRITE)) {
1432 /* "Upgrade" the database to indicate there may be compressed
1433 records */
1434 dv = DATA_VERSION_COMPRESS;
1435 val.data = &dv;
1436 val.size = sizeof dv;
1437
1438 ret = vdb->put(vdb, NULL, &key, &val, 0);
1439 if (ret != 0) {
1440 warn("OVDB: open: can't store version: %s", db_strerror(ret));
1441 vdb->close(vdb, 0);
1442 return ret;
1443 }
1444 }
1445 ovdb_data_ver = dv;
1446
1447 /* The version db also stores some config values, which will override the
1448 corresponding ovdb.conf setting. */
1449
1450 key.data = (char *) "numdbfiles";
1451 key.size = sizeof("numdbfiles");
1452 ret = vdb->get(vdb, NULL, &key, &val, 0);
1453 if (ret != 0) {
1454 if(ret != DB_NOTFOUND) {
1455 warn("OVDB: open: can't retrieve numdbfiles: %s",
1456 db_strerror(ret));
1457 vdb->close(vdb, 0);
1458 return ret;
1459 }
1460 }
1461 if(ret == DB_NOTFOUND || val.size != sizeof(ovdb_conf.numdbfiles)) {
1462 if(!(OVDBmode & OV_WRITE)) {
1463 vdb->close(vdb, 0);
1464 return EACCES;
1465 }
1466 val.data = &(ovdb_conf.numdbfiles);
1467 val.size = sizeof(ovdb_conf.numdbfiles);
1468 ret = vdb->put(vdb, NULL, &key, &val, 0);
1469 if (ret != 0) {
1470 warn("OVDB: open: can't store numdbfiles: %s", db_strerror(ret));
1471 vdb->close(vdb, 0);
1472 return ret;
1473 }
1474 } else {
1475 memcpy(&(ovdb_conf.numdbfiles), val.data, sizeof(ovdb_conf.numdbfiles));
1476 }
1477
1478 vdb->close(vdb, 0);
1479 return 0;
1480 }
1481
1482
ovdb_open_berkeleydb(int mode,int flags)1483 int ovdb_open_berkeleydb(int mode, int flags)
1484 {
1485 int ret;
1486 u_int32_t ai_flags = DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN;
1487
1488 OVDBmode = mode;
1489 read_ovdb_conf();
1490
1491 if(OVDBenv != NULL)
1492 return 0; /* already opened */
1493
1494 if(OVDBmode & OV_WRITE) {
1495 _db_flags |= DB_CREATE;
1496 ai_flags |= DB_CREATE;
1497 } else {
1498 _db_flags |= DB_RDONLY;
1499 }
1500 if(flags & OVDB_RECOVER)
1501 ai_flags |= DB_RECOVER;
1502
1503 ret = db_env_create(&OVDBenv, 0);
1504 if (ret != 0) {
1505 warn("OVDB: db_env_create: %s", db_strerror(ret));
1506 return ret;
1507 }
1508
1509 if((flags & (OVDB_UPGRADE | OVDB_RECOVER)) == (OVDB_UPGRADE | OVDB_RECOVER))
1510 ai_flags |= DB_PRIVATE;
1511 if(!(ai_flags & DB_PRIVATE)) {
1512 if(ovdb_conf.useshm)
1513 ai_flags |= DB_SYSTEM_MEM;
1514 OVDBenv->set_shm_key(OVDBenv, ovdb_conf.shmkey);
1515 }
1516
1517 OVDBenv->set_errcall(OVDBenv, OVDBerror);
1518 OVDBenv->set_cachesize(OVDBenv, 0, ovdb_conf.cachesize, ovdb_conf.ncache);
1519 OVDBenv->set_lk_max_locks(OVDBenv, ovdb_conf.maxlocks);
1520 OVDBenv->set_lk_max_lockers(OVDBenv, ovdb_conf.maxlocks);
1521 OVDBenv->set_lk_max_objects(OVDBenv, ovdb_conf.maxlocks);
1522
1523 if(ovdb_conf.txn_nosync)
1524 OVDBenv->set_flags(OVDBenv, DB_TXN_NOSYNC, 1);
1525
1526 if((flags & (OVDB_UPGRADE | OVDB_RECOVER)) != OVDB_UPGRADE) {
1527 ret = OVDBenv->open(OVDBenv, ovdb_conf.home, ai_flags, 0666);
1528 if (ret != 0) {
1529 OVDBenv->close(OVDBenv, 0);
1530 OVDBenv = NULL;
1531 warn("OVDB: OVDBenv->open: %s", db_strerror(ret));
1532 return ret;
1533 }
1534 }
1535
1536 return 0;
1537 }
1538
ovdb_open(int mode)1539 bool ovdb_open(int mode)
1540 {
1541 int i, ret;
1542 DB_TXN *tid;
1543
1544 if(OVDBenv != NULL || clientmode) {
1545 warn("OVDB: ovdb_open called more than once");
1546 return false;
1547 }
1548
1549 read_ovdb_conf();
1550 if(ovdb_conf.readserver && mode == OV_READ)
1551 clientmode = 1;
1552
1553 if(mode & OVDB_SERVER)
1554 clientmode = 0;
1555
1556 if(clientmode) {
1557 if(client_connect() == 0)
1558 return true;
1559 clientmode = 0;
1560 }
1561 if(!ovdb_check_user()) {
1562 warn("OVDB: must be running as runasuser user to access overview");
1563 return false;
1564 }
1565 if(!ovdb_getlock(OVDB_LOCK_NORMAL)) {
1566 syswarn("OVDB: ovdb_getlock failed");
1567 return false;
1568 }
1569
1570 if(ovdb_open_berkeleydb(mode, 0) != 0)
1571 return false;
1572
1573 if(check_version() != 0)
1574 return false;
1575
1576 if(mode & OV_WRITE || mode & OVDB_SERVER) {
1577 oneatatime = 0;
1578 } else {
1579 oneatatime = 1;
1580 }
1581
1582 dbs = xcalloc(ovdb_conf.numdbfiles, sizeof(DB *));
1583
1584 if(!oneatatime) {
1585 for(i = 0; i < ovdb_conf.numdbfiles; i++) {
1586 ret = open_db_file(i);
1587 if (ret != 0) {
1588 warn("OVDB: open_db_file failed: %s", db_strerror(ret));
1589 return false;
1590 }
1591 }
1592 }
1593
1594 ret = db_create(&groupinfo, OVDBenv, 0);
1595 if (ret != 0) {
1596 warn("OVDB: open: db_create: %s", db_strerror(ret));
1597 return false;
1598 }
1599 TXN_START_NORETRY(t_open_groupinfo, tid);
1600 ret = groupinfo->open(groupinfo, tid, "groupinfo", NULL, DB_BTREE,
1601 _db_flags, 0666);
1602 if (ret == 0)
1603 TXN_COMMIT(t_open_groupinfo, tid);
1604 if (ret != 0) {
1605 groupinfo->close(groupinfo, 0);
1606 warn("OVDB: open: groupinfo->open: %s", db_strerror(ret));
1607 return false;
1608 }
1609 ret = db_create(&groupaliases, OVDBenv, 0);
1610 if (ret != 0) {
1611 warn("OVDB: open: db_create: %s", db_strerror(ret));
1612 return false;
1613 }
1614 TXN_START_NORETRY(t_open_groupaliases, tid);
1615 ret = groupaliases->open(groupaliases, tid, "groupaliases", NULL, DB_HASH,
1616 _db_flags, 0666);
1617 if (ret == 0)
1618 TXN_COMMIT(t_open_groupaliases, tid);
1619 if (ret != 0) {
1620 groupaliases->close(groupaliases, 0);
1621 warn("OVDB: open: groupaliases->open: %s", db_strerror(ret));
1622 return false;
1623 }
1624
1625 Cutofflow = false;
1626 return true;
1627 }
1628
1629
1630 bool
ovdb_groupstats(const char * group,int * lo,int * hi,int * count,int * flag)1631 ovdb_groupstats(const char *group, int *lo, int *hi, int *count, int *flag)
1632 {
1633 int ret;
1634 struct groupinfo gi;
1635
1636 if (clientmode) {
1637 struct rs_cmd rs;
1638 struct rs_groupstats repl;
1639
1640 rs.what = CMD_GROUPSTATS;
1641 rs.grouplen = strlen(group)+1;
1642
1643 if (csend(&rs, sizeof(rs)) < 0)
1644 return false;
1645 if (csend(group, rs.grouplen) < 0)
1646 return false;
1647 crecv(&repl, sizeof(repl));
1648
1649 if(repl.status != CMD_GROUPSTATS)
1650 return false;
1651
1652 /* we don't use the alias yet, but the OV API will be extended
1653 at some point so that the alias is returned also */
1654 if(repl.aliaslen != 0) {
1655 char *buf = xmalloc(repl.aliaslen);
1656 crecv(buf, repl.aliaslen);
1657 free(buf);
1658 }
1659
1660 if(lo)
1661 *lo = repl.lo;
1662 if(hi)
1663 *hi = repl.hi;
1664 if(count)
1665 *count = repl.count;
1666 if(flag)
1667 *flag = repl.flag;
1668 return true;
1669 }
1670
1671 ret = ovdb_getgroupinfo(group, &gi, true, NULL, 0);
1672 switch (ret)
1673 {
1674 case 0:
1675 break;
1676 case DB_NOTFOUND:
1677 return false;
1678 default:
1679 warn("OVDB: ovdb_getgroupinfo failed: %s", db_strerror(ret));
1680 return false;
1681 }
1682
1683 if(lo != NULL)
1684 *lo = gi.low;
1685 if(hi != NULL)
1686 *hi = gi.high;
1687 if(count != NULL)
1688 *count = gi.count;
1689 if(flag != NULL)
1690 *flag = gi.flag;
1691 return true;
1692 }
1693
1694 bool
ovdb_groupadd(const char * group,ARTNUM lo,ARTNUM hi,char * flag)1695 ovdb_groupadd(const char *group, ARTNUM lo, ARTNUM hi, char *flag)
1696 {
1697 DBT key, val;
1698 struct groupinfo gi;
1699 DB_TXN *tid;
1700 int ret = 0;
1701 int new;
1702
1703 memset(&key, 0, sizeof key);
1704 memset(&val, 0, sizeof val);
1705
1706 TXN_START(t_groupadd, tid);
1707
1708 if(tid==NULL)
1709 return false;
1710
1711 new = 0;
1712 ret = ovdb_getgroupinfo(group, &gi, false, tid, DB_RMW);
1713 switch (ret)
1714 {
1715 case DB_NOTFOUND:
1716 new = 1;
1717 case 0:
1718 break;
1719 case TRYAGAIN:
1720 TXN_RETRY(t_groupadd, tid);
1721 default:
1722 TXN_ABORT(t_groupadd, tid);
1723 warn("OVDB: ovdb_getgroupinfo failed: %s", db_strerror(ret));
1724 return false;
1725 }
1726
1727 if(!new && (gi.status & GROUPINFO_DELETED)
1728 && !(gi.status & GROUPINFO_EXPIRING)
1729 && !(gi.status & GROUPINFO_MOVING)) {
1730 int s, c = 0;
1731 char g[MED_BUFFER];
1732
1733 strlcpy(g, group, sizeof(g));
1734 s = strlen(g) + 1;
1735 key.data = g;
1736 key.size = s + sizeof(int);
1737 do {
1738 c++;
1739 memcpy(g+s, &c, sizeof(int));
1740 ret = groupinfo->get(groupinfo, tid, &key, &val, 0);
1741 } while(ret == 0);
1742 if(ret == TRYAGAIN) {
1743 TXN_RETRY(t_groupadd, tid);
1744 }
1745 val.data = &gi;
1746 val.size = sizeof(gi);
1747 ret = groupinfo->put(groupinfo, tid, &key, &val, 0);
1748 switch (ret)
1749 {
1750 case 0:
1751 break;
1752 case TRYAGAIN:
1753 TXN_RETRY(t_groupadd, tid);
1754 default:
1755 TXN_ABORT(t_groupadd, tid);
1756 warn("OVDB: groupinfo->put: %s", db_strerror(ret));
1757 return false;
1758 }
1759 key.data = (char *) group;
1760 key.size = strlen(group);
1761 ret = groupinfo->del(groupinfo, tid, &key, 0);
1762 switch (ret)
1763 {
1764 case 0:
1765 break;
1766 case TRYAGAIN:
1767 TXN_RETRY(t_groupadd, tid);
1768 default:
1769 TXN_ABORT(t_groupadd, tid);
1770 warn("OVDB: groupinfo->del: %s", db_strerror(ret));
1771 return false;
1772 }
1773 new = 1;
1774 }
1775
1776 if(new) {
1777 ret = groupid_new(&gi.current_gid, tid);
1778 switch (ret)
1779 {
1780 case 0:
1781 break;
1782 case TRYAGAIN:
1783 TXN_RETRY(t_groupadd, tid);
1784 default:
1785 TXN_ABORT(t_groupadd, tid);
1786 warn("OVDB: groupid_new: %s", db_strerror(ret));
1787 return false;
1788 }
1789 gi.low = lo ? lo : 1;
1790 gi.high = hi;
1791 gi.count = 0;
1792 gi.flag = *flag;
1793 gi.expired = time(NULL);
1794 gi.expiregrouppid = 0;
1795 gi.current_db = gi.new_db = which_db(group);
1796 gi.new_gid = gi.current_gid;
1797 gi.status = 0;
1798 } else {
1799 gi.flag = *flag;
1800 }
1801
1802 key.data = (char *) group;
1803 key.size = strlen(group);
1804 val.data = &gi;
1805 val.size = sizeof gi;
1806
1807 ret = groupinfo->put(groupinfo, tid, &key, &val, 0);
1808 switch (ret) {
1809 case 0:
1810 break;
1811 case TRYAGAIN:
1812 TXN_RETRY(t_groupadd, tid);
1813 default:
1814 TXN_ABORT(t_groupadd, tid);
1815 warn("OVDB: groupadd: groupinfo->put: %s", db_strerror(ret));
1816 return false;
1817 }
1818
1819 if(*flag == NF_FLAG_ALIAS) {
1820 key.data = (char *) group;
1821 key.size = strlen(group);
1822 val.data = flag + 1;
1823 val.size = strcspn(flag, "\n") - 1;
1824
1825 switch(ret = groupaliases->put(groupaliases, tid, &key, &val, 0)) {
1826 case 0:
1827 break;
1828 case TRYAGAIN:
1829 TXN_RETRY(t_groupadd, tid);
1830 default:
1831 TXN_ABORT(t_groupadd, tid);
1832 warn("OVDB: groupadd: groupaliases->put: %s", db_strerror(ret));
1833 return false;
1834 }
1835 }
1836
1837 TXN_COMMIT(t_groupadd, tid);
1838 return true;
1839 }
1840
1841 bool
ovdb_groupdel(const char * group)1842 ovdb_groupdel(const char *group)
1843 {
1844 DBT key, val;
1845 struct groupinfo gi;
1846 DB_TXN *tid;
1847 int ret = 0;
1848
1849 memset(&key, 0, sizeof key);
1850 memset(&val, 0, sizeof val);
1851
1852 /* We only need to set the deleted flag in groupinfo to prevent readers
1853 from seeing this group. The actual overview records aren't deleted
1854 now, since that could take a significant amount of time (and innd
1855 is who normally calls this function). The expireover run will
1856 clean up the deleted groups. */
1857
1858 TXN_START(t_groupdel, tid);
1859
1860 if(tid==NULL)
1861 return false;
1862
1863 ret = ovdb_getgroupinfo(group, &gi, true, tid, DB_RMW);
1864 switch (ret)
1865 {
1866 case DB_NOTFOUND:
1867 return true;
1868 case 0:
1869 break;
1870 case TRYAGAIN:
1871 TXN_RETRY(t_groupdel, tid);
1872 default:
1873 warn("OVDB: ovdb_getgroupinfo failed: %s", db_strerror(ret));
1874 TXN_ABORT(t_groupdel, tid);
1875 return false;
1876 }
1877
1878 gi.status |= GROUPINFO_DELETED;
1879
1880 key.data = (char *) group;
1881 key.size = strlen(group);
1882 val.data = &gi;
1883 val.size = sizeof gi;
1884
1885 switch(ret = groupinfo->put(groupinfo, tid, &key, &val, 0)) {
1886 case 0:
1887 break;
1888 case TRYAGAIN:
1889 TXN_RETRY(t_groupdel, tid);
1890 default:
1891 TXN_ABORT(t_groupdel, tid);
1892 warn("OVDB: groupadd: groupinfo->put: %s", db_strerror(ret));
1893 return false;
1894 }
1895
1896 switch(ret = groupaliases->del(groupaliases, tid, &key, 0)) {
1897 case 0:
1898 case DB_NOTFOUND:
1899 break;
1900 case TRYAGAIN:
1901 TXN_RETRY(t_groupdel, tid);
1902 default:
1903 warn("OVDB: groupdel: groupaliases->del: %s", db_strerror(ret));
1904 TXN_ABORT(t_groupdel, tid);
1905 return false;
1906 }
1907
1908 TXN_COMMIT(t_groupdel, tid);
1909 return true;
1910 }
1911
1912 bool
ovdb_add(const char * group,ARTNUM artnum,TOKEN token,char * data,int len,time_t arrived,time_t expires)1913 ovdb_add(const char *group, ARTNUM artnum, TOKEN token, char *data, int len,
1914 time_t arrived, time_t expires)
1915 {
1916 static size_t databuflen = 0;
1917 static char *databuf;
1918 #ifdef HAVE_ZLIB
1919 uLong c_sz = 0;
1920 #else
1921 #define c_sz 0
1922 #endif
1923 DB *db;
1924 DBT key, val;
1925 DB_TXN *tid;
1926 struct groupinfo gi;
1927 struct datakey dk;
1928 int ret = 0;
1929
1930 memset(&dk, 0, sizeof dk);
1931
1932 if(databuflen == 0) {
1933 databuflen = BIG_BUFFER;
1934 databuf = xmalloc(databuflen);
1935 }
1936 #ifdef HAVE_ZLIB
1937 if(ovdb_conf.compress) {
1938 /* Allow for worst-case compression */
1939 c_sz = len + (len / 1000) + 20;
1940 }
1941 #endif
1942
1943 if(databuflen < len + sizeof(struct ovdata) + c_sz) {
1944 databuflen = len + sizeof(struct ovdata) + c_sz;
1945 databuf = xrealloc(databuf, databuflen);
1946 }
1947
1948 /* Hmm... Berkeley DB needs something like a 'struct iovec' so that we don't
1949 have to make a new buffer and copy everything in to it. */
1950
1951 ((struct ovdata *)databuf)->token = token;
1952 ((struct ovdata *)databuf)->arrived = arrived;
1953 ((struct ovdata *)databuf)->expires = expires;
1954
1955 #ifdef HAVE_ZLIB
1956 if(ovdb_conf.compress && len > COMPRESS_MIN) {
1957 uint32_t sz;
1958 c_sz -= sizeof(uint32_t);
1959 ret = compress(
1960 (Bytef *)(databuf + sizeof(struct ovdata) + sizeof(uint32_t)),
1961 &c_sz, (Bytef *)data, (uLong)len);
1962 if(ret != Z_OK) {
1963 memcpy(databuf + sizeof(struct ovdata), data, len);
1964 len += sizeof(struct ovdata);
1965 } else {
1966 sz = htonl((uint32_t)len);
1967 memcpy(databuf + sizeof(struct ovdata), &sz, sizeof(uint32_t));
1968
1969 /* The following line is mostly paranoia. Just want to make sure
1970 that the first byte is 0 (it should be 0 anyway), so ovdb_search
1971 recognizes this as compressed data. */
1972 *(databuf + sizeof(struct ovdata)) = 0;
1973
1974 len = c_sz + sizeof(struct ovdata) + sizeof(uint32_t);
1975 }
1976 } else {
1977 #endif
1978 memcpy(databuf + sizeof(struct ovdata), data, len);
1979 len += sizeof(struct ovdata);
1980 #ifdef HAVE_ZLIB
1981 }
1982 #endif
1983
1984 memset(&key, 0, sizeof key);
1985 memset(&val, 0, sizeof val);
1986
1987 /* start the transaction */
1988 TXN_START(t_add, tid);
1989
1990 if(tid==NULL)
1991 return false;
1992
1993 /* first, retrieve groupinfo */
1994 ret = ovdb_getgroupinfo(group, &gi, true, tid, DB_RMW);
1995 switch (ret)
1996 {
1997 case 0:
1998 break;
1999 case DB_NOTFOUND:
2000 TXN_ABORT(t_add, tid);
2001 return true;
2002 case TRYAGAIN:
2003 TXN_RETRY(t_add, tid);
2004 default:
2005 TXN_ABORT(t_add, tid);
2006 warn("OVDB: add: ovdb_getgroupinfo: %s", db_strerror(ret));
2007 return false;
2008 }
2009
2010 /* adjust groupinfo */
2011 if(Cutofflow && gi.low > artnum) {
2012 TXN_ABORT(t_add, tid);
2013 return true;
2014 }
2015 if(gi.low == 0 || gi.low > artnum)
2016 gi.low = artnum;
2017 if(gi.high < artnum)
2018 gi.high = artnum;
2019 gi.count++;
2020
2021 /* store groupinfo */
2022 key.data = (char *) group;
2023 key.size = strlen(group);
2024 val.data = &gi;
2025 val.size = sizeof gi;
2026
2027 ret = groupinfo->put(groupinfo, tid, &key, &val, 0);
2028 switch (ret)
2029 {
2030 case 0:
2031 break;
2032 case TRYAGAIN:
2033 TXN_RETRY(t_add, tid);
2034 default:
2035 TXN_ABORT(t_add, tid);
2036 warn("OVDB: add: groupinfo->put: %s", db_strerror(ret));
2037 return false;
2038 }
2039
2040 /* store overview */
2041 db = get_db_bynum(gi.current_db);
2042 if(db == NULL) {
2043 TXN_ABORT(t_add, tid);
2044 return false;
2045 }
2046 dk.groupnum = gi.current_gid;
2047 dk.artnum = htonl((u_int32_t)artnum);
2048
2049 key.data = &dk;
2050 key.size = sizeof dk;
2051 val.data = databuf;
2052 val.size = len;
2053
2054 ret = db->put(db, tid, &key, &val, 0);
2055 switch (ret)
2056 {
2057 case 0:
2058 break;
2059 case TRYAGAIN:
2060 TXN_RETRY(t_add, tid);
2061 default:
2062 TXN_ABORT(t_add, tid);
2063 warn("OVDB: add: db->put: %s", db_strerror(ret));
2064 return false;
2065 }
2066
2067 if(artnum < gi.high && gi.status & GROUPINFO_MOVING) {
2068 /* If the GROUPINFO_MOVING flag is set, then expireover
2069 is writing overview records under a new groupid.
2070 If this overview record is not at the highmark,
2071 we need to also store it under the new groupid */
2072 db = get_db_bynum(gi.new_db);
2073 if(db == NULL) {
2074 TXN_ABORT(t_add, tid);
2075 return false;
2076 }
2077 dk.groupnum = gi.new_gid;
2078
2079 ret = db->put(db, tid, &key, &val, 0);
2080 switch (ret)
2081 {
2082 case 0:
2083 break;
2084 case TRYAGAIN:
2085 TXN_RETRY(t_add, tid);
2086 default:
2087 TXN_ABORT(t_add, tid);
2088 warn("OVDB: add: db->put: %s", db_strerror(ret));
2089 return false;
2090 }
2091 }
2092
2093 TXN_COMMIT(t_add, tid);
2094 return true;
2095 }
2096
ovdb_cancel(const char * group UNUSED,ARTNUM artnum UNUSED)2097 bool ovdb_cancel(const char *group UNUSED, ARTNUM artnum UNUSED)
2098 {
2099 return true;
2100 }
2101
2102 #ifdef HAVE_ZLIB
2103 static char *
myuncompress(char * buf,size_t buflen,size_t * newlen)2104 myuncompress(char *buf, size_t buflen, size_t *newlen)
2105 {
2106 static char *dbuf = NULL;
2107 static uLongf dbuflen = 0, ulen;
2108 uint32_t sz;
2109 int ret;
2110
2111 memcpy(&sz, buf, sizeof(sz));
2112 sz = ntohl(sz);
2113
2114 if(sz >= dbuflen) {
2115 if(dbuflen == 0) {
2116 dbuflen = sz + 512;
2117 dbuf = xmalloc(dbuflen);
2118 } else {
2119 dbuflen = sz + 512;
2120 dbuf = xrealloc(dbuf, dbuflen);
2121 }
2122 }
2123 ulen = dbuflen - 1;
2124
2125 ret = uncompress((Bytef *)dbuf, &ulen, (Bytef *)(buf + sizeof(uint32_t)), buflen - sizeof(uint32_t));
2126 if(ret != Z_OK) {
2127 warn("OVDB: uncompress failed");
2128 return NULL;
2129 }
2130 dbuf[ulen] = 0; /* paranoia */
2131 if(newlen)
2132 *newlen = ulen;
2133 return dbuf;
2134 }
2135 #endif
2136
2137 struct ovdbsearch {
2138 DBC *cursor;
2139 group_id_t gid;
2140 u_int32_t firstart;
2141 u_int32_t lastart;
2142 int state;
2143 };
2144
2145 /* Even though nnrpd only does one search at a time, a read server process
2146 could do many concurrent searches; hence we must keep track of an arbitrary
2147 number of open searches */
2148 static struct ovdbsearch **searches = NULL;
2149 static int nsearches = 0;
2150 static int maxsearches = 0;
2151
2152 void *
ovdb_opensearch(const char * group,int low,int high)2153 ovdb_opensearch(const char *group, int low, int high)
2154 {
2155 DB *db;
2156 struct ovdbsearch *s;
2157 struct groupinfo gi;
2158 int ret;
2159
2160 if(clientmode) {
2161 struct rs_cmd rs;
2162 struct rs_opensrch repl;
2163
2164 rs.what = CMD_OPENSRCH;
2165 rs.grouplen = strlen(group)+1;
2166 rs.artlo = low;
2167 rs.arthi = high;
2168
2169 if (csend(&rs, sizeof(rs)) < 0)
2170 return NULL;
2171 if (csend(group, rs.grouplen) < 0)
2172 return NULL;
2173 crecv(&repl, sizeof(repl));
2174
2175 if(repl.status != CMD_OPENSRCH)
2176 return NULL;
2177
2178 return repl.handle;
2179 }
2180
2181 ret = ovdb_getgroupinfo(group, &gi, true, NULL, 0);
2182 switch (ret)
2183 {
2184 case 0:
2185 break;
2186 case DB_NOTFOUND:
2187 return NULL;
2188 default:
2189 warn("OVDB: ovdb_getgroupinfo failed: %s", db_strerror(ret));
2190 return NULL;
2191 }
2192
2193 s = xmalloc(sizeof(struct ovdbsearch));
2194 db = get_db_bynum(gi.current_db);
2195 if(db == NULL) {
2196 free(s);
2197 return NULL;
2198 }
2199
2200 ret = db->cursor(db, NULL, &(s->cursor), 0);
2201 if (ret != 0) {
2202 warn("OVDB: opensearch: s->db->cursor: %s", db_strerror(ret));
2203 free(s);
2204 return NULL;
2205 }
2206
2207 s->gid = gi.current_gid;
2208 s->firstart = low;
2209 s->lastart = high;
2210 s->state = 0;
2211
2212 if(searches == NULL) {
2213 nsearches = 0;
2214 maxsearches = 50;
2215 searches = xmalloc(sizeof(struct ovdbsearch *) * maxsearches);
2216 }
2217 if(nsearches == maxsearches) {
2218 maxsearches += 50;
2219 searches = xrealloc(searches, sizeof(struct ovdbsearch *) * maxsearches);
2220 }
2221 searches[nsearches] = s;
2222 nsearches++;
2223
2224 return (void *)s;
2225 }
2226
2227 bool
ovdb_search(void * handle,ARTNUM * artnum,char ** data,int * len,TOKEN * token,time_t * arrived)2228 ovdb_search(void *handle, ARTNUM *artnum, char **data, int *len,
2229 TOKEN *token, time_t *arrived)
2230 {
2231 struct ovdbsearch *s = (struct ovdbsearch *)handle;
2232 DBT key, val;
2233 u_int32_t flags;
2234 struct ovdata ovd;
2235 uint32_t sz = 0;
2236 struct datakey dk;
2237 int ret;
2238 char *dp;
2239
2240 if (clientmode) {
2241 struct rs_cmd rs;
2242 struct rs_srch repl;
2243 static char *databuf;
2244 static int buflen = 0;
2245
2246 rs.what = CMD_SRCH;
2247 rs.handle = handle;
2248
2249 if (csend(&rs, sizeof(rs)) < 0)
2250 return false;
2251 if (crecv(&repl, sizeof(repl)) < 0)
2252 return false;
2253
2254 if(repl.status != CMD_SRCH)
2255 return false;
2256 if(repl.len > buflen) {
2257 if(buflen == 0) {
2258 buflen = repl.len + 512;
2259 databuf = xmalloc(buflen);
2260 } else {
2261 buflen = repl.len + 512;
2262 databuf = xrealloc(databuf, buflen);
2263 }
2264 }
2265 crecv(databuf, repl.len);
2266
2267 if(artnum)
2268 *artnum = repl.artnum;
2269 if(token)
2270 *token = repl.token;
2271 if(arrived)
2272 *arrived = repl.arrived;
2273 if(len)
2274 *len = repl.len;
2275 if(data)
2276 *data = databuf;
2277 return true;
2278 }
2279
2280 switch(s->state) {
2281 case 0:
2282 flags = DB_SET_RANGE;
2283 memset(&dk, 0, sizeof dk);
2284 dk.groupnum = s->gid;
2285 dk.artnum = htonl(s->firstart);
2286 s->state = 1;
2287 break;
2288 case 1:
2289 flags = DB_NEXT;
2290 break;
2291 case 2:
2292 s->state = 3;
2293 return false;
2294 default:
2295 warn("OVDB: OVsearch called again after false return");
2296 return false;
2297 }
2298
2299 memset(&key, 0, sizeof key);
2300 memset(&val, 0, sizeof val);
2301 key.data = &dk;
2302 key.size = key.ulen = sizeof(struct datakey);
2303 key.flags = DB_DBT_USERMEM;
2304
2305 if(!data && !len) {
2306 /* caller doesn't need data, so we don't have to retrieve it all */
2307 val.flags |= DB_DBT_PARTIAL;
2308
2309 if(token || arrived)
2310 val.dlen = sizeof(struct ovdata);
2311 }
2312
2313 switch(ret = s->cursor->c_get(s->cursor, &key, &val, flags)) {
2314 case 0:
2315 break;
2316 case DB_NOTFOUND:
2317 s->state = 3;
2318 s->cursor->c_close(s->cursor);
2319 s->cursor = NULL;
2320 return false;
2321 default:
2322 warn("OVDB: search: c_get: %s", db_strerror(ret));
2323 s->state = 3;
2324 s->cursor->c_close(s->cursor);
2325 s->cursor = NULL;
2326 return false;
2327 }
2328
2329 if(key.size != sizeof(struct datakey)) {
2330 s->state = 3;
2331 s->cursor->c_close(s->cursor);
2332 s->cursor = NULL;
2333 return false;
2334 }
2335
2336 if(dk.groupnum != s->gid || ntohl(dk.artnum) > s->lastart) {
2337 s->state = 3;
2338 s->cursor->c_close(s->cursor);
2339 s->cursor = NULL;
2340 return false;
2341 }
2342
2343 if( ((len || data) && val.size <= sizeof(struct ovdata))
2344 || ((token || arrived) && val.size < sizeof(struct ovdata)) ) {
2345 warn("OVDB: search: bad value length");
2346 s->state = 3;
2347 s->cursor->c_close(s->cursor);
2348 s->cursor = NULL;
2349 return false;
2350 }
2351
2352 if(ntohl(dk.artnum) == s->lastart) {
2353 s->state = 2;
2354 s->cursor->c_close(s->cursor);
2355 s->cursor = NULL;
2356 }
2357
2358 if(artnum)
2359 *artnum = ntohl(dk.artnum);
2360
2361 if(token || arrived)
2362 memcpy(&ovd, val.data, sizeof(struct ovdata));
2363 if(token)
2364 *token = ovd.token;
2365 if(arrived)
2366 *arrived = ovd.arrived;
2367
2368 dp = (char *)val.data + sizeof(struct ovdata);
2369 #ifdef HAVE_ZLIB
2370 if(val.size >= (sizeof(struct ovdata) + sizeof(uint32_t)) && *dp == 0) {
2371 /* data is compressed */
2372 memcpy(&sz, dp, sizeof(uint32_t));
2373 sz = ntohl(sz);
2374 if(sz > MAX_UNZIP_SZ) { /* sanity check */
2375 warn("OVDB: search: bogus sz: %d", sz);
2376 sz = 0;
2377 }
2378 }
2379 #endif
2380
2381 if(len) {
2382 if(sz) {
2383 *len = sz;
2384 } else {
2385 *len = val.size - sizeof(struct ovdata);
2386 }
2387 }
2388
2389 if(data) {
2390 #ifdef HAVE_ZLIB
2391 if(sz && val.size > sizeof(struct ovdata) + sizeof(uint32_t)) {
2392 *data = myuncompress(dp, val.size - sizeof(struct ovdata), NULL);
2393 if(*data == NULL) {
2394 s->state = 3;
2395 s->cursor->c_close(s->cursor);
2396 s->cursor = NULL;
2397 return false;
2398 }
2399 } else
2400 #endif
2401 *data = dp;
2402 }
2403
2404 return true;
2405 }
2406
ovdb_closesearch(void * handle)2407 void ovdb_closesearch(void *handle)
2408 {
2409 int i;
2410 if(clientmode) {
2411 struct rs_cmd rs;
2412
2413 rs.what = CMD_CLOSESRCH;
2414 rs.handle = handle;
2415 csend(&rs, sizeof(rs));
2416 /* no reply is sent for a CMD_CLOSESRCH */
2417 } else {
2418 struct ovdbsearch *s = (struct ovdbsearch *)handle;
2419
2420 if(s->cursor)
2421 s->cursor->c_close(s->cursor);
2422
2423 for(i = 0; i < nsearches; i++) {
2424 if(s == searches[i]) {
2425 break;
2426 }
2427 }
2428 nsearches--;
2429 for( ; i < nsearches; i++) {
2430 searches[i] = searches[i+1];
2431 }
2432
2433 free(handle);
2434 }
2435 }
2436
2437 bool
ovdb_getartinfo(const char * group,ARTNUM artnum,TOKEN * token)2438 ovdb_getartinfo(const char *group, ARTNUM artnum, TOKEN *token)
2439 {
2440 int ret, cdb = 0;
2441 group_id_t cgid = 0;
2442 DB *db;
2443 DBT key, val;
2444 struct ovdata ovd;
2445 struct datakey dk;
2446 struct groupinfo gi;
2447 int pass = 0;
2448
2449 if(clientmode) {
2450 struct rs_cmd rs;
2451 struct rs_artinfo repl;
2452
2453 rs.what = CMD_ARTINFO;
2454 rs.grouplen = strlen(group)+1;
2455 rs.artlo = artnum;
2456
2457 if (csend(&rs, sizeof(rs)) < 0)
2458 return false;
2459 if (csend(group, rs.grouplen) < 0)
2460 return false;
2461 crecv(&repl, sizeof(repl));
2462
2463 if(repl.status != CMD_ARTINFO)
2464 return false;
2465
2466 if(token)
2467 *token = repl.token;
2468
2469 return true;
2470 }
2471
2472 while(1) {
2473 ret = ovdb_getgroupinfo(group, &gi, true, NULL, 0);
2474 switch (ret)
2475 {
2476 case 0:
2477 break;
2478 case DB_NOTFOUND:
2479 return false;
2480 default:
2481 warn("OVDB: ovdb_getgroupinfo failed: %s", db_strerror(ret));
2482 return false;
2483 }
2484
2485 if(pass) {
2486 /* This was our second groupinfo retrieval; because the article
2487 retrieval came up empty. If the group ID hasn't changed
2488 since the first groupinfo retrieval, we can assume the article
2489 is definitely not there. Otherwise, we'll try to retrieve
2490 it the article again. */
2491 if(cdb == gi.current_db && cgid == gi.current_gid)
2492 return false;
2493 }
2494
2495 db = get_db_bynum(gi.current_db);
2496 if(db == NULL)
2497 return false;
2498
2499 memset(&dk, 0, sizeof dk);
2500 dk.groupnum = gi.current_gid;
2501 dk.artnum = htonl((u_int32_t)artnum);
2502
2503 memset(&key, 0, sizeof key);
2504 memset(&val, 0, sizeof val);
2505
2506 key.data = &dk;
2507 key.size = sizeof dk;
2508
2509 /* caller doesn't need data, so we don't have to retrieve it all */
2510 val.flags = DB_DBT_PARTIAL;
2511
2512 if(token)
2513 val.dlen = sizeof(struct ovdata);
2514
2515 switch(ret = db->get(db, NULL, &key, &val, 0)) {
2516 case 0:
2517 case DB_NOTFOUND:
2518 break;
2519 default:
2520 warn("OVDB: getartinfo: db->get: %s", db_strerror(ret));
2521 return false;
2522 }
2523
2524 if(ret == DB_NOTFOUND) {
2525 /* If the group is being moved (i.e., its group ID is going
2526 to change), there's a chance the article is now under the
2527 new ID. So we'll grab the groupinfo again to check for
2528 that. */
2529 if(!pass && (gi.status & GROUPINFO_MOVING)) {
2530 cdb = gi.current_db;
2531 cgid = gi.current_gid;
2532 pass++;
2533 continue;
2534 }
2535 return false;
2536 }
2537 break;
2538 }
2539
2540 if(token && val.size < sizeof(struct ovdata)) {
2541 warn("OVDB: getartinfo: data too short");
2542 return false;
2543 }
2544
2545 if(token) {
2546 memcpy(&ovd, val.data, sizeof(struct ovdata));
2547 *token = ovd.token;
2548 }
2549 return true;
2550 }
2551
2552 bool
ovdb_expiregroup(const char * group,int * lo,struct history * h)2553 ovdb_expiregroup(const char *group, int *lo, struct history *h)
2554 {
2555 DB *db, *ndb = NULL;
2556 DBT key, val, nkey, gkey, gval;
2557 DB_TXN *tid;
2558 DBC *cursor = NULL;
2559 int ret = 0, delete, old_db = 0, cleanup;
2560 struct groupinfo gi;
2561 struct ovdata ovd;
2562 struct datakey dk, ndk;
2563 group_id_t old_gid = 0;
2564 ARTHANDLE *ah;
2565 u_int32_t artnum = 0, currentart, lowest;
2566 int i, compact, done, currentcount, newcount;
2567
2568 if(eo_start == 0) {
2569 eo_start = time(NULL);
2570 delete_old_stuff(0); /* remove deleted groups first */
2571 }
2572
2573 /* Special case: when called with NULL group, we're to clean out
2574 * records for forgotton groups (groups removed from the active file
2575 * but not from overview).
2576 * This happens at the end of the expireover run, and only if all
2577 * of the groups in the active file have been processed.
2578 * delete_old_stuff(1) will remove groups that are in ovdb but
2579 * have not been processed during this run.
2580 */
2581
2582 if(group == NULL)
2583 return delete_old_stuff(1);
2584
2585 memset(&key, 0, sizeof key);
2586 memset(&nkey, 0, sizeof nkey);
2587 memset(&val, 0, sizeof val);
2588 memset(&dk, 0, sizeof dk);
2589 memset(&ndk, 0, sizeof ndk);
2590
2591 TXN_START(t_expgroup_1, tid);
2592
2593 if(tid==NULL)
2594 return false;
2595
2596 cleanup = 0;
2597
2598 ret = ovdb_getgroupinfo(group, &gi, true, tid, DB_RMW);
2599 switch (ret)
2600 {
2601 case 0:
2602 break;
2603 case TRYAGAIN:
2604 TXN_RETRY(t_expgroup_1, tid);
2605 default:
2606 warn("OVDB: expiregroup: ovdb_getgroupinfo failed: %s",
2607 db_strerror(ret));
2608 /* fallthrough */
2609 case DB_NOTFOUND:
2610 TXN_ABORT(t_expgroup_1, tid);
2611 return false;
2612 }
2613
2614 if(gi.status & GROUPINFO_EXPIRING) {
2615 /* is there another expireover working on this group? */
2616 ret = kill(gi.expiregrouppid, 0);
2617 switch(ret)
2618 {
2619 case 0:
2620 case EPERM:
2621 TXN_ABORT(t_expgroup_1, tid);
2622 return false;
2623 }
2624
2625 /* a previous expireover run must've died. We'll clean
2626 up after it */
2627 if(gi.status & GROUPINFO_MOVING) {
2628 cleanup = 1;
2629 old_db = gi.new_db;
2630 old_gid = gi.new_gid;
2631
2632 ret = mk_temp_groupinfo(old_db, old_gid, tid);
2633 switch(ret) {
2634 case 0:
2635 break;
2636 case TRYAGAIN:
2637 TXN_RETRY(t_expgroup_1, tid);
2638 default:
2639 TXN_ABORT(t_expgroup_1, tid);
2640 return false;
2641 }
2642 gi.status &= ~GROUPINFO_MOVING;
2643 }
2644 }
2645
2646 if(gi.count < ovdb_conf.nocompact || ovdb_conf.nocompact == 0)
2647 compact = 1;
2648 else
2649 compact = 0;
2650
2651 if(gi.count == 0)
2652 compact = 0;
2653
2654 db = get_db_bynum(gi.current_db);
2655 if(db == NULL) {
2656 TXN_ABORT(t_expgroup_1, tid);
2657 return false;
2658 }
2659
2660 gi.status |= GROUPINFO_EXPIRING;
2661 gi.expiregrouppid = getpid();
2662 if(compact) {
2663 gi.status |= GROUPINFO_MOVING;
2664 gi.new_db = gi.current_db;
2665 ndb = db;
2666 ret = groupid_new(&gi.new_gid, tid);
2667 switch (ret)
2668 {
2669 case 0:
2670 break;
2671 case TRYAGAIN:
2672 TXN_RETRY(t_expgroup_1, tid);
2673 default:
2674 TXN_ABORT(t_expgroup_1, tid);
2675 warn("OVDB: expiregroup: groupid_new: %s", db_strerror(ret));
2676 return false;
2677 }
2678 }
2679
2680 key.data = (char *) group;
2681 key.size = strlen(group);
2682 val.data = &gi;
2683 val.size = sizeof gi;
2684
2685 ret = groupinfo->put(groupinfo, tid, &key, &val, 0);
2686 switch (ret)
2687 {
2688 case 0:
2689 break;
2690 case TRYAGAIN:
2691 TXN_RETRY(t_expgroup_1, tid);
2692 default:
2693 TXN_ABORT(t_expgroup_1, tid);
2694 warn("OVDB: expiregroup: groupinfo->put: %s", db_strerror(ret));
2695 return false;
2696 }
2697 TXN_COMMIT(t_expgroup_1, tid);
2698
2699 if(cleanup) {
2700 if(delete_all_records(old_db, old_gid) == 0) {
2701 rm_temp_groupinfo(old_gid);
2702 }
2703 }
2704
2705 /*
2706 * The following loop iterates over the OV records for the group in
2707 * "batches", to limit transaction sizes.
2708 *
2709 * loop {
2710 * start transaction
2711 * get groupinfo
2712 * process EXPIREGROUP_TXN_SIZE records
2713 * write updated groupinfo
2714 * commit transaction
2715 * }
2716 */
2717 currentart = 0;
2718 lowest = currentcount = 0;
2719
2720 memset(&gkey, 0, sizeof gkey);
2721 memset(&gval, 0, sizeof gval);
2722 gkey.data = (char *) group;
2723 gkey.size = strlen(group);
2724 gval.data = &gi;
2725 gval.size = sizeof gi;
2726
2727 while(1) {
2728 TXN_START(t_expgroup_loop, tid);
2729 if(tid==NULL)
2730 return false;
2731 done = 0;
2732 newcount = 0;
2733
2734 ret = ovdb_getgroupinfo(group, &gi, false, tid, DB_RMW);
2735 switch (ret)
2736 {
2737 case 0:
2738 break;
2739 case TRYAGAIN:
2740 TXN_RETRY(t_expgroup_loop, tid);
2741 default:
2742 TXN_ABORT(t_expgroup_loop, tid);
2743 warn("OVDB: expiregroup: ovdb_getgroupinfo: %s",
2744 db_strerror(ret));
2745 return false;
2746 }
2747
2748 ret = db->cursor(db, tid, &cursor, 0);
2749 switch (ret)
2750 {
2751 case 0:
2752 break;
2753 case TRYAGAIN:
2754 TXN_RETRY(t_expgroup_loop, tid);
2755 default:
2756 TXN_ABORT(t_expgroup_loop, tid);
2757 warn("OVDB: expiregroup: db->cursor: %s", db_strerror(ret));
2758 return false;
2759 }
2760
2761 dk.groupnum = gi.current_gid;
2762 dk.artnum = htonl(currentart);
2763 key.data = &dk;
2764 key.size = key.ulen = sizeof dk;
2765 key.flags = DB_DBT_USERMEM;
2766
2767 for(i=0; i < EXPIREGROUP_TXN_SIZE; i++) {
2768 ret = cursor->c_get(cursor, &key, &val,
2769 i == 0 ? DB_SET_RANGE : DB_NEXT);
2770 switch (ret)
2771 {
2772 case 0:
2773 case DB_NOTFOUND:
2774 break;
2775 case TRYAGAIN:
2776 cursor->c_close(cursor);
2777 TXN_RETRY(t_expgroup_loop, tid);
2778 default:
2779 cursor->c_close(cursor);
2780 TXN_ABORT(t_expgroup_loop, tid);
2781 warn("OVDB: expiregroup: c_get: %s", db_strerror(ret));
2782 return false;
2783 }
2784
2785 /* stop if: there are no more keys, an unknown key is reached,
2786 or reach a different group */
2787
2788 if(ret == DB_NOTFOUND
2789 || key.size != sizeof dk
2790 || dk.groupnum != gi.current_gid) {
2791 done++;
2792 break;
2793 }
2794
2795 artnum = ntohl(dk.artnum);
2796
2797 delete = 0;
2798 if(val.size < sizeof ovd) {
2799 delete = 1; /* must be corrupt, just delete it */
2800 } else {
2801 char *p = (char *)val.data + sizeof(ovd);
2802 size_t sz = val.size - sizeof(ovd);
2803 #ifdef HAVE_ZLIB
2804 if(*p == 0)
2805 p = myuncompress(p, sz, &sz);
2806 if(p == NULL) {
2807 p = (char *) "";
2808 delete = 1;
2809 }
2810 #endif
2811 memcpy(&ovd, val.data, sizeof ovd);
2812
2813 ah = NULL;
2814 if (!SMprobe(EXPENSIVESTAT, &ovd.token, NULL) || OVstatall) {
2815 if((ah = SMretrieve(ovd.token, RETR_STAT)) == NULL) {
2816 delete = 1;
2817 } else
2818 SMfreearticle(ah);
2819 } else {
2820 if (!delete && !OVhisthasmsgid(h, p)) {
2821 delete = 1;
2822 }
2823 }
2824 if (!delete && innconf->groupbaseexpiry
2825 && OVgroupbasedexpire(ovd.token, group,
2826 p, sz, ovd.arrived, ovd.expires)) {
2827 delete = 1;
2828 }
2829 }
2830
2831 if(delete) {
2832 if(!compact) {
2833 switch(ret = cursor->c_del(cursor, 0)) {
2834 case 0:
2835 case DB_NOTFOUND:
2836 case DB_KEYEMPTY:
2837 break;
2838 case TRYAGAIN:
2839 cursor->c_close(cursor);
2840 TXN_RETRY(t_expgroup_loop, tid);
2841 default:
2842 cursor->c_close(cursor);
2843 TXN_ABORT(t_expgroup_loop, tid);
2844 warn("OVDB: expiregroup: c_del: %s",
2845 db_strerror(ret));
2846 return false;
2847 }
2848 }
2849 if(gi.count > 0)
2850 gi.count--;
2851 } else {
2852 if(compact) {
2853 ndk.groupnum = gi.new_gid;
2854 ndk.artnum = dk.artnum;
2855 nkey.data = &ndk;
2856 nkey.size = sizeof ndk;
2857
2858 switch(ret = ndb->put(ndb, tid, &nkey, &val, 0)) {
2859 case 0:
2860 break;
2861 case TRYAGAIN:
2862 cursor->c_close(cursor);
2863 TXN_RETRY(t_expgroup_loop, tid);
2864 default:
2865 cursor->c_close(cursor);
2866 TXN_ABORT(t_expgroup_loop, tid);
2867 warn("OVDB: expiregroup: ndb->put: %s",
2868 db_strerror(ret));
2869 return false;
2870 }
2871 }
2872 newcount++;
2873 if (lowest != (u_int32_t) -1)
2874 if (lowest == 0 || artnum < lowest)
2875 lowest = artnum;
2876 }
2877 }
2878 /* end of for loop */
2879
2880 if(cursor->c_close(cursor) == TRYAGAIN) {
2881 TXN_RETRY(t_expgroup_loop, tid);
2882 }
2883
2884 if(lowest != 0 && lowest != (u_int32_t) -1)
2885 gi.low = lowest;
2886
2887 if(done) {
2888 if(compact) {
2889 old_db = gi.current_db;
2890 gi.current_db = gi.new_db;
2891 old_gid = gi.current_gid;
2892 gi.current_gid = gi.new_gid;
2893 gi.status &= ~GROUPINFO_MOVING;
2894
2895 ret = mk_temp_groupinfo(old_db, old_gid, tid);
2896 switch(ret) {
2897 case 0:
2898 break;
2899 case TRYAGAIN:
2900 TXN_RETRY(t_expgroup_loop, tid);
2901 default:
2902 TXN_ABORT(t_expgroup_loop, tid);
2903 return false;
2904 }
2905 }
2906
2907 gi.status &= ~GROUPINFO_EXPIRING;
2908 gi.expired = time(NULL);
2909 if(gi.count == 0 && lowest == 0)
2910 gi.low = gi.high+1;
2911 }
2912
2913 ret = groupinfo->put(groupinfo, tid, &gkey, &gval, 0);
2914 switch (ret)
2915 {
2916 case 0:
2917 break;
2918 case TRYAGAIN:
2919 TXN_RETRY(t_expgroup_loop, tid);
2920 default:
2921 TXN_ABORT(t_expgroup_loop, tid);
2922 warn("OVDB: expiregroup: groupinfo->put: %s", db_strerror(ret));
2923 return false;
2924 }
2925 TXN_COMMIT(t_expgroup_loop, tid);
2926
2927 currentcount += newcount;
2928 if(lowest != 0)
2929 lowest = (u_int32_t) -1;
2930
2931 if(done)
2932 break;
2933
2934 currentart = artnum+1;
2935 }
2936
2937 if(compact) {
2938 if(delete_all_records(old_db, old_gid) == 0) {
2939 rm_temp_groupinfo(old_gid);
2940 }
2941 }
2942
2943 if(currentcount != gi.count) {
2944 notice("OVDB: expiregroup: recounting %s", group);
2945
2946 TXN_START(t_expgroup_recount, tid);
2947 if(tid == NULL)
2948 return false;
2949
2950 switch(ret = ovdb_getgroupinfo(group, &gi, false, tid, DB_RMW)) {
2951 case 0:
2952 break;
2953 case TRYAGAIN:
2954 TXN_RETRY(t_expgroup_recount, tid);
2955 default:
2956 TXN_ABORT(t_expgroup_recount, tid);
2957 warn("OVDB: expiregroup: ovdb_getgroupinfo: %s",
2958 db_strerror(ret));
2959 return false;
2960 }
2961
2962 if(count_records(&gi) != 0) {
2963 TXN_ABORT(t_expgroup_recount, tid);
2964 return false;
2965 }
2966
2967 ret = groupinfo->put(groupinfo, tid, &gkey, &gval, 0);
2968 switch (ret)
2969 {
2970 case 0:
2971 break;
2972 case TRYAGAIN:
2973 TXN_RETRY(t_expgroup_recount, tid);
2974 default:
2975 TXN_ABORT(t_expgroup_recount, tid);
2976 warn("OVDB: expiregroup: groupinfo->put: %s", db_strerror(ret));
2977 return false;
2978 }
2979 TXN_COMMIT(t_expgroup_recount, tid);
2980 }
2981
2982 if(lo)
2983 *lo = gi.low;
2984 return true;
2985 }
2986
ovdb_ctl(OVCTLTYPE type,void * val)2987 bool ovdb_ctl(OVCTLTYPE type, void *val)
2988 {
2989 int *i;
2990 float *f;
2991 OVSORTTYPE *sorttype;
2992 bool *boolval;
2993
2994 switch (type) {
2995 case OVSPACE:
2996 f = (float *)val;
2997 *f = -1.0f;
2998 return true;
2999 case OVSORT:
3000 sorttype = (OVSORTTYPE *)val;
3001 *sorttype = OVNEWSGROUP;
3002 return true;
3003 case OVCUTOFFLOW:
3004 Cutofflow = *(bool *)val;
3005 return true;
3006 case OVSTATICSEARCH:
3007 i = (int *)val;
3008 *i = true;
3009 return true;
3010 case OVCACHEKEEP:
3011 case OVCACHEFREE:
3012 boolval = (bool *)val;
3013 *boolval = false;
3014 return true;
3015 default:
3016 return false;
3017 }
3018 }
3019
ovdb_close_berkeleydb(void)3020 void ovdb_close_berkeleydb(void)
3021 {
3022 if(OVDBenv) {
3023 /* close db environment */
3024 OVDBenv->close(OVDBenv, 0);
3025 OVDBenv = NULL;
3026 }
3027 }
3028
ovdb_close(void)3029 void ovdb_close(void)
3030 {
3031 int i;
3032
3033 if(clientmode) {
3034 client_disconnect();
3035 return;
3036 }
3037
3038 while(searches != NULL && nsearches) {
3039 ovdb_closesearch(searches[0]);
3040 }
3041 if(searches != NULL) {
3042 free(searches);
3043 searches = NULL;
3044 }
3045
3046 if(dbs) {
3047 /* close databases */
3048 for(i = 0; i < ovdb_conf.numdbfiles; i++)
3049 close_db_file(i);
3050
3051 free(dbs);
3052 dbs = NULL;
3053 }
3054 if(groupinfo) {
3055 groupinfo->close(groupinfo, 0);
3056 groupinfo = NULL;
3057 }
3058 if(groupaliases) {
3059 groupaliases->close(groupaliases, 0);
3060 groupaliases = NULL;
3061 }
3062
3063 ovdb_close_berkeleydb();
3064 ovdb_releaselock();
3065 }
3066
3067 #endif /* HAVE_BDB */
3068