mirror of
git://git.psyced.org/git/psyced
synced 2024-08-15 03:25:10 +00:00
162 lines
5 KiB
C
162 lines
5 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
|
|
|