#include <config.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <syslog.h>
#include <errno.h>
#if HAVE_DIRENT_H
# include <dirent.h>
# define NAMLEN(dirent) strlen((dirent)->d_name)
#else
# define dirent direct
# define NAMLEN(dirent) (dirent)->d_namlen
# if HAVE_SYS_NDIR_H
# include <sys/ndir.h>
# endif
# if HAVE_SYS_DIR_H
# include <sys/dir.h>
# endif
# if HAVE_NDIR_H
# include <ndir.h>
# endif
#endif
#include "annotate.h"
#include "cyrusdb.h"
#include "duplicate.h"
#include "global.h"
#include "exitcodes.h"
#include "libcyr_cfg.h"
#include "mboxlist.h"
#include "seen.h"
#include "tls.h"
#include "util.h"
#include "xmalloc.h"
#define N(a) (sizeof(a) / sizeof(a[0]))
const int config_need_data = 0;
struct cyrusdb {
const char *name;
struct cyrusdb_backend **env;
int archive;
} dblist[] = {
{ FNAME_MBOXLIST, &config_mboxlist_db, 1 },
{ FNAME_QUOTADB, &config_quota_db, 1 },
{ FNAME_ANNOTATIONS, &config_annotation_db, 1 },
{ FNAME_DELIVERDB, &config_duplicate_db, 0 },
{ FNAME_TLSSESSIONS, &config_tlscache_db, 0 },
#ifdef WITH_PTS
{ FNAME_PTSDB, &config_ptscache_db, 0 },
#endif
{ NULL, NULL, 0 }
};
static int compdb(const void *v1, const void *v2)
{
struct cyrusdb *db1 = (struct cyrusdb *) v1;
struct cyrusdb *db2 = (struct cyrusdb *) v2;
return (db1->env - db2->env);
}
void usage(void)
{
fprintf(stderr, "ctl_cyrusdb [-C <altconfig>] -c\n");
fprintf(stderr, "ctl_cyrusdb [-C <altconfig>] -r [-x]\n");
exit(-1);
}
static int fixmbox(char *name,
int matchlen __attribute__((unused)),
int maycreate __attribute__((unused)),
void *rock __attribute__((unused)))
{
int mbtype;
int r;
char *path, *part, *acl;
r = mboxlist_detail(name, &mbtype, &path, &part, &acl, NULL);
if(!r && (mbtype & MBTYPE_RESERVE)) {
if(!r) {
r = mboxlist_deletemailbox(name, 1, NULL, NULL, 0, 0, 1);
if(r) {
syslog(LOG_ERR,
"could not remove reserved mailbox '%s': %s",
name, error_message(r));
} else {
syslog(LOG_ERR,
"removed reserved mailbox '%s'",
name);
}
}
}
return 0;
}
void recover_reserved()
{
char pattern[2] = { '*', '\0' };
mboxlist_init(0);
mboxlist_open(NULL);
annotatemore_init(0, NULL, NULL);
annotatemore_open(NULL);
mboxlist_findall(NULL, pattern, 1, NULL,
NULL, fixmbox, NULL);
annotatemore_close();
annotatemore_done();
mboxlist_close();
mboxlist_done();
}
int main(int argc, char *argv[])
{
extern char *optarg;
int opt, r, r2;
char *alt_config = NULL;
int reserve_flag = 1;
enum { RECOVER, CHECKPOINT, NONE } op = NONE;
char dirname[1024], backup1[1024], backup2[1024];
char *archive_files[N(dblist)];
char *msg = "";
int i, j, rotated = 0;
if (geteuid() == 0) fatal("must run as the Cyrus user", EC_USAGE);
r = r2 = 0;
while ((opt = getopt(argc, argv, "C:rxc")) != EOF) {
switch (opt) {
case 'C':
alt_config = optarg;
break;
case 'r':
libcyrus_config_setint(CYRUSOPT_DB_INIT_FLAGS, CYRUSDB_RECOVER);
msg = "verifying cyrus databases";
if (op == NONE) op = RECOVER;
else usage();
break;
case 'c':
msg = "checkpointing cyrus databases";
if (op == NONE) op = CHECKPOINT;
else usage();
break;
case 'x':
reserve_flag = 0;
break;
default:
usage();
break;
}
}
if (op == NONE || (op != RECOVER && !reserve_flag)) {
usage();
}
cyrus_init(alt_config, "ctl_cyrusdb", 0);
strcpy(dirname, config_dir);
strcat(dirname, FNAME_DBDIR);
strcpy(backup1, dirname);
strcat(backup1, ".backup1");
strcpy(backup2, dirname);
strcat(backup2, ".backup2");
syslog(LOG_NOTICE, "%s", msg);
qsort(dblist, N(dblist)-1, sizeof(struct cyrusdb), &compdb);
memset(archive_files, 0, N(dblist) * sizeof(char*));
for (i = 0, j = 0; dblist[i].name != NULL; i++) {
if (dblist[i].archive) {
archive_files[j] = (char*) xmalloc(strlen(config_dir) +
strlen(dblist[i].name) + 1);
strcpy(archive_files[j], config_dir);
strcat(archive_files[j++], dblist[i].name);
}
if (dblist[i].env == dblist[i+1].env) continue;
r = r2 = 0;
switch (op) {
case RECOVER:
break;
case CHECKPOINT:
r2 = (*(dblist[i].env))->sync();
if (r2) {
syslog(LOG_ERR, "DBERROR: sync %s: %s", dirname,
cyrusdb_strerror(r2));
fprintf(stderr,
"ctl_cyrusdb: unable to sync environment\n");
}
r2 = 0;
if (!rotated) {
char *tail;
DIR *dirp;
struct dirent *dirent;
tail = backup2 + strlen(backup2);
dirp = opendir(backup2);
strcat(tail++, "/");
if (dirp) {
while ((dirent = readdir(dirp)) != NULL) {
if (dirent->d_name[0] == '.') continue;
strcpy(tail, dirent->d_name);
unlink(backup2);
}
closedir(dirp);
}
tail[-1] = '\0';
r2 = rmdir(backup2);
if (r2 == 0 || errno == ENOENT)
r2 = rename(backup1, backup2);
if (r2 == 0 || errno == ENOENT)
r2 = mkdir(backup1, 0755);
rotated = 1;
}
if (r2 == 0)
r2 = (*(dblist[i].env))->archive((const char**) archive_files,
backup1);
if (r2) {
syslog(LOG_ERR, "DBERROR: archive %s: %s", dirname,
cyrusdb_strerror(r2));
fprintf(stderr,
"ctl_cyrusdb: unable to archive environment\n");
}
break;
default:
break;
}
while (j > 0) {
free(archive_files[--j]);
archive_files[j] = NULL;
}
}
if(op == RECOVER && reserve_flag)
recover_reserved();
cyrus_done();
syslog(LOG_NOTICE, "done %s", msg);
exit(r || r2);
}