X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fserv_expire.c;h=6927a6509dbf5112935bcc1ca884a42d1d5494ff;hb=8eac29db12e027867644fcac287a6b4c34e067c7;hp=6ba24723e7f53f11ff5b7278b363555b54387bb7;hpb=4d9a00d2846d49086faf8dfefb832925be9e19b3;p=citadel.git diff --git a/citadel/serv_expire.c b/citadel/serv_expire.c index 6ba24723e..6927a6509 100644 --- a/citadel/serv_expire.c +++ b/citadel/serv_expire.c @@ -1,9 +1,8 @@ /* - * serv_expire.c + * $Id$ * * This module handles the expiry of old messages and the purging of old users. * - * $Id$ */ @@ -15,11 +14,14 @@ * then the second stage deletes all listed objects from the database. * * At first glance this may seem cumbersome and unnecessary. The reason it is - * implemented in this way is because GDBM (and perhaps some other backends we - * may hook into in the future) explicitly do _not_ support the deletion of + * implemented in this way is because Berkeley DB, and possibly other backends + * we may hook into in the future, explicitly do _not_ support the deletion of * records from a file while the file is being traversed. The delete operation * will succeed, but the traversal is not guaranteed to visit every object if * this is done. Therefore we utilize the two-stage purge. + * + * When using Berkeley DB, there's another reason for the two-phase purge: we + * don't want the entire thing being done as one huge transaction. */ @@ -32,35 +34,38 @@ #include #include #include -#include + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + #include #include #include #include "citadel.h" #include "server.h" -#include -#include #include "sysdep_decls.h" #include "citserver.h" #include "support.h" #include "config.h" -#include "dynloader.h" +#include "serv_extensions.h" #include "room_ops.h" #include "policy.h" #include "database.h" #include "msgbase.h" #include "user_ops.h" #include "control.h" +#include "serv_network.h" #include "tools.h" -struct oldvisit { - char v_roomname[ROOMNAMELEN]; - long v_generation; - long v_lastseen; - unsigned int v_flags; -}; - struct PurgeList { struct PurgeList *next; char name[ROOMNAMELEN]; /* use the larger of username or roomname */ @@ -84,15 +89,40 @@ struct ValidUser { long vu_usernum; }; + +struct ctdlroomref { + struct ctdlroomref *next; + long msgnum; +}; + +struct UPurgeList { + struct UPurgeList *next; + char up_key[256]; +}; + +struct EPurgeList { + struct EPurgeList *next; + int ep_keylen; + char *ep_key; +}; + + struct PurgeList *UserPurgeList = NULL; struct PurgeList *RoomPurgeList = NULL; struct ValidRoom *ValidRoomList = NULL; struct ValidUser *ValidUserList = NULL; int messages_purged; +struct ctdlroomref *rr = NULL; + extern struct CitContext *ContextList; -void DoPurgeMessages(struct quickroom *qrbuf) { + +/* + * First phase of message purge -- gather the locations of messages which + * qualify for purging and write them to a temp file. + */ +void GatherPurgeMessages(struct ctdlroom *qrbuf, void *data) { struct ExpirePolicy epbuf; long delnum; time_t xtime, now; @@ -101,40 +131,40 @@ void DoPurgeMessages(struct quickroom *qrbuf) { struct cdbdata *cdbfr; long *msglist = NULL; int num_msgs = 0; + FILE *purgelist; + + purgelist = (FILE *)data; + fprintf(purgelist, "r=%s\n", qrbuf->QRname); time(&now); GetExpirePolicy(&epbuf, qrbuf); - + /* If the room is set to never expire messages ... do nothing */ if (epbuf.expire_mode == EXPIRE_NEXTLEVEL) return; if (epbuf.expire_mode == EXPIRE_MANUAL) return; - begin_critical_section(S_QUICKROOM); cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf->QRnumber, sizeof(long)); if (cdbfr != NULL) { - msglist = mallok(cdbfr->len); + msglist = malloc(cdbfr->len); memcpy(msglist, cdbfr->ptr, cdbfr->len); num_msgs = cdbfr->len / sizeof(long); cdb_free(cdbfr); } - + /* Nothing to do if there aren't any messages */ if (num_msgs == 0) { - end_critical_section(S_QUICKROOM); + if (msglist != NULL) free(msglist); return; } /* If the room is set to expire by count, do that */ if (epbuf.expire_mode == EXPIRE_NUMMSGS) { - while (num_msgs > epbuf.expire_value) { - delnum = msglist[0]; - lprintf(5, "Expiring message %ld\n", delnum); - AdjRefCount(delnum, -1); - memcpy(&msglist[0], &msglist[1], - (sizeof(long)*(num_msgs - 1))); - --num_msgs; - ++messages_purged; + if (num_msgs > epbuf.expire_value) { + for (a=0; a<(num_msgs - epbuf.expire_value); ++a) { + fprintf(purgelist, "m=%ld\n", msglist[a]); + ++messages_purged; + } } } @@ -143,7 +173,7 @@ void DoPurgeMessages(struct quickroom *qrbuf) { for (a=0; acm_fields['T']); CtdlFreeMessage(msg); @@ -153,53 +183,81 @@ void DoPurgeMessages(struct quickroom *qrbuf) { if ((xtime > 0L) && (now - xtime > (time_t)(epbuf.expire_value * 86400L))) { - lprintf(5, "Expiring message %ld\n", delnum); - AdjRefCount(delnum, -1); - msglist[a] = 0L; + fprintf(purgelist, "m=%ld\n", delnum); ++messages_purged; } } } - if (num_msgs > 0) { - num_msgs = sort_msglist(msglist, num_msgs); - } - - cdb_store(CDB_MSGLISTS, &qrbuf->QRnumber, sizeof(long), - msglist, (num_msgs * sizeof(long)) ); + if (msglist != NULL) free(msglist); +} - if (msglist != NULL) phree(msglist); - end_critical_section(S_QUICKROOM); +/* + * Second phase of message purge -- read list of msgs from temp file and + * delete them. + */ +void DoPurgeMessages(FILE *purgelist) { + char roomname[ROOMNAMELEN]; + long msgnum; + char buf[SIZ]; + + rewind(purgelist); + strcpy(roomname, "nonexistent room ___ ___"); + while (fgets(buf, sizeof buf, purgelist) != NULL) { + buf[strlen(buf)-1]=0; + if (!strncasecmp(buf, "r=", 2)) { + strcpy(roomname, &buf[2]); + } + if (!strncasecmp(buf, "m=", 2)) { + msgnum = atol(&buf[2]); + if (msgnum > 0L) { + CtdlDeleteMessages(roomname, &msgnum, 1, "", 0); + } + } + } } + void PurgeMessages(void) { - lprintf(5, "PurgeMessages() called\n"); + FILE *purgelist; + + lprintf(CTDL_DEBUG, "PurgeMessages() called\n"); messages_purged = 0; - ForEachRoom(DoPurgeMessages); + + purgelist = tmpfile(); + if (purgelist == NULL) { + lprintf(CTDL_CRIT, "Can't create purgelist temp file: %s\n", + strerror(errno)); + return; + } + + ForEachRoom(GatherPurgeMessages, (void *)purgelist ); + DoPurgeMessages(purgelist); + fclose(purgelist); } -void AddValidUser(struct usersupp *usbuf) { +void AddValidUser(struct ctdluser *usbuf, void *data) { struct ValidUser *vuptr; - vuptr = (struct ValidUser *)mallok(sizeof(struct ValidUser)); + vuptr = (struct ValidUser *)malloc(sizeof(struct ValidUser)); vuptr->next = ValidUserList; vuptr->vu_usernum = usbuf->usernum; ValidUserList = vuptr; } -void AddValidRoom(struct quickroom *qrbuf) { +void AddValidRoom(struct ctdlroom *qrbuf, void *data) { struct ValidRoom *vrptr; - vrptr = (struct ValidRoom *)mallok(sizeof(struct ValidRoom)); + vrptr = (struct ValidRoom *)malloc(sizeof(struct ValidRoom)); vrptr->next = ValidRoomList; vrptr->vr_roomnum = qrbuf->QRnumber; vrptr->vr_roomgen = qrbuf->QRgen; ValidRoomList = vrptr; } -void DoPurgeRooms(struct quickroom *qrbuf) { +void DoPurgeRooms(struct ctdlroom *qrbuf, void *data) { time_t age, purge_secs; struct PurgeList *pptr; struct ValidUser *vuptr; @@ -210,102 +268,139 @@ void DoPurgeRooms(struct quickroom *qrbuf) { * it. Bypass any other rules. */ if (qrbuf->QRflags & QR_MAILBOX) { + /* if user not found, do_purge will be 1 */ + do_purge = 1; for (vuptr=ValidUserList; vuptr!=NULL; vuptr=vuptr->next) { if (vuptr->vu_usernum == atol(qrbuf->QRname)) { do_purge = 0; - goto BYPASS; } } - /* user not found */ - do_purge = 1; - goto BYPASS; } - - /* Any of these attributes render a room non-purgable */ - if (qrbuf->QRflags & QR_PERMANENT) return; - if (qrbuf->QRflags & QR_DIRECTORY) return; - if (qrbuf->QRflags & QR_NETWORK) return; - if (is_noneditable(qrbuf)) return; - - /* If we don't know the modification date, be safe and don't purge */ - if (qrbuf->QRmtime <= (time_t)0) return; - - /* If no room purge time is set, be safe and don't purge */ - if (config.c_roompurge < 0) return; - - /* Otherwise, check the date of last modification */ - age = time(NULL) - (qrbuf->QRmtime); - purge_secs = (time_t)config.c_roompurge * (time_t)86400; - if (purge_secs <= (time_t)0) return; - lprintf(9, "<%s> is <%ld> seconds old\n", qrbuf->QRname, age); - if (age > purge_secs) do_purge = 1; - -BYPASS: if (do_purge) { - pptr = (struct PurgeList *) mallok(sizeof(struct PurgeList)); + else { + /* Any of these attributes render a room non-purgable */ + if (qrbuf->QRflags & QR_PERMANENT) return; + if (qrbuf->QRflags & QR_DIRECTORY) return; + if (qrbuf->QRflags & QR_NETWORK) return; + if (!strcasecmp(qrbuf->QRname, SYSCONFIGROOM)) return; + if (is_noneditable(qrbuf)) return; + + /* If we don't know the modification date, be safe and don't purge */ + if (qrbuf->QRmtime <= (time_t)0) return; + + /* If no room purge time is set, be safe and don't purge */ + if (config.c_roompurge < 0) return; + + /* Otherwise, check the date of last modification */ + age = time(NULL) - (qrbuf->QRmtime); + purge_secs = (time_t)config.c_roompurge * (time_t)86400; + if (purge_secs <= (time_t)0) return; + lprintf(CTDL_DEBUG, "<%s> is <%ld> seconds old\n", qrbuf->QRname, (long)age); + if (age > purge_secs) do_purge = 1; + } /* !QR_MAILBOX */ + + if (do_purge) { + pptr = (struct PurgeList *) malloc(sizeof(struct PurgeList)); pptr->next = RoomPurgeList; strcpy(pptr->name, qrbuf->QRname); RoomPurgeList = pptr; } } - + int PurgeRooms(void) { struct PurgeList *pptr; int num_rooms_purged = 0; - struct quickroom qrbuf; + struct ctdlroom qrbuf; struct ValidUser *vuptr; char *transcript = NULL; - lprintf(5, "PurgeRooms() called\n"); + lprintf(CTDL_DEBUG, "PurgeRooms() called\n"); /* Load up a table full of valid user numbers so we can delete * user-owned rooms for users who no longer exist */ - ForEachUser(AddValidUser); + ForEachUser(AddValidUser, NULL); /* Then cycle through the room file */ - ForEachRoom(DoPurgeRooms); + ForEachRoom(DoPurgeRooms, NULL); /* Free the valid user list */ while (ValidUserList != NULL) { vuptr = ValidUserList->next; - phree(ValidUserList); + free(ValidUserList); ValidUserList = vuptr; } - transcript = mallok(256); + transcript = malloc(SIZ); strcpy(transcript, "The following rooms have been auto-purged:\n"); while (RoomPurgeList != NULL) { if (getroom(&qrbuf, RoomPurgeList->name) == 0) { - transcript=reallok(transcript, strlen(transcript)+256); - sprintf(&transcript[strlen(transcript)], " %s\n", + transcript=realloc(transcript, strlen(transcript)+SIZ); + snprintf(&transcript[strlen(transcript)], SIZ, " %s\n", qrbuf.QRname); delete_room(&qrbuf); } pptr = RoomPurgeList->next; - phree(RoomPurgeList); + free(RoomPurgeList); RoomPurgeList = pptr; ++num_rooms_purged; } if (num_rooms_purged > 0) aide_message(transcript); - phree(transcript); + free(transcript); - lprintf(5, "Purged %d rooms.\n", num_rooms_purged); + lprintf(CTDL_DEBUG, "Purged %d rooms.\n", num_rooms_purged); return(num_rooms_purged); } -void do_user_purge(struct usersupp *us) { +/* + * Back end function to check user accounts for associated Unix accounts + * which no longer exist. + */ +void do_uid_user_purge(struct ctdluser *us, void *data) { +#ifdef ENABLE_AUTOLOGIN + struct PurgeList *pptr; + + if ((us->uid != (-1)) && (us->uid != CTDLUID)) { + if (getpwuid(us->uid) == NULL) { + pptr = (struct PurgeList *) + malloc(sizeof(struct PurgeList)); + pptr->next = UserPurgeList; + strcpy(pptr->name, us->fullname); + UserPurgeList = pptr; + } + } + +#endif /* ENABLE_AUTOLOGIN */ +} + + + +/* + * Back end function to check user accounts for expiration. + */ +void do_user_purge(struct ctdluser *us, void *data) { int purge; time_t now; time_t purge_time; struct PurgeList *pptr; + /* stupid recovery routine to re-create missing mailboxen. + * don't enable this. + struct ctdlroom qrbuf; + char mailboxname[ROOMNAMELEN]; + MailboxName(mailboxname, us, MAILROOM); + create_room(mailboxname, 4, "", 0, 1, 1, VIEW_BBS); + if (getroom(&qrbuf, mailboxname) != 0) return; + lprintf(CTDL_DEBUG, "Got %s\n", qrbuf.QRname); + */ + + /* Set purge time; if the user overrides the system default, use it */ if (us->USuserpurge > 0) { purge_time = ((time_t)us->USuserpurge) * 86400L; @@ -343,8 +438,13 @@ void do_user_purge(struct usersupp *us) { */ if (us->timescalled == 0) purge = 1; + /* User number 0, as well as any negative user number, is + * also impossible. + */ + if (us->usernum < 1L) purge = 1; + if (purge == 1) { - pptr = (struct PurgeList *) mallok(sizeof(struct PurgeList)); + pptr = (struct PurgeList *) malloc(sizeof(struct PurgeList)); pptr->next = UserPurgeList; strcpy(pptr->name, us->fullname); UserPurgeList = pptr; @@ -359,29 +459,33 @@ int PurgeUsers(void) { int num_users_purged = 0; char *transcript = NULL; - lprintf(5, "PurgeUsers() called\n"); + lprintf(CTDL_DEBUG, "PurgeUsers() called\n"); +#ifdef ENABLE_AUTOLOGIN + ForEachUser(do_uid_user_purge, NULL); +#else if (config.c_userpurge > 0) { - ForEachUser(do_user_purge); + ForEachUser(do_user_purge, NULL); } +#endif - transcript = mallok(256); + transcript = malloc(SIZ); strcpy(transcript, "The following users have been auto-purged:\n"); while (UserPurgeList != NULL) { - transcript=reallok(transcript, strlen(transcript)+256); - sprintf(&transcript[strlen(transcript)], " %s\n", + transcript=realloc(transcript, strlen(transcript)+SIZ); + snprintf(&transcript[strlen(transcript)], SIZ, " %s\n", UserPurgeList->name); purge_user(UserPurgeList->name); pptr = UserPurgeList->next; - phree(UserPurgeList); + free(UserPurgeList); UserPurgeList = pptr; ++num_users_purged; } if (num_users_purged > 0) aide_message(transcript); - phree(transcript); + free(transcript); - lprintf(5, "Purged %d users.\n", num_users_purged); + lprintf(CTDL_DEBUG, "Purged %d users.\n", num_users_purged); return(num_users_purged); } @@ -411,10 +515,10 @@ int PurgeVisits(void) { int RoomIsValid, UserIsValid; /* First, load up a table full of valid room/gen combinations */ - ForEachRoom(AddValidRoom); + ForEachRoom(AddValidRoom, NULL); /* Then load up a table full of valid user numbers */ - ForEachUser(AddValidUser); + ForEachUser(AddValidUser, NULL); /* Now traverse through the visits, purging irrelevant records... */ cdb_rewind(CDB_VISIT); @@ -444,7 +548,7 @@ int PurgeVisits(void) { /* Put the record on the purge list if it's dead */ if ((RoomIsValid==0) || (UserIsValid==0)) { vptr = (struct VPurgeList *) - mallok(sizeof(struct VPurgeList)); + malloc(sizeof(struct VPurgeList)); vptr->next = VisitPurgeList; vptr->vp_roomnum = vbuf.v_roomnum; vptr->vp_roomgen = vbuf.v_roomgen; @@ -457,14 +561,14 @@ int PurgeVisits(void) { /* Free the valid room/gen combination list */ while (ValidRoomList != NULL) { vrptr = ValidRoomList->next; - phree(ValidRoomList); + free(ValidRoomList); ValidRoomList = vrptr; } /* Free the valid user list */ while (ValidUserList != NULL) { vuptr = ValidUserList->next; - phree(ValidUserList); + free(ValidUserList); ValidUserList = vuptr; } @@ -476,65 +580,247 @@ int PurgeVisits(void) { VisitPurgeList->vp_usernum); cdb_delete(CDB_VISIT, IndexBuf, IndexLen); vptr = VisitPurgeList->next; - phree(VisitPurgeList); + free(VisitPurgeList); VisitPurgeList = vptr; ++purged; } - + return(purged); } +/* + * Purge the use table of old entries. + * + */ +int PurgeUseTable(void) { + int purged = 0; + struct cdbdata *cdbut; + struct UseTable ut; + struct UPurgeList *ul = NULL; + struct UPurgeList *uptr; + + /* Phase 1: traverse through the table, discovering old records... */ + lprintf(CTDL_DEBUG, "Purge use table: phase 1\n"); + cdb_rewind(CDB_USETABLE); + while(cdbut = cdb_next_item(CDB_USETABLE), cdbut != NULL) { + + memcpy(&ut, cdbut->ptr, + ((cdbut->len > sizeof(struct UseTable)) ? + sizeof(struct UseTable) : cdbut->len)); + cdb_free(cdbut); + + if ( (time(NULL) - ut.ut_timestamp) > USETABLE_RETAIN ) { + uptr = (struct UPurgeList *) malloc(sizeof(struct UPurgeList)); + if (uptr != NULL) { + uptr->next = ul; + safestrncpy(uptr->up_key, ut.ut_msgid, sizeof uptr->up_key); + ul = uptr; + } + ++purged; + } -void cmd_expi(char *argbuf) { - char cmd[256]; - int retval; - - - if ((!(CC->logged_in))&&(!(CC->internal_pgm))) { - cprintf("%d Not logged in.\n",ERROR+NOT_LOGGED_IN); - return; } - if ((!is_room_aide()) && (!(CC->internal_pgm)) ) { - cprintf("%d Higher access required.\n", - ERROR+HIGHER_ACCESS_REQUIRED); - return; + /* Phase 2: delete the records */ + lprintf(CTDL_DEBUG, "Purge use table: phase 2\n"); + while (ul != NULL) { + cdb_delete(CDB_USETABLE, ul->up_key, strlen(ul->up_key)); + uptr = ul->next; + free(ul); + ul = uptr; } - extract(cmd, argbuf, 0); - if (!strcasecmp(cmd, "users")) { - retval = PurgeUsers(); - cprintf("%d Purged %d users.\n", OK, retval); - return; + lprintf(CTDL_DEBUG, "Purge use table: finished (purged %d records)\n", purged); + return(purged); +} + + + +/* + * Purge the EUID Index of old records. + * + */ +int PurgeEuidIndexTable(void) { + int purged = 0; + struct cdbdata *cdbei; + struct EPurgeList *el = NULL; + struct EPurgeList *eptr; + long msgnum; + struct CtdlMessage *msg; + + /* Phase 1: traverse through the table, discovering old records... */ + lprintf(CTDL_DEBUG, "Purge EUID index: phase 1\n"); + cdb_rewind(CDB_EUIDINDEX); + while(cdbei = cdb_next_item(CDB_EUIDINDEX), cdbei != NULL) { + + memcpy(&msgnum, cdbei->ptr, sizeof(long)); + + msg = CtdlFetchMessage(msgnum, 0); + if (msg != NULL) { + CtdlFreeMessage(msg); /* it still exists, so do nothing */ + } + else { + eptr = (struct EPurgeList *) malloc(sizeof(struct EPurgeList)); + if (eptr != NULL) { + eptr->next = el; + eptr->ep_keylen = cdbei->len - sizeof(long); + eptr->ep_key = malloc(cdbei->len); + memcpy(eptr->ep_key, &cdbei->ptr[sizeof(long)], eptr->ep_keylen); + el = eptr; + } + ++purged; + } + + cdb_free(cdbei); + } - else if (!strcasecmp(cmd, "messages")) { - PurgeMessages(); - cprintf("%d Expired %d messages.\n", OK, messages_purged); - return; + + /* Phase 2: delete the records */ + lprintf(CTDL_DEBUG, "Purge euid index: phase 2\n"); + while (el != NULL) { + cdb_delete(CDB_EUIDINDEX, el->ep_key, el->ep_keylen); + free(el->ep_key); + eptr = el->next; + free(el); + el = eptr; } - else if (!strcasecmp(cmd, "rooms")) { - retval = PurgeRooms(); - cprintf("%d Expired %d rooms.\n", OK, retval); + + lprintf(CTDL_DEBUG, "Purge euid index: finished (purged %d records)\n", purged); + return(purged); +} + + +void purge_databases(void) { + int retval; + static time_t last_purge = 0; + time_t now; + struct tm tm; + + /* Do the auto-purge if the current hour equals the purge hour, + * but not if the operation has already been performed in the + * last twelve hours. This is usually enough granularity. + */ + now = time(NULL); + localtime_r(&now, &tm); + if (tm.tm_hour != config.c_purge_hour) return; + if ((now - last_purge) < 43200) return; + + lprintf(CTDL_INFO, "Auto-purger: starting.\n"); + + retval = PurgeUsers(); + lprintf(CTDL_NOTICE, "Purged %d users.\n", retval); + + PurgeMessages(); + lprintf(CTDL_NOTICE, "Expired %d messages.\n", messages_purged); + + retval = PurgeRooms(); + lprintf(CTDL_NOTICE, "Expired %d rooms.\n", retval); + + retval = PurgeVisits(); + lprintf(CTDL_NOTICE, "Purged %d visits.\n", retval); + + retval = PurgeUseTable(); + lprintf(CTDL_NOTICE, "Purged %d entries from the use table.\n", retval); + + retval = PurgeEuidIndexTable(); + lprintf(CTDL_NOTICE, "Purged %d entries from the EUID index.\n", retval); + + lprintf(CTDL_INFO, "Auto-purger: finished.\n"); + + last_purge = now; /* So we don't do it again soon */ +} + +/*****************************************************************************/ + + +void do_fsck_msg(long msgnum, void *userdata) { + struct ctdlroomref *ptr; + + ptr = (struct ctdlroomref *)malloc(sizeof(struct ctdlroomref)); + ptr->next = rr; + ptr->msgnum = msgnum; + rr = ptr; +} + +void do_fsck_room(struct ctdlroom *qrbuf, void *data) +{ + getroom(&CC->room, qrbuf->QRname); + CtdlForEachMessage(MSGS_ALL, 0L, NULL, NULL, NULL, do_fsck_msg, NULL); +} + +/* + * Check message reference counts + */ +void cmd_fsck(char *argbuf) { + long msgnum; + struct cdbdata *cdbmsg; + struct MetaData smi; + struct ctdlroomref *ptr; + int realcount; + + if (CtdlAccessCheck(ac_aide)) return; + + /* Lame way of checking whether anyone else is doing this now */ + if (rr != NULL) { + cprintf("%d Another FSCK is already running.\n", ERROR + RESOURCE_BUSY); return; } - else if (!strcasecmp(cmd, "visits")) { - retval = PurgeVisits(); - cprintf("%d Purged %d visits.\n", OK, retval); - } - else if (!strcasecmp(cmd, "defrag")) { - defrag_databases(); - cprintf("%d Defragmented the databases.\n", OK); + + cprintf("%d Checking message reference counts\n", LISTING_FOLLOWS); + + cprintf("\nThis could take a while. Please be patient!\n\n"); + cprintf("Gathering pointers...\n"); + ForEachRoom(do_fsck_room, NULL); + + get_control(); + cprintf("Checking message base...\n"); + for (msgnum = 0L; msgnum <= CitControl.MMhighest; ++msgnum) { + + cdbmsg = cdb_fetch(CDB_MSGMAIN, &msgnum, sizeof(long)); + if (cdbmsg != NULL) { + cdb_free(cdbmsg); + cprintf("Message %7ld ", msgnum); + + GetMetaData(&smi, msgnum); + cprintf("refcount=%-2d ", smi.meta_refcount); + + realcount = 0; + for (ptr = rr; ptr != NULL; ptr = ptr->next) { + if (ptr->msgnum == msgnum) ++realcount; + } + cprintf("realcount=%-2d\n", realcount); + + if ( (smi.meta_refcount != realcount) + || (realcount == 0) ) { + smi.meta_refcount = realcount; + PutMetaData(&smi); + AdjRefCount(msgnum, 0); /* deletes if needed */ + } + + } + } - else { - cprintf("%d Invalid command.\n", ERROR+ILLEGAL_VALUE); - return; + + cprintf("Freeing memory...\n"); + while (rr != NULL) { + ptr = rr->next; + free(rr); + rr = ptr; } + + cprintf("Done!\n"); + cprintf("000\n"); + } -char *Dynamic_Module_Init(void) + +/*****************************************************************************/ + +char *serv_expire_init(void) { - CtdlRegisterProtoHook(cmd_expi, "EXPI", "Expire old system objects"); + CtdlRegisterSessionHook(purge_databases, EVT_TIMER); + CtdlRegisterProtoHook(cmd_fsck, "FSCK", "Check message ref counts"); return "$Id$"; }