Changeset f6ca6a5


Ignore:
Timestamp:
Jan 8, 2012 1:15:47 PM (9 years ago)
Author:
zzz <zzz@…>
Branches:
master
Children:
5a4f206
Parents:
56a6772
Message:
  • Router shutdown:
    • Fix failsafe shutdown hook broken in 0.8.8; HUP, INT, and TERM signals should now shut down cleanly.
    • Shutdown hook no longer prevents other hooks from running
    • Trap HUP, if router.gracefulHUP=true, and do graceful shutdown. Only under wrapper, non-Windows.
    • i2prouter stop now uses SIGTERM
    • Implement i2prouter graceful using SIGHUP (ticket #580)
    • Configure wrapper to ignore SIGUSR1 and SIGUSR2 as they will shut down or crash the JVM
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • apps/routerconsole/java/src/net/i2p/router/web/ConfigServiceHandler.java

    r56a6772 rf6ca6a5  
    99import net.i2p.router.RouterContext;
    1010import net.i2p.router.startup.ClientAppConfig;
     11import net.i2p.util.Log;
    1112
    1213import org.tanukisoftware.wrapper.WrapperManager;
     14import org.tanukisoftware.wrapper.event.WrapperControlEvent;
     15import org.tanukisoftware.wrapper.event.WrapperEvent;
     16import org.tanukisoftware.wrapper.event.WrapperEventListener;
    1317
    1418/**
     
    1923public class ConfigServiceHandler extends FormHandler {
    2024   
     25    private static WrapperEventListener _signalHandler;
     26
     27    private static final String PROP_GRACEFUL_HUP = "router.gracefulHUP";
     28
    2129    /**
    2230     *  Register two shutdown hooks, one to rekey and/or tell the wrapper we are stopping,
     
    125133        public boolean equals(Object o) {
    126134            return (o != null) && (o instanceof FinalWrapperTask);
     135        }
     136    }
     137
     138    /**
     139     *  Register a handler for signals,
     140     *  so we can handle HUP from the wrapper (non-Windows only)
     141     *
     142     *  @since 0.8.13
     143     */
     144    synchronized static void registerSignalHandler(RouterContext ctx) {
     145        if (ctx.hasWrapper() && _signalHandler == null &&
     146            !System.getProperty("os.name").startsWith("Win")) {
     147           _signalHandler = new SignalHandler(ctx);
     148           long mask = WrapperEventListener.EVENT_FLAG_CONTROL;
     149           WrapperManager.addWrapperEventListener(_signalHandler, mask);
     150        }
     151    }
     152
     153    /**
     154     *  Unregister the handler for signals
     155     *
     156     *  @since 0.8.13
     157     */
     158    public synchronized static void unregisterSignalHandler() {
     159        if (_signalHandler != null) {
     160           WrapperManager.removeWrapperEventListener(_signalHandler);
     161           _signalHandler = null;
     162        }
     163    }
     164
     165    /**
     166     *  Catch signals.
     167     *  The wrapper will potentially forward HUP, USR1, and USR2.
     168     *  But USR1 and USR2 are used by the JVM GC and cannot be trapped.
     169     *  So we will only get HUP.
     170     *
     171     *  @since 0.8.13
     172     */
     173    private static class SignalHandler implements WrapperEventListener {
     174        private final RouterContext _ctxt;
     175
     176        public SignalHandler(RouterContext ctx) {
     177            _ctxt = ctx;
     178        }
     179
     180        public void fired(WrapperEvent event) {
     181            if (!(event instanceof WrapperControlEvent))
     182                return;
     183            WrapperControlEvent wce = (WrapperControlEvent) event;
     184            Log log = _ctxt.logManager().getLog(ConfigServiceHandler.class);
     185            if (log.shouldLog(Log.WARN))
     186                log.warn("Got signal: " + wce.getControlEventName());
     187            int sig = wce.getControlEvent();
     188            switch (sig) {
     189              case WrapperManager.WRAPPER_CTRL_HUP_EVENT:
     190                if (_ctxt.getBooleanProperty(PROP_GRACEFUL_HUP)) {
     191                    wce.consume();
     192                    if (!(_ctxt.router().gracefulShutdownInProgress() ||
     193                          _ctxt.router().isFinalShutdownInProgress())) {
     194                        System.err.println("WARN: Graceful shutdown initiated by SIGHUP");
     195                        log.logAlways(Log.WARN, "Graceful shutdown initiated by SIGHUP");
     196                        registerWrapperNotifier(_ctxt, Router.EXIT_GRACEFUL, false);
     197                        _ctxt.router().shutdownGracefully();
     198                    }
     199                } else {
     200                    log.log(Log.CRIT, "Hard shutdown initiated by SIGHUP");
     201                    // JVM will call ShutdownHook if we don't do it ourselves
     202                    //wce.consume();
     203                    //registerWrapperNotifier(_ctxt, Router.EXIT_HARD, false);
     204                    //_ctxt.router().shutdown(Router.EXIT_HARD);
     205                }
     206                break;
     207            }
    127208        }
    128209    }
     
    195276        }
    196277    }
     278
    197279    private void uninstallService() {
    198280        try {
  • apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java

    r56a6772 rf6ca6a5  
    368368            // stat summarizer registers its own hook
    369369            ctx.addShutdownTask(new ServerShutdown());
     370            ConfigServiceHandler.registerSignalHandler(ctx);
    370371        } // else log CRIT ?
    371372    }
  • installer/resources/i2prouter

    r56a6772 rf6ca6a5  
    10131013}
    10141014
     1015
    10151016stopit() {
    10161017    # $1 exit if down flag
     
    10291030        then
    10301031            # Running so try to stop it.
    1031             kill $pid
     1032            kill -TERM $pid
    10321033            if [ $? -ne 0 ]
    10331034            then
     
    10771078        else
    10781079            eval echo `gettext 'Stopped $APP_LONG_NAME.'`
     1080        fi
     1081    fi
     1082}
     1083
     1084graceful() {
     1085    # $1 exit if down flag
     1086
     1087    eval echo `gettext 'Stopping $APP_LONG_NAME gracefully...'`
     1088    getpid
     1089    if [ "X$pid" = "X" ]
     1090    then
     1091        eval echo `gettext '$APP_LONG_NAME was not running.'`
     1092        if [ "X$1" = "X1" ]
     1093        then
     1094            exit 1
     1095        fi
     1096    else
     1097        if [ "X$IGNORE_SIGNALS" = "X" ]
     1098        then
     1099            # Running so try to stop it.
     1100            # This sends HUP. router.gracefulHUP must be set in router.config,
     1101            # or else this will do the same as stop.
     1102            kill $pid
     1103            if [ $? -ne 0 ]
     1104            then
     1105                # An explanation for the failure should have been given
     1106                eval echo `gettext 'Unable to stop $APP_LONG_NAME.'`
     1107                exit 1
     1108            fi
     1109        else
     1110            rm -f "$ANCHORFILE"
     1111            if [ -f "$ANCHORFILE" ]
     1112            then
     1113                # An explanation for the failure should have been given
     1114                eval echo `gettext 'Unable to stop $APP_LONG_NAME.'`
     1115                exit 1
     1116            fi
    10791117        fi
    10801118    fi
     
    15581596            echo "`gettext '  start        Start in the background as a daemon process.'`"
    15591597            echo "`gettext '  stop         Stop if running as a daemon or in another console.'`"
     1598            echo "`gettext '  graceful     Stop gracefully, may take up to 11 minutes.'`"
    15601599            echo "`gettext '  restart      Stop if running and then start.'`"
    15611600            echo "`gettext '  condrestart  Restart only if already running.'`"
     
    16251664            ;;
    16261665
     1666        'graceful')
     1667            checkUser "" "$COMMAND"
     1668            graceful "0"
     1669            ;;
     1670
    16271671        'restart')
    16281672            checkUser touchlock "$COMMAND"
  • installer/resources/wrapper.config

    r56a6772 rf6ca6a5  
    168168# Log Level for sys/event log output.  (See docs for log levels)
    169169wrapper.syslog.loglevel=NONE
     170
     171# these will shut down or crash the JVM
     172wrapper.signal.mode.usr1=IGNORE
     173wrapper.signal.mode.usr2=IGNORE
    170174
    171175# choose what to do if the JVM kills itself based on the exit code
  • router/java/src/net/i2p/router/Router.java

    r56a6772 rf6ca6a5  
    234234            _config.put("router.firstInstalled", now);
    235235            _config.put("router.updateLastInstalled", now);
     236            // only compatible with new i2prouter script
     237            _config.put("router.gracefulHUP", "true");
    236238            saveConfig();
    237239        }
     
    377379        _started = _context.clock().now();
    378380        try {
    379             Runtime.getRuntime().removeShutdownHook(_shutdownHook);
     381            Runtime.getRuntime().addShutdownHook(_shutdownHook);
    380382        } catch (IllegalStateException ise) {}
    381383        I2PThread.addOOMEventListener(_oomListener);
     
    988990    /**
    989991     *  Cancel the JVM runtime hook before calling this.
     992     *  Called by the ShutdownHook.
    990993     *  NOT to be called by others, use shutdown().
    991994     */
    992995    public void shutdown2(int exitCode) {
     996        _shutdownInProgress = true;
     997        _log.log(Log.CRIT, "Starting final shutdown(" + exitCode + ')');
    993998        // So we can get all the way to the end
    994999        // No, you can't do Thread.currentThread.setDaemon(false)
     
    10051010        // Maybe we need a delay after this too?
    10061011        for (Runnable task : _context.getShutdownTasks()) {
     1012            //System.err.println("Running shutdown task " + task.getClass());
    10071013            if (_log.shouldLog(Log.WARN))
    10081014                _log.warn("Running shutdown task " + task.getClass());
     
    10991105            // allow the Runtime shutdown hooks to execute
    11001106            Runtime.getRuntime().exit(exitCode);
    1101         } else {
     1107        } else if (System.getProperty("java.vendor").contains("Android")) {
    11021108            Runtime.getRuntime().gc();
    11031109        }
  • router/java/src/net/i2p/router/tasks/ShutdownHook.java

    r56a6772 rf6ca6a5  
    3333        Log l = _context.logManager().getLog(Router.class);
    3434        l.log(Log.CRIT, "Shutting down the router...");
     35        // Needed to make the wrapper happy, otherwise it gets confused
     36        // and thinks we haven't shut down, possibly because it
     37        // prevents other shutdown hooks from running
     38        _context.router().setKillVMOnEnd(false);
    3539        _context.router().shutdown2(Router.EXIT_HARD);
    3640    }
Note: See TracChangeset for help on using the changeset viewer.