X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fcontrol.c;h=3463de80b3baf0e6e817e1b96d62c9daf52e3297;hb=9ffea7c3315046ddcea2589656c13da5f5e0c076;hp=2b2721d04a31e1bca930bff66e5a29ba9f0eee74;hpb=cd5ae5682a319e1d7c698cb03c915b04bd55b978;p=citadel.git diff --git a/citadel/control.c b/citadel/control.c index 2b2721d04..3463de80b 100644 --- a/citadel/control.c +++ b/citadel/control.c @@ -1,7 +1,7 @@ /* * This module handles states which are global to the entire server. * - * Copyright (c) 1987-2015 by the citadel.org team + * Copyright (c) 1987-2019 by the citadel.org team * * This program is open source software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3. @@ -21,28 +21,32 @@ #include "citserver.h" #include "user_ops.h" -struct CitControl CitControl; -FILE *control_fp = NULL; long control_highest_user = 0; - /* - * lock_control - acquire a lock on the control record file. - * This keeps multiple citservers from running concurrently. + * This is the legacy "control record" format for the message base. If found + * on disk, its contents will be migrated into the system configuration. Never + * change this. */ -void lock_control(void) -{ -#if defined(LOCK_EX) && defined(LOCK_NB) - if (flock(fileno(control_fp), (LOCK_EX | LOCK_NB))) { - syslog(LOG_EMERG, "citserver: unable to lock %s.\n", file_citadel_control); - syslog(LOG_EMERG, "Is another citserver already running?\n"); - exit(CTDLEXIT_CONTROL); - } -#endif -} +struct legacy_ctrl_format { + long MMhighest; /* highest message number in file */ + unsigned MMflags; /* Global system flags */ + long MMnextuser; /* highest user number on system */ + long MMnextroom; /* highest room number on system */ + int MM_hosted_upgrade_level; /* Server-hosted upgrade level */ + int MM_fulltext_wordbreaker; /* ID of wordbreaker in use */ + long MMfulltext; /* highest message number indexed */ + int MMdbversion; /* Version of Berkeley DB used on previous server run */ +}; + /* - * callback to get highest room number when rebuilding control file + * Callback to get highest room number when rebuilding message base metadata + * + * sanity_diag_mode (can be set by -s flag at startup) may be: + * 0 = attempt to fix inconsistencies + * 1 = show inconsistencies but don't repair them, exit after complete + * 2 = show inconsistencies but don't repair them, continue execution */ void control_find_highest(struct ctdlroom *qrbuf, void *data) { @@ -51,16 +55,17 @@ void control_find_highest(struct ctdlroom *qrbuf, void *data) long *msglist; int num_msgs=0; int c; - int room_fixed = 0; - int message_fixed = 0; - if (qrbuf->QRnumber > CitControl.MMnextroom) - { - CitControl.MMnextroom = qrbuf->QRnumber; - room_fixed = 1; + if (qrbuf->QRnumber > CtdlGetConfigLong("MMnextroom")) { + syslog(LOG_DEBUG, "control: fixing MMnextroom %ld > %ld , found in %s", + qrbuf->QRnumber, CtdlGetConfigLong("MMnextroom"), qrbuf->QRname + ); + if (!sanity_diag_mode) { + CtdlSetConfigLong("MMnextroom", qrbuf->QRnumber); + } } - CtdlGetRoom (&room, qrbuf->QRname); + CtdlGetRoom(&room, qrbuf->QRname); /* Load the message list */ cdbfr = cdb_fetch(CDB_MSGLISTS, &room.QRnumber, sizeof(long)); @@ -71,22 +76,19 @@ void control_find_highest(struct ctdlroom *qrbuf, void *data) return; /* No messages at all? No further action. */ } - if (num_msgs>0) - { - for (c=0; c CitControl.MMhighest) - { - CitControl.MMhighest = msglist[c]; - message_fixed = 1; + if (num_msgs > 0) { + for (c=0; c CtdlGetConfigLong("MMhighest")) { + syslog(LOG_DEBUG, "control: fixing MMhighest %ld > %ld , found in %s", + msglist[c], CtdlGetConfigLong("MMhighest"), qrbuf->QRname + ); + if (!sanity_diag_mode) { + CtdlSetConfigLong("MMhighest", msglist[c]); + } } } } cdb_free(cdbfr); - if (room_fixed) - syslog(LOG_INFO, "Control record checking....Fixed room counter\n"); - if (message_fixed) - syslog(LOG_INFO, "Control record checking....Fixed message count\n"); return; } @@ -94,107 +96,52 @@ void control_find_highest(struct ctdlroom *qrbuf, void *data) /* * Callback to get highest user number. */ - -void control_find_user (struct ctdluser *EachUser, void *out_data) -{ - int user_fixed = 0; - - if (EachUser->usernum > CitControl.MMnextuser) - { - CitControl.MMnextuser = EachUser->usernum; - user_fixed = 1; - } - if(user_fixed) - syslog(LOG_INFO, "Control record checking....Fixed user count\n"); -} +void control_find_user(char *username, void *out_data) { + struct ctdluser EachUser; - -/* - * get_control - read the control record into memory. - */ -void get_control(void) -{ - static int already_have_control = 0; - int rv = 0; - - /* - * If we already have the control record in memory, there's no point - * in reading it from disk again. - */ - if (already_have_control) return; - - /* Zero it out. If the control record on disk is missing or short, - * the system functions with all control record fields initialized - * to zero. - */ - memset(&CitControl, 0, sizeof(struct CitControl)); - if (control_fp == NULL) { - control_fp = fopen(file_citadel_control, "rb+"); - if (control_fp != NULL) { - lock_control(); - rv = fchown(fileno(control_fp), ctdluid, -1); - if (rv == -1) - syslog(LOG_EMERG, "Failed to adjust ownership of: %s [%s]\n", - file_citadel_control, strerror(errno)); - rv = fchmod(fileno(control_fp), S_IRUSR|S_IWUSR); - if (rv == -1) - syslog(LOG_EMERG, "Failed to adjust accessrights of: %s [%s]\n", - file_citadel_control, strerror(errno)); - } - } - if (control_fp == NULL) { - control_fp = fopen(file_citadel_control, "wb+"); - if (control_fp != NULL) { - lock_control(); - memset(&CitControl, 0, sizeof(struct CitControl)); - - rv = fchown(fileno(control_fp), ctdluid, -1); - if (rv == -1) - syslog(LOG_EMERG, "Failed to adjust ownership of: %s [%s]\n", - file_citadel_control, strerror(errno)); - - rv = fchmod(fileno(control_fp), S_IRUSR|S_IWUSR); - if (rv == -1) - syslog(LOG_EMERG, "Failed to adjust accessrights of: %s [%s]\n", - file_citadel_control, strerror(errno)); - rv = fwrite(&CitControl, sizeof(struct CitControl), 1, control_fp); - if (rv == -1) - syslog(LOG_EMERG, "Failed to write: %s [%s]\n", - file_citadel_control, strerror(errno)); - rewind(control_fp); - } - } - if (control_fp == NULL) { - syslog(LOG_ALERT, "ERROR opening %s: %s\n", file_citadel_control, strerror(errno)); + if (CtdlGetUser(&EachUser, username) != 0) { return; } - rewind(control_fp); - rv = fread(&CitControl, sizeof(struct CitControl), 1, control_fp); - if (rv == -1) - syslog(LOG_EMERG, "Failed to read Controlfile: %s [%s]\n", - file_citadel_control, strerror(errno)); - already_have_control = 1; - rv = chown(file_citadel_control, ctdluid, (-1)); - if (rv == -1) - syslog(LOG_EMERG, "Failed to adjust ownership of: %s [%s]\n", - file_citadel_control, strerror(errno)); + if (EachUser.usernum > CtdlGetConfigLong("MMnextuser")) { + syslog(LOG_DEBUG, "control: fixing MMnextuser %ld > %ld , found in %s", + EachUser.usernum, CtdlGetConfigLong("MMnextuser"), EachUser.fullname + ); + if (!sanity_diag_mode) { + CtdlSetConfigLong("MMnextuser", EachUser.usernum); + } + } } + /* - * put_control - write the control record to disk. + * If we have a legacy format control record on disk, import it. */ -void put_control(void) +void migrate_legacy_control_record(void) { - int rv = 0; - - if (control_fp != NULL) { - rewind(control_fp); - rv = fwrite(&CitControl, sizeof(struct CitControl), 1, control_fp); - if (rv == -1) - syslog(LOG_EMERG, "Failed to write: %s [%s]\n", - file_citadel_control, strerror(errno)); - fflush(control_fp); + FILE *fp = NULL; + struct legacy_ctrl_format c; + memset(&c, 0, sizeof(c)); + + fp = fopen(file_citadel_control, "rb+"); + if (fp != NULL) { + syslog(LOG_INFO, "control: legacy format record found -- importing to db"); + fread(&c, sizeof(struct legacy_ctrl_format), 1, fp); + + CtdlSetConfigLong( "MMhighest", c.MMhighest); + CtdlSetConfigInt( "MMflags", c.MMflags); + CtdlSetConfigLong( "MMnextuser", c.MMnextuser); + CtdlSetConfigLong( "MMnextroom", c.MMnextroom); + CtdlSetConfigInt( "MM_hosted_upgrade_level", c.MM_hosted_upgrade_level); + CtdlSetConfigInt( "MM_fulltext_wordbreaker", c.MM_fulltext_wordbreaker); + CtdlSetConfigLong( "MMfulltext", c.MMfulltext); + + fclose(fp); + if (unlink(file_citadel_control) != 0) { + fprintf(stderr, "Unable to remove legacy control record %s after migrating it.\n", file_citadel_control); + fprintf(stderr, "Exiting to prevent data corruption.\n"); + exit(CTDLEXIT_CONFIG); + } } } @@ -204,26 +151,18 @@ void put_control(void) */ void check_control(void) { - syslog(LOG_INFO, "Checking/re-building control record\n"); - get_control(); - // Find highest room number and message number. + syslog(LOG_INFO, "control: sanity checking the recorded highest message and room numbers"); CtdlForEachRoom(control_find_highest, NULL); + syslog(LOG_INFO, "control: sanity checking the recorded highest user number"); ForEachUser(control_find_user, NULL); - put_control(); -} - - -/* - * release_control - close our fd on exit - */ -void release_control(void) -{ - if (control_fp != NULL) { - fclose(control_fp); + syslog(LOG_INFO, "control: sanity checks complete"); + if (sanity_diag_mode == 1) { + syslog(LOG_INFO, "control: sanity check diagnostic mode is active - exiting now"); + abort(); } - control_fp = NULL; } + /* * get_new_message_number() - Obtain a new, unique ID to be used for a message. */ @@ -231,9 +170,9 @@ long get_new_message_number(void) { long retval = 0L; begin_critical_section(S_CONTROL); - get_control(); - retval = ++CitControl.MMhighest; - put_control(); + retval = CtdlGetConfigLong("MMhighest"); + ++retval; + CtdlSetConfigLong("MMhighest", retval); end_critical_section(S_CONTROL); return(retval); } @@ -244,15 +183,12 @@ long get_new_message_number(void) * This provides a quick way to initialise a variable that might be used to indicate * messages that should not be processed. EG. a new Sieve script will use this * to record determine that messages older than this should not be processed. + * + * (Why is this function here? Can't we just go straight to the config variable it fetches?) */ long CtdlGetCurrentMessageNumber(void) { - long retval = 0L; - begin_critical_section(S_CONTROL); - get_control(); - retval = CitControl.MMhighest; - end_critical_section(S_CONTROL); - return(retval); + return CtdlGetConfigLong("MMhighest"); } @@ -263,15 +199,14 @@ long get_new_user_number(void) { long retval = 0L; begin_critical_section(S_CONTROL); - get_control(); - retval = ++CitControl.MMnextuser; - put_control(); + retval = CtdlGetConfigLong("MMnextuser"); + ++retval; + CtdlSetConfigLong("MMnextuser", retval); end_critical_section(S_CONTROL); return(retval); } - /* * get_new_room_number() - Obtain a new, unique ID to be used for a room. */ @@ -279,15 +214,14 @@ long get_new_room_number(void) { long retval = 0L; begin_critical_section(S_CONTROL); - get_control(); - retval = ++CitControl.MMnextroom; - put_control(); + retval = CtdlGetConfigLong("MMnextroom"); + ++retval; + CtdlSetConfigLong("MMnextroom", retval); end_critical_section(S_CONTROL); return(retval); } - /* * Helper function for cmd_conf() to handle boolean values */ @@ -303,13 +237,13 @@ int confbool(char *v) * Get or set global configuration options * * IF YOU ADD OR CHANGE FIELDS HERE, YOU *MUST* DOCUMENT YOUR CHANGES AT: - * http://www.citadel.org/doku.php?id=documentation:applicationprotocol + * http://www.citadel.org/doku.php/documentation:appproto:system_config * */ void cmd_conf(char *argbuf) { char cmd[16]; - char buf[256]; + char buf[1024]; int a, i; long ii; char *confptr; @@ -318,6 +252,9 @@ void cmd_conf(char *argbuf) if (CtdlAccessCheck(ac_aide)) return; extract_token(cmd, argbuf, 0, '|', sizeof cmd); + + // CONF GET - retrieve system configuration in legacy format + // This is deprecated; please do not add fields or change their order. if (!strcasecmp(cmd, "GET")) { cprintf("%d Configuration...\n", LISTING_FOLLOWS); cprintf("%s\n", CtdlGetConfigStr("c_nodename")); @@ -381,13 +318,13 @@ void cmd_conf(char *argbuf) cprintf("%d\n", CtdlGetConfigInt("c_pftcpdict_port")); cprintf("%d\n", CtdlGetConfigInt("c_managesieve_port")); cprintf("%d\n", CtdlGetConfigInt("c_auth_mode")); - cprintf("%s\n", CtdlGetConfigStr("c_funambol_host")); - cprintf("%d\n", CtdlGetConfigInt("c_funambol_port")); - cprintf("%s\n", CtdlGetConfigStr("c_funambol_source")); - cprintf("%s\n", CtdlGetConfigStr("c_funambol_auth")); + cprintf("\n"); + cprintf("\n"); + cprintf("\n"); + cprintf("\n"); cprintf("%d\n", CtdlGetConfigInt("c_rbl_at_greeting")); - cprintf("%s\n", CtdlGetConfigStr("c_master_user")); - cprintf("%s\n", CtdlGetConfigStr("c_master_pass")); + cprintf("\n"); + cprintf("\n"); cprintf("%s\n", CtdlGetConfigStr("c_pager_program")); cprintf("%d\n", CtdlGetConfigInt("c_imap_keep_from")); cprintf("%d\n", CtdlGetConfigInt("c_xmpp_c2s_port")); @@ -403,6 +340,8 @@ void cmd_conf(char *argbuf) cprintf("000\n"); } + // CONF SET - set system configuration in legacy format + // This is deprecated; please do not add fields or change their order. else if (!strcasecmp(cmd, "SET")) { unbuffer_output(); cprintf("%d Send configuration...\n", SEND_LISTING); @@ -422,7 +361,7 @@ void cmd_conf(char *argbuf) /* placeholder -- field no longer in use */ break; case 4: - CtdlSetConfigInt("c_creataide", atoi(buf)); + CtdlSetConfigInt("c_creataide", confbool(buf)); break; case 5: CtdlSetConfigInt("c_sleeping", atoi(buf)); @@ -449,10 +388,10 @@ void cmd_conf(char *argbuf) CtdlSetConfigInt("c_restrict", confbool(buf)); break; case 12: - CtdlSetConfigInt("c_site_location", confbool(buf)); + CtdlSetConfigStr("c_site_location", buf); break; case 13: - CtdlSetConfigInt("c_sysadm", confbool(buf)); + CtdlSetConfigStr("c_sysadm", buf); break; case 14: i = atoi(buf); @@ -589,25 +528,25 @@ void cmd_conf(char *argbuf) CtdlSetConfigInt("c_auth_mode", atoi(buf)); break; case 53: - CtdlSetConfigStr("c_funambol_host", buf); + /* niu */ break; case 54: - CtdlSetConfigInt("c_funambol_port", atoi(buf)); + /* niu */ break; case 55: - CtdlSetConfigStr("c_funambol_source", buf); + /* niu */ break; case 56: - CtdlSetConfigStr("c_funambol_auth", buf); + /* niu */ break; case 57: CtdlSetConfigInt("c_rbl_at_greeting", confbool(buf)); break; case 58: - CtdlSetConfigStr("c_master_user", buf); + /* niu */ break; case 59: - CtdlSetConfigStr("c_master_pass", buf); + /* niu */ break; case 60: CtdlSetConfigStr("c_pager_program", buf); @@ -652,7 +591,7 @@ void cmd_conf(char *argbuf) "The global system configuration has been edited by %s.\n", (CC->logged_in ? CC->curr_user : "an administrator") ); - CtdlAideMessage(buf,"Citadel Configuration Manager Message"); + CtdlAideMessage(buf, "Citadel Configuration Manager Message"); if (!IsEmptyStr(CtdlGetConfigStr("c_logpages"))) CtdlCreateRoom(CtdlGetConfigStr("c_logpages"), 3, "", 0, 1, 1, VIEW_BBS); @@ -661,11 +600,11 @@ void cmd_conf(char *argbuf) * index so it doesn't try to use it later. */ if (CtdlGetConfigInt("c_enable_fulltext") == 0) { - CitControl.MM_fulltext_wordbreaker = 0; - put_control(); + CtdlSetConfigInt("MM_fulltext_wordbreaker", 0); } } + // CONF GETSYS - retrieve arbitrary system configuration stanzas stored in the message base else if (!strcasecmp(cmd, "GETSYS")) { extract_token(confname, argbuf, 1, '|', sizeof confname); confptr = CtdlGetSysConfig(confname); @@ -685,21 +624,90 @@ void cmd_conf(char *argbuf) } } + // CONF PUTSYS - store arbitrary system configuration stanzas in the message base else if (!strcasecmp(cmd, "PUTSYS")) { extract_token(confname, argbuf, 1, '|', sizeof confname); unbuffer_output(); cprintf("%d %s\n", SEND_LISTING, confname); - confptr = CtdlReadMessageBody(HKEY("000"), CtdlGetConfigLong("c_maxmsglen"), NULL, 0, 0); + confptr = CtdlReadMessageBody(HKEY("000"), CtdlGetConfigLong("c_maxmsglen"), NULL, 0); CtdlPutSysConfig(confname, confptr); free(confptr); } + // CONF GETVAL - retrieve configuration variables from the database + // CONF LOADVAL - same thing but can handle variables bigger than 1 KB + else if ( (!strcasecmp(cmd, "GETVAL")) || (!strcasecmp(cmd, "LOADVAL")) ) { + extract_token(confname, argbuf, 1, '|', sizeof confname); + char *v = CtdlGetConfigStr(confname); + if ( (v) && (!strcasecmp(cmd, "GETVAL")) ) { + cprintf("%d %s|\n", CIT_OK, v); + } + else if ( (v) && (!strcasecmp(cmd, "LOADVAL")) ) { + cprintf("%d %d\n", BINARY_FOLLOWS, (int)strlen(v)); + client_write(v, strlen(v)); + } + else { + cprintf("%d |\n", ERROR); + } + } + + // CONF PUTVAL - store configuration variables in the database + else if (!strcasecmp(cmd, "PUTVAL")) { + if (num_tokens(argbuf, '|') < 3) { + cprintf("%d name and value required\n", ERROR); + } + else { + extract_token(confname, argbuf, 1, '|', sizeof confname); + extract_token(buf, argbuf, 2, '|', sizeof buf); + CtdlSetConfigStr(confname, buf); + cprintf("%d setting '%s' to '%s'\n", CIT_OK, confname, buf); + } + } + + // CONF STOREVAL - store configuration variables in the database bigger than 1 KB + else if (!strcasecmp(cmd, "STOREVAL")) { + if (num_tokens(argbuf, '|') < 3) { + cprintf("%d name and length required\n", ERROR); + } + else { + extract_token(confname, argbuf, 1, '|', sizeof confname); + int bytes = extract_int(argbuf, 2); + char *valbuf = malloc(bytes + 1); + cprintf("%d %d\n", SEND_BINARY, bytes); + client_read(valbuf, bytes); + valbuf[bytes+1] = 0; + CtdlSetConfigStr(confname, valbuf); + free(valbuf); + } + } + + // CONF LISTVAL - list configuration variables in the database and their values + else if (!strcasecmp(cmd, "LISTVAL")) { + struct cdbdata *cdbcfg; + int keylen = 0; + char *key = NULL; + char *value = NULL; + + cprintf("%d all configuration variables\n", LISTING_FOLLOWS); + cdb_rewind(CDB_CONFIG); + while (cdbcfg = cdb_next_item(CDB_CONFIG), cdbcfg != NULL) { + if (cdbcfg->len < 1020) { + keylen = strlen(cdbcfg->ptr); + key = cdbcfg->ptr; + value = cdbcfg->ptr + keylen + 1; + cprintf("%s|%s\n", key, value); + } + cdb_free(cdbcfg); + } + cprintf("000\n"); + } + else { - cprintf("%d Illegal option(s) specified.\n", - ERROR + ILLEGAL_VALUE); + cprintf("%d Illegal option(s) specified.\n", ERROR + ILLEGAL_VALUE); } } + typedef struct __ConfType { ConstStr Name; long Type;