// $Id: admin.c,v 1.27 2008/10/07 12:27:25 lynx Exp $ // vim:syntax=lpc // // admin functions and shutdown procedure // #include #include #include // amount of seconds per pass #ifndef TIME_SHUTDOWN # if DEBUG > 0 # define TIME_SHUTDOWN 2 # else # define TIME_SHUTDOWN 4 # endif #endif // this used to be notify_shutdown() in master.c, but we need to do this // before the backend actually terminates, and give it a little time to // shutdown communications sanely. so now the shutdown is a 3 pass process. // // pass 0: shutdown all entities // pass 1: shutdown all network circuits // pass 2: make sure everything is done and terminate // // beware that an external "killall -1 psyced" inevitabely performs only // the last pass, so we need to be able to clean up as best we can, so that // psyced gets half a chance to exit in a psyc-friendly way. it is therefore // not recommended to shutdown a psyc server by signal. please use /ciao or // implement a _request_shutdown you can trigger from say perlpsyc's tell. // varargs int server_shutdown(string reason, int restart, int pass) { object o; int i, errors; PROTECT("SERVER_SHUTDOWN") shutdown_in_progress++; if (master) master->notify_shutdown_first(shutdown_in_progress); else debug_message("Warning: Master object destructed?\n"); if (!reason || reason == "") { #ifdef DEFAULT_SHUTDOWN_REASON reason = DEFAULT_SHUTDOWN_REASON; #else reason = SERVER_HOST " is performing a quick full twist double " "salto backwards."; #endif } unless (pass) { // first pass P0(("Server %s requested by %O. Grace period: "+ TIME_SHUTDOWN * 2 +" secs.\n", restart? "RESTART": "SHUTDOWN", previous_object())) // see "psyced" script (was: muvelauncher) unless (restart) rm(DATA_PATH ".autorestart"); call_out(#'server_shutdown, TIME_SHUTDOWN, reason, restart, 1); } // we first quit all users.. then do the items foreach (o : objects_people()) { if (catch(o->reboot(reason, restart, pass))) errors++; } if (pass++) { // this walks thru the complete list of objects of the driver // and gives every stupid little object a chance to store its state. // this may one day change into something more strategic, but since // most PSYC objects do have something to save, this does a good job! #if __EFUN_DEFINED__(debug_info) for(i=0; o = debug_info(2,i); i++) { // we skip the tcp links, as the other objects will need them unless (interactive(o)) { // will output error anyway, but not stop loop from running P2(("%O->reboot(%O, %O, %O)\n", o, reason, restart, pass)) if (catch(o->reboot(reason, restart, pass))) errors++; } } #endif // we use this because remove_player() comes too late to properly // terminate any protocols and send any notifications // TODO: rewrite this with a foreach catch! pass++; P2(("%O->reboot(%O, %O, %O)\n", users(), reason, restart, pass)) #ifdef __LPC_ARRAY_CALLS__ // this is actually imperfect, as a bug in one user breaks the loop if (catch(users()->reboot(reason, restart, pass))) errors++; #else # if __EFUN_DEFINED__(lambda) && __EFUN_DEFINED__(filter) // same problem here filter(users(), lambda(({'u}), ({ #'call_other, 'u, "reboot", reason, restart, pass }) //' )); # else # echo Warning: No neat shutdown function. # endif #endif if (pass == 3) { call_out(#'shutdown, TIME_SHUTDOWN); save_object(DATA_PATH "library"); } else { P0(("server_shutdown ended at pass %O\n", pass)) } } P2(("Errors during shutdown: %O\n", errors)) return errors; } varargs void shout(mixed who, string what, string text, mapping vars) { PROTECT("SHOUT") unless(mappingp(vars)) vars = ([]); #if 0 //def __LPC_ARRAY_CALLS__ // we can't use this cuz we need to copy(vars) for each objects_people() -> msg(who, what, text, vars); #else # if __EFUN_DEFINED__(lambda) && (__EFUN_DEFINED__(filter_array) || __EFUN_DEFINED__(filter)) # if __EFUN_DEFINED__(filter) filter(objects_people(), lambda(({'u}), ({#'call_other, 'u, "msg", who, to_string(what), to_string(text), ({ #'copy, vars }) }) )); # else filter_array(objects_people(), lambda(({'u}), ({#'call_other, 'u, "msg", who, to_string(what), to_string(text), ({ #'copy, vars }) }) )); # endif // # else // # echo shout() admin function disabled on this driver # endif #endif } #ifndef PRO_PATH // boss commands belong into an appropriate commander daemon meguesses // anyway, they get called by bossy user objects - but also by the httpd // int boss_command(string cmd, string args) { object o; PROTECT("BOSS_COMMAND") switch(cmd) { case "shutdown": // "Server shutting down. Please don't cry." // shout(0, "_notice_broadcast_shutdown", // "Server shutdown: [_reason]", ([ "_reason": args ])); server_shutdown(args, 0); return 1; case "ciao": case "hasta": case "reboot": case "restart": // "Server restarting. Fasten seat belts." // shout(0, "_notice_broadcast_shutdown_restart", // "Server restart: [_reason]", ([ "_reason": args ])); server_shutdown(args, 1); return 1; } return 0; } #endif