psyced/world/net/library/admin.c

163 lines
5.0 KiB
C

// $Id: admin.c,v 1.27 2008/10/07 12:27:25 lynx Exp $ // vim:syntax=lpc
//
// admin functions and shutdown procedure
//
#include <net.h>
#include <proto.h>
#include <sandbox.h>
// 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