[IRC-Dev CVS] SF.net SVN: irc-dev:[198] ircd/trunk
Brought to you by:
zolty
From: <zo...@us...> - 2008-08-15 18:53:59
|
Revision: 198 http://irc-dev.svn.sourceforge.net/irc-dev/?rev=198&view=rev Author: zolty Date: 2008-08-15 18:54:05 +0000 (Fri, 15 Aug 2008) Log Message: ----------- Soporte de RESTART/DIE diferido Modified Paths: -------------- ircd/trunk/include/ircd.h ircd/trunk/include/send.h ircd/trunk/ircd/ddb.c ircd/trunk/ircd/engine_devpoll.c ircd/trunk/ircd/engine_epoll.c ircd/trunk/ircd/engine_kqueue.c ircd/trunk/ircd/engine_poll.c ircd/trunk/ircd/engine_select.c ircd/trunk/ircd/ircd.c ircd/trunk/ircd/ircd_signal.c ircd/trunk/ircd/m_die.c ircd/trunk/ircd/m_restart.c ircd/trunk/ircd/send.c Modified: ircd/trunk/include/ircd.h =================================================================== --- ircd/trunk/include/ircd.h 2008-08-15 18:11:20 UTC (rev 197) +++ ircd/trunk/include/ircd.h 2008-08-15 18:54:05 UTC (rev 198) @@ -45,6 +45,15 @@ int pid_fd; /**< File descriptor for process id file. */ }; +/** Describes pending exit. */ +struct PendingExit +{ + int restart; /**< Pending exit is for a restart. */ + char* who; /**< Who initiated the exit. */ + char* message; /**< Message to emit. */ + time_t time; /**< Absolute time at which to exit. */ +}; + /* * Macros */ @@ -61,13 +70,21 @@ #define MAJOR_PROTOCOL "10" /**< Current protocol version. */ #define BASE_VERSION "u2.10" /**< Base name of IRC daemon version. */ +#define PEND_INT_LONG 300 /**< Length of long message interval. */ +#define PEND_INT_MEDIUM 60 /**< Length of medium message interval. */ +#define PEND_INT_SHORT 30 /**< Length of short message interval. */ +#define PEND_INT_END 10 /**< Length of the end message interval. */ +#define PEND_INT_LAST 1 /**< Length of last message interval. */ + /* * Proto types */ -extern void server_die(const char* message); extern void server_panic(const char* message); -extern void server_restart(const char* message); +extern void exit_cancel(struct Client *who); +extern void exit_schedule(int restart, time_t when, struct Client *who, + const char *message); + extern struct Client me; extern time_t CurrentTime; extern struct Client* GlobalClientList; Modified: ircd/trunk/include/send.h =================================================================== --- ircd/trunk/include/send.h 2008-08-15 18:11:20 UTC (rev 197) +++ ircd/trunk/include/send.h 2008-08-15 18:54:05 UTC (rev 198) @@ -139,4 +139,7 @@ unsigned int mask, time_t *rate, const char *pattern, ...); +/* Send server notice to all local users */ +extern void sendto_lusers(const char *pattern, ...); + #endif /* INCLUDED_send_h */ Modified: ircd/trunk/ircd/ddb.c =================================================================== --- ircd/trunk/ircd/ddb.c 2008-08-15 18:11:20 UTC (rev 197) +++ ircd/trunk/ircd/ddb.c 2008-08-15 18:54:05 UTC (rev 198) @@ -244,6 +244,7 @@ sendto_opmask(0, SNO_OLDSNO, "Lo: %d Hashtable_Lo: %d Hi: %d Hashtable_Hi %d", lo, ddb_hashtable_lo[table], hi, ddb_hashtable_hi[table]); +#if 0 if ((ddb_hashtable_hi[table] != hi) || (ddb_hashtable_lo[table] != lo)) { struct DLink *lp; @@ -266,6 +267,7 @@ } } else +#endif { ddb_db_hash_write(table); @@ -744,7 +746,7 @@ else if (IsServer(acptr)) sendcmdto_one(&me, CMD_ERROR, acptr, ":Terminated by %s", exitmsg); } - server_die(exitmsg); + exit_schedule(0, 0, 0, exitmsg); } /** Finalizes the %DDB subsystem. Modified: ircd/trunk/ircd/engine_devpoll.c =================================================================== --- ircd/trunk/ircd/engine_devpoll.c 2008-08-15 18:11:20 UTC (rev 197) +++ ircd/trunk/ircd/engine_devpoll.c 2008-08-15 18:54:05 UTC (rev 198) @@ -316,7 +316,7 @@ timer_add(timer_init(&clear_error), error_clear, 0, TT_PERIODIC, ERROR_EXPIRE_TIME); else if (errors > DEVPOLL_ERROR_THRESHOLD) /* too many errors... */ - server_restart("too many /dev/poll errors"); + exit_schedule(1, 0, 0, ""too many /dev/poll errors"); } /* old code did a sleep(1) here; with usage these days, * that may be too expensive Modified: ircd/trunk/ircd/engine_epoll.c =================================================================== --- ircd/trunk/ircd/engine_epoll.c 2008-08-15 18:11:20 UTC (rev 197) +++ ircd/trunk/ircd/engine_epoll.c 2008-08-15 18:54:05 UTC (rev 198) @@ -278,7 +278,7 @@ timer_add(timer_init(&clear_error), error_clear, 0, TT_PERIODIC, ERROR_EXPIRE_TIME); else if (errors > EPOLL_ERROR_THRESHOLD) - server_restart("too many epoll errors"); + exit_schedule(1, 0, 0, "too many epoll errors"); } continue; } Modified: ircd/trunk/ircd/engine_kqueue.c =================================================================== --- ircd/trunk/ircd/engine_kqueue.c 2008-08-15 18:11:20 UTC (rev 197) +++ ircd/trunk/ircd/engine_kqueue.c 2008-08-15 18:54:05 UTC (rev 198) @@ -345,7 +345,7 @@ timer_add(timer_init(&clear_error), error_clear, 0, TT_PERIODIC, ERROR_EXPIRE_TIME); else if (errors > KQUEUE_ERROR_THRESHOLD) /* too many errors... */ - server_restart("too many kevent errors"); + exit_schedule(1, 0, 0, "too many kevent errors"); } /* old code did a sleep(1) here; with usage these days, * that may be too expensive Modified: ircd/trunk/ircd/engine_poll.c =================================================================== --- ircd/trunk/ircd/engine_poll.c 2008-08-15 18:11:20 UTC (rev 197) +++ ircd/trunk/ircd/engine_poll.c 2008-08-15 18:54:05 UTC (rev 198) @@ -303,7 +303,7 @@ timer_add(timer_init(&clear_error), error_clear, 0, TT_PERIODIC, ERROR_EXPIRE_TIME); else if (errors > POLL_ERROR_THRESHOLD) /* too many errors... */ - server_restart("too many poll errors"); + exit_schedule(1, 0, 0, "too many poll errors"); } /* old code did a sleep(1) here; with usage these days, * that may be too expensive Modified: ircd/trunk/ircd/engine_select.c =================================================================== --- ircd/trunk/ircd/engine_select.c 2008-08-15 18:11:20 UTC (rev 197) +++ ircd/trunk/ircd/engine_select.c 2008-08-15 18:54:05 UTC (rev 198) @@ -296,7 +296,7 @@ timer_add(timer_init(&clear_error), error_clear, 0, TT_PERIODIC, ERROR_EXPIRE_TIME); else if (errors > SELECT_ERROR_THRESHOLD) /* too many errors... */ - server_restart("too many select errors"); + exit_schedule(1, 0, 0, "too many select errors"); } /* old code did a sleep(1) here; with usage these days, * that may be too expensive Modified: ircd/trunk/ircd/ircd.c =================================================================== --- ircd/trunk/ircd/ircd.c 2008-08-15 18:11:20 UTC (rev 197) +++ ircd/trunk/ircd/ircd.c 2008-08-15 18:54:05 UTC (rev 198) @@ -40,6 +40,7 @@ #include "ircd_log.h" #include "ircd_reply.h" #include "ircd_signal.h" +#include "ircd_snprintf.h" #include "ircd_string.h" #include "jupe.h" #include "list.h" @@ -121,6 +122,7 @@ static struct Timer connect_timer; /**< timer structure for try_connections() */ static struct Timer ping_timer; /**< timer structure for check_pings() */ static struct Timer destruct_event_timer; /**< timer structure for exec_expired_destruct_events() */ +static struct Timer countdown_timer; /**< timer structure for exit_countdown() */ /** Daemon information. */ static struct Daemon thisServer = { 0, 0, 0, 0, 0, 0, -1 }; @@ -129,83 +131,290 @@ int running = 1; -/*---------------------------------------------------------------------------- - * API: server_die - *--------------------------------------------------------------------------*/ -/** Terminate the server with a message. - * @param[in] message Message to log and send to operators. +/** + * Perform a restart or die, sending and logging all necessary messages. + * @param[in] pe Pointer to structure describing pending exit. */ -void server_die(const char *message) +static void pending_exit(struct PendingExit *pe) { - /* log_write will send out message to both log file and as server notice */ - log_write(LS_SYSTEM, L_CRIT, 0, "Server terminating: %s", message); + static int looping = 0; + enum LogLevel level = pe->restart ? L_WARNING : L_CRIT; + const char *what = pe->restart ? "restarting" : "terminating"; + + if (looping++) /* increment looping to prevent looping */ + return; + + if (pe->message) { + sendto_lusers("Server %s: %s", what, pe->message); + + if (pe->who) { /* write notice to log */ + log_write(LS_SYSTEM, level, 0, "%s %s server: %s", pe->who, what, + pe->message); + sendcmdto_serv(&me, CMD_SQUIT, 0, "%s 0 :%s %s server: %s", + cli_name(&me), pe->who, what, pe->message); + } else { + log_write(LS_SYSTEM, level, 0, "Server %s: %s", what, pe->message); + sendcmdto_serv(&me, CMD_SQUIT, 0, "%s 0 :Server %s: %s", + cli_name(&me), what, pe->message); + } + } else { /* just notify of the restart/termination */ + sendto_lusers("Server %s...", what); + + if (pe->who) { /* write notice to log */ + log_write(LS_SYSTEM, level, 0, "%s %s server...", pe->who, what); + sendcmdto_serv(&me, CMD_SQUIT, 0, "%s 0 :%s %s server...", + cli_name(&me), pe->who, what); + } else { + log_write(LS_SYSTEM, level, 0, "Server %s...", what); + sendcmdto_serv(&me, CMD_SQUIT, 0, "%s 0 :Server %s...", + cli_name(&me), what); + } + } + + /* now let's perform the restart or exit */ flush_connections(0); - close_connections(1); - running = 0; - + #if defined(DDB) ddb_end(); #endif + + log_close(); + close_connections(!pe->restart || + !(thisServer.bootopt & (BOOT_TTY | BOOT_DEBUG | BOOT_CHKCONF))); + + if (!pe->restart) { /* just set running = 0 */ + running = 0; + return; + } + + /* OK, so we're restarting... */ + reap_children(); + + execv(SPATH, thisServer.argv); /* restart the server */ + + /* something failed; reopen the logs so we can complain */ + log_reopen(); + + log_write(LS_SYSTEM, L_CRIT, 0, "execv(%s,%s) failed: %m", SPATH, + *thisServer.argv); + + Debug((DEBUG_FATAL, "Couldn't restart server \"%s\": %s", SPATH, + (strerror(errno)) ? strerror(errno) : "")); + exit(8); } -/*---------------------------------------------------------------------------- - * API: server_panic - *--------------------------------------------------------------------------*/ -/** Immediately terminate the server with a message. - * @param[in] message Message to log, but not send to operators. +/** + * Issue server notice warning about impending restart or die. + * @param[in] pe Pointer to structure describing pending exit. + * @param[in] until How long until the exit (approximately). */ -void server_panic(const char *message) +static void countdown_notice(struct PendingExit *pe, time_t until) { - /* inhibit sending server notice--we may be panicking due to low memory */ - log_write(LS_SYSTEM, L_CRIT, LOG_NOSNOTICE, "Server panic: %s", message); - flush_connections(0); - log_close(); - close_connections(1); - exit(1); + const char *what = pe->restart ? "restarting" : "terminating"; + const char *units; + + if (until >= 60) { /* measure in minutes */ + until /= 60; /* so convert it to minutes */ + units = (until == 1) ? "minute" : "minutes"; + } else + units = (until == 1) ? "second" : "seconds"; + + /* send the message */ + if (pe->message) + sendto_lusers("Server %s in %d %s: %s", what, until, units, pe->message); + else + sendto_lusers("Server %s in %d %s...", what, until, units); +} + +static void exit_countdown(struct Event *ev); + +/** + * Performs a delayed pending exit, issuing server notices as appropriate. + * Reschedules exit_countdown() as needed. + * @param[in] ev Timer event. + */ +static void _exit_countdown(struct PendingExit *pe, int do_notice) +{ + time_t total, next, approx; + + if (CurrentTime >= pe->time) { /* time to do the exit */ + pending_exit(pe); + return; + } + + /* OK, we need to figure out how long to the next message and approximate + * how long until the actual exit. + */ + total = pe->time - CurrentTime; /* how long until exit */ + +#define t_adjust(interval, interval2) \ + do { \ + approx = next = total - (total % (interval)); \ + if (next >= total - (interval2)) { \ + next -= (interval); /* have to adjust next... */ \ + if (next < (interval)) /* slipped into next interval */ \ + next = (interval) - (interval2); \ + } else /* have to adjust approx... */ \ + approx += (interval); \ + } while (0) + + if (total > PEND_INT_LONG) /* in the long interval regime */ + t_adjust(PEND_INT_LONG, PEND_INT_MEDIUM); + else if (total > PEND_INT_MEDIUM) /* in the medium interval regime */ + t_adjust(PEND_INT_MEDIUM, PEND_INT_SHORT); + else if (total > PEND_INT_SHORT) /* in the short interval regime */ + t_adjust(PEND_INT_SHORT, PEND_INT_END); + else if (total > PEND_INT_END) /* in the end interval regime */ + t_adjust(PEND_INT_END, PEND_INT_LAST); + else if (total > PEND_INT_LAST) /* in the last message interval */ + t_adjust(PEND_INT_LAST, PEND_INT_LAST); + else { /* next event is to actually exit */ + next = 0; + approx = PEND_INT_LAST; + } + + /* convert next to an absolute timestamp */ + next = pe->time - next; + assert(next > CurrentTime); + + /* issue the warning notices... */ + if (do_notice) + countdown_notice(pe, approx); + + /* reschedule the timer... */ + timer_add(&countdown_timer, exit_countdown, pe, TT_ABSOLUTE, next); } -/*---------------------------------------------------------------------------- - * API: server_restart - *--------------------------------------------------------------------------*/ -/** Restart the server with a message. - * @param[in] message Message to log and send to operators. +/** + * Timer callback for _exit_countdown(). + * @param[in] ev Timer event. */ -void server_restart(const char *message) +static void exit_countdown(struct Event *ev) { - static int restarting = 0; + if (ev_type(ev) == ET_DESTROY) + return; /* do nothing with destroy events */ + + assert(ET_EXPIRE == ev_type(ev)); + + /* perform the event we were called to do */ + _exit_countdown(t_data(&countdown_timer), 1); +} - /* inhibit sending any server notices; we may be in a loop */ - log_write(LS_SYSTEM, L_WARNING, LOG_NOSNOTICE, "Restarting Server: %s", - message); - if (restarting++) /* increment restarting to prevent looping */ - return; +/** + * Cancel a pending exit. + * @param[in] who Client cancelling the impending exit. + */ +void exit_cancel(struct Client *who) +{ + const char *what; + struct PendingExit *pe; - sendto_opmask(0, SNO_OLDSNO, "Restarting server: %s", message); - Debug((DEBUG_NOTICE, "Restarting server...")); - flush_connections(0); + if (!t_onqueue(&countdown_timer)) + return; /* it's not running... */ + + pe = t_data(&countdown_timer); /* get the pending exit data */ + timer_del(&countdown_timer); /* delete the timer */ -#if defined(DDB) - ddb_end(); -#endif + if (who) { /* explicitly issued cancellation */ + /* issue a notice about the exit being canceled */ + sendto_lusers("Server %s CANCELED", + what = (pe->restart ? "restart" : "termination")); + + /* log the cancellation */ + if (IsUser(who)) + log_write(LS_SYSTEM, L_NOTICE, 0, "Server %s CANCELED by %s!%s@%s", what, + cli_name(who), cli_user(who)->username, cli_sockhost(who)); + else + log_write(LS_SYSTEM, L_NOTICE, 0, "Server %s CANCELED by %s", what, + cli_name(who)); + } + + /* release the pending exit structure */ + if (pe->who) + MyFree(pe->who); + if (pe->message) + MyFree(pe->message); + MyFree(pe); - log_close(); + /* Oh, and restore connections */ + refuse = 0; +} - close_connections(!(thisServer.bootopt & (BOOT_TTY | BOOT_DEBUG | BOOT_CHKCONF))); +/** + * Schedule a pending exit. Note that only real people issue delayed + * exits, so \a who should not be NULL if \a when is non-zero. + * @param[in] restart True if a restart is desired, false otherwise. + * @param[in] when Interval until the exit; 0 for immediate exit. + * @param[in] who Client issuing exit (or NULL). + * @param[in] message Message explaining exit. + */ +void exit_schedule(int restart, time_t when, struct Client *who, + const char *message) +{ + struct PendingExit *pe; - reap_children(); + /* first, let's cancel any pending exit */ + exit_cancel(0); + + /* now create a new pending exit */ + pe = MyMalloc(sizeof(struct PendingExit)); + pe->restart = restart; + + pe->time = when + CurrentTime; /* make time absolute */ + if (who) { /* save who issued it... */ + if (IsUser(who)) { + char nuhbuf[NICKLEN + USERLEN + HOSTLEN + 3]; + ircd_snprintf(0, nuhbuf, sizeof(nuhbuf), "%s!%s@%s", cli_name(who), + cli_user(who)->username, cli_sockhost(who)); + DupString(pe->who, nuhbuf); + } else + DupString(pe->who, cli_name(who)); + } else + pe->who = 0; + if (message) /* also save the message */ + DupString(pe->message, message); + else + pe->message = 0; - execv(SPATH, thisServer.argv); + /* let's refuse new connections... */ + refuse = 1; + + if (!when) { /* do it right now? */ + pending_exit(pe); + return; + } - /* Have to reopen since it has been closed above */ - log_reopen(); + assert(who); /* only people issue delayed exits */ + + /* issue a countdown notice... */ + countdown_notice(pe, when); - log_write(LS_SYSTEM, L_CRIT, 0, "execv(%s,%s) failed: %m", SPATH, - *thisServer.argv); - - Debug((DEBUG_FATAL, "Couldn't restart server \"%s\": %s", - SPATH, (strerror(errno)) ? strerror(errno) : "")); - exit(8); + /* log who issued the shutdown */ + if (pe->message) + log_write(LS_SYSTEM, L_NOTICE, 0, "Delayed server %s issued by %s: %s", + restart ? "restart" : "termination", pe->who, pe->message); + else + log_write(LS_SYSTEM, L_NOTICE, 0, "Delayed server %s issued by %s...", + restart ? "restart" : "termination", pe->who); + /* and schedule the timer */ + _exit_countdown(pe, 0); } + +/*---------------------------------------------------------------------------- + * API: server_panic + *--------------------------------------------------------------------------*/ +/** Immediately terminate the server with a message. + * @param[in] message Message to log, but not send to operators. + */ +void server_panic(const char *message) +{ + /* inhibit sending server notice--we may be panicking due to low memory */ + log_write(LS_SYSTEM, L_CRIT, LOG_NOSNOTICE, "Server panic: %s", message); + flush_connections(0); + log_close(); + close_connections(1); + exit(1); +} /*---------------------------------------------------------------------------- @@ -214,7 +423,7 @@ /** Handle out-of-memory condition. */ static void outofmemory(void) { Debug((DEBUG_FATAL, "Out of memory: restarting server...")); - server_restart("Out of Memory"); + exit_schedule(1, 0, 0, "Out of Memory"); } @@ -767,6 +976,7 @@ timer_add(timer_init(&connect_timer), try_connections, 0, TT_RELATIVE, 1); timer_add(timer_init(&ping_timer), check_pings, 0, TT_RELATIVE, 1); timer_add(timer_init(&destruct_event_timer), exec_expired_destruct_events, 0, TT_PERIODIC, 60); + timer_init(&countdown_timer); CurrentTime = time(NULL); Modified: ircd/trunk/ircd/ircd_signal.c =================================================================== --- ircd/trunk/ircd/ircd_signal.c 2008-08-15 18:11:20 UTC (rev 197) +++ ircd/trunk/ircd/ircd_signal.c 2008-08-15 18:54:05 UTC (rev 198) @@ -89,7 +89,7 @@ assert(SIGTERM == sig_signal(ev_signal(ev))); assert(SIGTERM == ev_data(ev)); - server_die("received signal SIGTERM"); + exit_schedule(0, 0, 0, "Received signal SIGTERM"); } /** Signal callback for SIGHUP. @@ -116,7 +116,7 @@ assert(SIGINT == sig_signal(ev_signal(ev))); assert(SIGINT == ev_data(ev)); - server_restart("caught signal: SIGINT"); + exit_schedule(1, 0, 0, "Received signal SIGINT"); } /** Allocate a child callback record. Modified: ircd/trunk/ircd/m_die.c =================================================================== --- ircd/trunk/ircd/m_die.c 2008-08-15 18:11:20 UTC (rev 197) +++ ircd/trunk/ircd/m_die.c 2008-08-15 18:54:05 UTC (rev 198) @@ -68,7 +68,7 @@ sendcmdto_one(&me, CMD_ERROR, acptr, ":Terminated by %s", get_client_name(sptr, HIDE_IP)); } - server_die("received DIE"); + exit_schedule(0, 0, 0, "Received DIE"); return 0; } Modified: ircd/trunk/ircd/m_restart.c =================================================================== --- ircd/trunk/ircd/m_restart.c 2008-08-15 18:11:20 UTC (rev 197) +++ ircd/trunk/ircd/m_restart.c 2008-08-15 18:54:05 UTC (rev 198) @@ -52,7 +52,7 @@ return send_reply(sptr, ERR_NOPRIVILEGES); log_write(LS_SYSTEM, L_NOTICE, 0, "Server RESTART by %#C", sptr); - server_restart("received RESTART"); + exit_schedule(1, 0, 0, "Received RESTART"); return 0; } Modified: ircd/trunk/ircd/send.c =================================================================== --- ircd/trunk/ircd/send.c 2008-08-15 18:11:20 UTC (rev 197) +++ ircd/trunk/ircd/send.c 2008-08-15 18:54:05 UTC (rev 198) @@ -895,3 +895,32 @@ msgq_clean(mb); } + +/** Send a server notice to all local users on this server. + * @param[in] pattern Format string for server notice. + */ +void sendto_lusers(const char *pattern, ...) +{ + struct VarData vd; + struct Client *cptr; + struct MsgBuf *mb; + int i; + + /* Build the message we're going to send... */ + vd.vd_format = pattern; + va_start(vd.vd_args, pattern); + mb = msgq_make(0, ":%s " MSG_NOTICE " * :*** Notice -- %v", cli_name(&me), + &vd); + va_end(vd.vd_args); + + /* send it along */ + for (i = 0; i <= HighestFd; i++) { + if (!(cptr = LocalClientArray[i]) || !IsUser(cptr)) + continue; /* skip empty slots... */ + + send_buffer(cptr, mb, 1); /* send with high priority */ + } + + msgq_clean(mb); /* clean up after ourselves */ +} + \ No newline at end of file This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |