// File : /adm/daemons/purge.c // Creator : Watcher@TMI-2 (5/93) // // This daemon allows admins to delete user characters that are // older than the inputed integer time value. Wizards can be // deleted by defining flag = 1, and both the wizard and its // directory by defining flag = 2. If test if true, the directories // and user files will not be deleted, only flagged. #include <uid.h> #include <config.h> #include <daemons.h> #include <mudlib.h> // Purges work in the following way. Every subdirectory of P_DATA_DIR is // checked in succession. Every file in those subdirectories is checked // to see if it's a valid .o file, and if it is, when the user last // logged on. If it's NG, the user is removed. // This is a lot of file access, so it's all done by call_outs. There are // two parameters you can define: the time between each remove call, and // the time between each check of a directory. The default values are // 5 seconds per user remove and 300 seconds between directory sweeps: // this means that directory sweeps will not run simultanously unless // more than 60 users need to be purged from any one directory. It also // means a full sweep will take 26*300 = 7800 seconds, about two hours. #define USER_CALL_TIME 5 #define SWEEP_CALL_TIME 300 #define MAX_ERRORS 4 inherit DAEMON; static int sweep_dir(string dir, int when, int verbose, int flag, int test); static int fail_test(string dri, string what, int when, int flag); static int remove_user(string where, int verbose, int flag, int test); object link; int total, busy; int errors; string err; // Don't want this cleaning up with callouts pending. int clean_up() { if (find_call_out("sweep_dir")>-1 || find_call_out("remove_user")>-1) { return 1 ; } ::clean_up() ; } int process_users(int when, int verbose, int flag, int test) { string *main; int loop; // Check time length and caller's permissions. if(!when || !intp(when) || (geteuid(previous_object()) != ROOT_UID && !adminp(geteuid(previous_object())))) return 0; seteuid(getuid(this_object())); // Check to see if the daemon is presently in use ... If so // tell the caller to try again later (shouldn't really have // repeated calls anyways). if(busy) return 0; busy = 1; // If there isn't a this_player(), then any write()s will get // dumped to the syslog ... would be messy ... so block it. :) if(!this_player()) verbose = 0; // Check to make sure the flags are acceptable. if(flag < 0) flag = 0; if(flag > 2) flag = 2; // Create temporary link connection object for restoring data. link = new(CONNECTION); total = 0; // Get subdirectory listing from user save directory. main = get_dir( PDATA_DIR + "/" ); // For a test ; if(!main || !sizeof(main)) return 0; if(verbose) write(underscore("Initiating user character " + (test ? "test " : "") + "purge") + "\n" + "Last login prior to " + ctime(time()) + ".\n\n"); // Loop through directory contents using a catch() to catch // the "Too large evaluation" errors, then backup and do the // failed directory again until you finish it. for(loop=0; loop<sizeof(main); loop++) call_out( "sweep_dir", loop * SWEEP_CALL_TIME, PDATA_DIR + "/" + main[loop] + "/", when, verbose, flag, test ); /* if(err = catch(sweep_dir(PDATA_DIR + "/" + main[loop] + "/", when, verbose, flag, test))) { errors++; if( errors >= MAX_ERRORS ) { error( "Purge giving up.\n" ); return 0; } if(verbose) write("Error occurred: " + err + "\nRestarting subdir sweep.\n"); loop--; } errors = 0; } */ call_out( "alert", (loop+3)*SWEEP_CALL_TIME, verbose, test) ; return 1; } void alert( int verbose, int test ) { // We're all done ... destruct the working link. destruct(link); busy = 0; } // This function sweeps through each of the user save subdirectories // and check each user to see if they match the purge criteria. static int sweep_dir(string dir, int when, int verbose, int flag, int test) { string *contents; int loop, dinged; if(!directory_exists(dir)) return 0; if(verbose) write(bold("Sweeping: " + dir) + "\n"); dinged = 0 ; contents = get_dir(dir); if(!contents || !sizeof(contents)) return 0; // Loop through contents of the subdirectory check for compliance. for(loop=0; loop<sizeof(contents); loop++) { if(fail_test(dir, contents[loop], when, flag)) { dinged ++ ; call_out( "remove_user", dinged * USER_CALL_TIME, contents[loop], verbose, flag, test ); } } return 1; } static int fail_test(string dir, string what, int when, int flag) { string name; // Parse off the user's name ... if(!what || !file_exists(dir + what) || sscanf(what, "%s" + __SAVE_EXTENSION__, name) != 1) return 0; // Reset the link's properties for the next user. reload_object(link); seteuid(name); export_uid(link); seteuid(ROOT_UID); link->set("name", name); if(!link->restore()) return 0; if(!flag && (int)link->query("wizard")) return 0; if(to_int((int)link->query("last_on")) < when) return 1; return 0; } static int remove_user(string where, int verbose, int flag, int test) { string name, wiz_dir, tmp; sscanf(where, "%s" + __SAVE_EXTENSION__, name); wiz_dir = HOME_DIRS + extract(name, 0, 0) + "/" + name + "/"; total++; // Don't want to purge admins -- Rust if( adminp(name) ) return 0; tmp = capitalize(name); if(flag == 2 && directory_exists(wiz_dir)) { /* if(!test) CLEAN_D->clean_dir(wiz_dir); */ // Rather than delete the dirs, move them to /purged/name/blah // inspiral. if( !test ) "/adm/daemons/move_dir" -> clean_dir( wiz_dir ); tmp += " and " + wiz_dir + " " + (test ? "flagged" : "deleted") + ".\n"; } else tmp += " " + (test ? "flagged" : "deleted") + ".\n"; if(!test) { rm(DATA_DIR + "/std/user/" + name[0..0] + "/" + name + __SAVE_EXTENSION__); rm(PDATA_DIR + "/" + name[0..0] + "/" + name + __SAVE_EXTENSION__); } if(verbose) write(tmp); return 1; } int query_busy() { return busy; }