From bf995da7affc04f5df998e8a529dc0edd15bed49 Mon Sep 17 00:00:00 2001 From: Amr Ali Date: Mon, 23 Dec 2013 09:06:55 +0200 Subject: [PATCH 01/10] Rewrite CLI options handling * Use argparse for option handling * Remove global variables and put them under an argparse namespace --- bin/huptime | 246 +++++++++++++++++++++------------------------------- 1 file changed, 101 insertions(+), 145 deletions(-) diff --git a/bin/huptime b/bin/huptime index a4d3f05..51c77f2 100755 --- a/bin/huptime +++ b/bin/huptime @@ -25,6 +25,9 @@ import re import copy import time import traceback +import argparse + +from functools import partial REALPATH = os.path.realpath(sys.argv[0]) BINDIR = os.path.dirname(REALPATH) @@ -35,135 +38,86 @@ SOFILE = os.path.join(LIBDIR, "huptime.so") # The version (injected by the build). VERSION = "@(VERSION)" -# Defaults. -STATUS = False -RESTART = False -STOP = False - -HUPTIME_MODE = "fork" -HUPTIME_MULTI = False -HUPTIME_REVIVE = False -HUPTIME_WAIT = False -HUPTIME_UNLINK = "" HUPTIME_DEBUG = False -MULTI_COUNT = 1 -MULTI_PIDS = [] - +# Defaults STOP_TIMEOUT = 10.0 - -def usage(): - print "usage: huptime [options] [--] " - print " or huptime [options] [--] --status " - print " or huptime [options] [--] --restart " - print " or huptime [options] [--] --stop " - print " or huptime --help" - print - print "where options are:" - print - print " --version Print the version and exit." - print " --fork Run using fork mode (exclusive of --exec)." - print " --exec Run using exec mode (exclusive of --fork)." - print " --revive Restart the process on exit." - print " --wait Wait for child processes to finish." - print " --multi= Run N processes (and wait for exit)." - print " This will enable SO_REUSEPORT (needs Linux 3.9+)." - print " --unlink= Unlink the given file on restart." - print " This is useful for pid files." - print " --debug Print debug output to stderr." - print " --timeout= Timeout between TERM and KILL for --stop." - print " The default is %2.2f seconds." % STOP_TIMEOUT - print - print "Huptime is distributed in the hope that it will be useful," - print "but WITHOUT ANY WARRANTY; without even the implied warranty of" - print "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the" - print "GNU General Public License for more details." +MULTI_COUNT = 1 def debug(msg): if HUPTIME_DEBUG: print "huptime %d: %s" % (os.getpid(), msg) +# Configure the command line parser + +description="Huptime is a tool for achieving zero downtime restarts." + +epilog = """\ +Huptime is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warrant of +MERCHANTABILITY or FITNESS FOR PARTICULAR PURPOSE. See the +GNU General Public License for more details. +""" + +parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description=description, + epilog=epilog) + +# Parser types + +def positive(type, value): + ivalue = type(value) + if ivalue < 0: + raise argparse.ArgumentTypeError("{0} is an invalid positive value".format(value)) + return ivalue + +# Miscellaneous arguments +parser.add_argument('-d', '--debug', action='store_true', + help='print debug output to stderr.') +parser.add_argument('--version', action='version', + version='%(prog)s ' + VERSION, + help='output version information and exit') + +# Mode arguments +mutex_group = parser.add_mutually_exclusive_group() +mutex_group.add_argument('-e', '--exec', action='store_true', dest='mexec', + help='run using exec mode') +mutex_group.add_argument('-f', '--fork', action='store_true', + help='run using fork mode') + +# Action arguments +mutex_group = parser.add_mutually_exclusive_group() +mutex_group.add_argument('--status', action='store_true', + help='check whether a process is still running') +mutex_group.add_argument('--restart', action='store_true', + help='send a SIGHUP signal to a running process') +mutex_group.add_argument('--stop', action='store_true', + help='send a SIGTERM signal to a running process') + +# Other arguments +parser.add_argument('-r', '--revive', action='store_true', + help='restart the process on exit') +parser.add_argument('-w', '--wait', action='store_true', + help='wait for child processes to finish') +parser.add_argument('-m', '--multi', type=partial(positive, int), + default=MULTI_COUNT, metavar='N', + help='run N processes and wait for exit') +parser.add_argument('-u', '--unlink', metavar='FILE', + help='unlink the given file on restart') +parser.add_argument('-t', '--timeout', type=partial(positive, float), + default=STOP_TIMEOUT, metavar='T', + help='timeout between TERM and KILL for --stop (default: {0})'.format( + STOP_TIMEOUT)) +parser.add_argument('command', nargs='+', + help='the command to be executed') + # Parse all options. -ARGS = sys.argv[1:] - -while len(ARGS) > 0: - - arg = ARGS[0] - - if arg.startswith("--"): - if "=" in arg: - arg, value = arg[2:].split("=", 1) - else: - arg, value = arg[2:], None - - if not arg: - break - elif arg == "exec" and not value: - HUPTIME_MODE = "exec" - elif arg == "fork" and not value: - HUPTIME_MODE = "fork" - elif arg == "multi" and value: - HUPTIME_MULTI = True - MULTI_COUNT = value - elif arg == "timeout" and value: - STOP_TIMEOUT = value - elif arg == "revive" and not value: - HUPTIME_REVIVE = True - elif arg == "wait" and not value: - HUPTIME_WAIT = True - elif arg == "debug" and not value: - HUPTIME_DEBUG = True - elif arg == "unlink" and value: - HUPTIME_UNLINK = value - elif arg == "help" and not value: - usage() - sys.exit(0) - elif arg == "status" and not value: - STATUS = True - elif arg == "restart" and not value: - RESTART = True - elif arg == "stop" and not value: - STOP = True - elif arg == "version" and not value: - print VERSION - sys.exit(0) - else: - usage() - sys.exit(1) - else: - # Non-option. - break - - # Move to the next option. - ARGS.pop(0) - -if len(ARGS) == 0: - usage() - sys.exit(0) - -try: - MULTI_COUNT = int(MULTI_COUNT) - if MULTI_COUNT <= 0: - raise ValueError() -except ValueError: - print "Invalid value for --multi (should be positive integer)." - sys.exit(1) - -try: - STOP_TIMEOUT = float(STOP_TIMEOUT) - if STOP_TIMEOUT < 0.0: - raise ValueError() -except ValueError: - print "Invalid value for --timeout (should be non-negative)." - sys.exit(1) - -if STATUS or RESTART or STOP: - - # Check that the user hasn't passed any - # options which we could consider invalid. - if len([x for x in (STATUS, RESTART, STOP) if x]) > 1: - print "Invalid options: can't specify multi of --status, --restart and --stop." - sys.exit(1) +ARGS = parser.parse_args() +HUPTIME_DEBUG = ARGS.debug +ARGS.mode = 'fork' if ARGS.fork else ('exec' if ARGS.mexec else 'fork') + +if ARGS.status or ARGS.restart or ARGS.stop: # Go through /proc/*/cmdline and find matches. # NOTE: Some interpretors may fudge the command @@ -171,6 +125,7 @@ if STATUS or RESTART or STOP: exact_matches = [] inter_matches = [] + command = ARGS.command for pid in os.listdir("/proc"): try: pid = int(pid) @@ -180,12 +135,12 @@ if STATUS or RESTART or STOP: cmd = open("/proc/%d/cmdline" % pid, 'r').read().split("\0") # An exact match. - if len(cmd) >= len(ARGS) and cmd[:len(ARGS)] == ARGS: + if len(cmd) >= len(command) and cmd[:len(command)] == command: exact_matches.append(pid) # Interpreter match. - elif (len(cmd) >= 1+len(ARGS) and cmd[1:1+len(ARGS)] == ARGS) or \ - (len(cmd) >= 2+len(ARGS) and cmd[2:2+len(ARGS)] == ARGS): + elif (len(cmd) >= 1+len(command) and cmd[1:1+len(command)] == command) or \ + (len(cmd) >= 2+len(command) and cmd[2:2+len(command)] == command): inter_matches.append(pid) except KeyboardInterrupt: sys.exit(1) @@ -211,19 +166,19 @@ if STATUS or RESTART or STOP: for pid in active_pids: try: - if STATUS: + if ARGS.status: print pid - elif RESTART: + elif ARGS.restart: debug("Restarting PID %d..." % pid) os.kill(pid, signal.SIGHUP) - elif STOP: + elif ARGS.stop: debug("Killing PID %d (TERM)..." % pid) os.kill(pid, signal.SIGTERM) except OSError: continue # Nothing more to do. - if STATUS: + if ARGS.status: sys.exit(0) # Block until the SIGHUP signal has been @@ -235,7 +190,7 @@ if STATUS or RESTART or STOP: while True: try: data = open("/proc/%d/status" % pid, 'r').read().split("\n") - if RESTART: + if ARGS.restart: is_restarted = False for line in data: m = re.match("SigBlk:\s*([0-9a-f]+)", line) @@ -249,9 +204,9 @@ if STATUS or RESTART or STOP: break if is_restarted: break - elif STOP: + elif ARGS.stop: now = time.time() - if now - start_time > STOP_TIMEOUT: + if now - start_time > ARGS.timeout: debug("Killing PID %d (KILL)..." % pid) os.kill(pid, signal.SIGKILL) start_time = now @@ -262,31 +217,32 @@ if STATUS or RESTART or STOP: debug("Restart complete for PID %d." % pid) else: - debug("Mode is %s." % HUPTIME_MODE) - debug("Unlink is %s." % HUPTIME_UNLINK) - debug("Multi is %s." % HUPTIME_MULTI) - debug("Revive is %s." % HUPTIME_REVIVE) - debug("Wait is %s." % HUPTIME_WAIT) + debug("Mode is %s." % ARGS.mode) + debug("Unlink is %s." % ARGS.unlink) + debug("Multi is %s." % ARGS.multi) + debug("Revive is %s." % ARGS.revive) + debug("Wait is %s." % ARGS.wait) ENV = copy.copy(os.environ) ENV["LD_PRELOAD"] = SOFILE - ENV["HUPTIME_DEBUG"] = str(HUPTIME_DEBUG).lower() - ENV["HUPTIME_MODE"] = HUPTIME_MODE - ENV["HUPTIME_UNLINK"] = HUPTIME_UNLINK - ENV["HUPTIME_MULTI"] = str(HUPTIME_MULTI).lower() - ENV["HUPTIME_REVIVE"] = str(HUPTIME_REVIVE).lower() - ENV["HUPTIME_WAIT"] = str(HUPTIME_WAIT).lower() + ENV["HUPTIME_DEBUG"] = str(ARGS.debug).lower() + ENV["HUPTIME_MODE"] = ARGS.mode + ENV["HUPTIME_UNLINK"] = ARGS.unlink + ENV["HUPTIME_MULTI"] = str(ARGS.multi).lower() + ENV["HUPTIME_REVIVE"] = str(ARGS.revive).lower() + ENV["HUPTIME_WAIT"] = str(ARGS.wait).lower() def do_exec(): try: - os.execvpe(ARGS[0], ARGS, ENV) + command = ARGS.command + os.execvpe(command[0], command, ENV) except Exception as e: sys.stderr.write("huptime: %s\n" % str(e)) - if HUPTIME_DEBUG: + if ARGS.debug: traceback.print_exc() sys.exit(1) - if MULTI_COUNT == 1: + if ARGS.multi == 1: # Execute our new process. do_exec() @@ -297,7 +253,7 @@ else: # and init scripts, we wait for the children # to complete. child_pids = [] - for _ in range(MULTI_COUNT): + for _ in range(ARGS.multi): pid = os.fork() if pid == 0: do_exec() From 7f1047d5ed4eb5669a12a3050cf2c3b6a13c7d11 Mon Sep 17 00:00:00 2001 From: Amr Ali Date: Mon, 23 Dec 2013 10:25:34 +0200 Subject: [PATCH 02/10] Add logging mechanism * Replace 'print' statements with the appropriate log calls * Replace sys.stderr calls with the appropriate log calls * Remove global variable HUPTIME_DEBUG --- bin/huptime | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/bin/huptime b/bin/huptime index 51c77f2..543fc5c 100755 --- a/bin/huptime +++ b/bin/huptime @@ -26,6 +26,7 @@ import copy import time import traceback import argparse +import logging from functools import partial @@ -38,15 +39,14 @@ SOFILE = os.path.join(LIBDIR, "huptime.so") # The version (injected by the build). VERSION = "@(VERSION)" -HUPTIME_DEBUG = False - # Defaults STOP_TIMEOUT = 10.0 MULTI_COUNT = 1 -def debug(msg): - if HUPTIME_DEBUG: - print "huptime %d: %s" % (os.getpid(), msg) +# Setup logging +logging.basicConfig(level=logging.INFO, + format="%(module)s %(process)d: %(message)s") +logger = logging.getLogger() # Configure the command line parser @@ -114,8 +114,9 @@ parser.add_argument('command', nargs='+', # Parse all options. ARGS = parser.parse_args() -HUPTIME_DEBUG = ARGS.debug ARGS.mode = 'fork' if ARGS.fork else ('exec' if ARGS.mexec else 'fork') +if ARGS.debug: + logger.setLevel(logging.DEBUG) if ARGS.status or ARGS.restart or ARGS.stop: @@ -148,9 +149,9 @@ if ARGS.status or ARGS.restart or ARGS.stop: continue if exact_matches: - debug("Found exact processes: %s" % exact_matches) + logger.debug("Found exact processes: %s" % exact_matches) if inter_matches: - debug("Found interpreter processes: %s" % inter_matches) + logger.debug("Found interpreter processes: %s" % inter_matches) # Kill the preferred process group in order # to do the restart. We grab the pids to block @@ -161,18 +162,18 @@ if ARGS.status or ARGS.restart or ARGS.stop: elif inter_matches: active_pids = inter_matches else: - print "No process found?" + logger.info('No process found?') sys.exit(1) for pid in active_pids: try: if ARGS.status: - print pid + logger.info(pid) elif ARGS.restart: - debug("Restarting PID %d..." % pid) + logger.debug("Restarting PID %d..." % pid) os.kill(pid, signal.SIGHUP) elif ARGS.stop: - debug("Killing PID %d (TERM)..." % pid) + logger.debug("Killing PID %d (TERM)..." % pid) os.kill(pid, signal.SIGTERM) except OSError: continue @@ -207,21 +208,21 @@ if ARGS.status or ARGS.restart or ARGS.stop: elif ARGS.stop: now = time.time() if now - start_time > ARGS.timeout: - debug("Killing PID %d (KILL)..." % pid) + logger.debug("Killing PID %d (KILL)..." % pid) os.kill(pid, signal.SIGKILL) start_time = now except KeyboardInterrupt: sys.exit(1) except: break - debug("Restart complete for PID %d." % pid) + logger.debug("Restart complete for PID %d." % pid) else: - debug("Mode is %s." % ARGS.mode) - debug("Unlink is %s." % ARGS.unlink) - debug("Multi is %s." % ARGS.multi) - debug("Revive is %s." % ARGS.revive) - debug("Wait is %s." % ARGS.wait) + logger.debug("Mode is %s." % ARGS.mode) + logger.debug("Unlink is %s." % ARGS.unlink) + logger.debug("Multi is %s." % ARGS.multi) + logger.debug("Revive is %s." % ARGS.revive) + logger.debug("Wait is %s." % ARGS.wait) ENV = copy.copy(os.environ) ENV["LD_PRELOAD"] = SOFILE @@ -237,9 +238,9 @@ else: command = ARGS.command os.execvpe(command[0], command, ENV) except Exception as e: - sys.stderr.write("huptime: %s\n" % str(e)) + logger.error(str(e)) if ARGS.debug: - traceback.print_exc() + logger.exception('Error while launching a new process') sys.exit(1) if ARGS.multi == 1: From 2c723e5d6c29795e3806a0cfaf7e8a487fed4353 Mon Sep 17 00:00:00 2001 From: Jack Danger Canty Date: Sun, 22 Dec 2013 06:47:00 -0800 Subject: [PATCH 03/10] Fix typo and $PID example in README --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f52a1c0..a4f89ae 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,7 @@ Basic Example In a terminal, run: huptime --exec python -m SimpleHTTPServer & - echo $! - -Record the result of the echo, we'll call it $PID. + PID=$! Then, in a second terminal: @@ -50,7 +48,7 @@ Compound this with the fact that many applications consist of many different small components (written using different languages and frameworks), and you've got yourself a headache. -Because of this complexity, one of first things people have to do is implement +Because of this complexity, one of the first things people have to do is implement a custom load balancing tier and a complex upgrade process. Although this is important at a certain scale, it shouldn't be that hard for simple services. It's crazy to add a whole new tier when the problem can be solved in a much From 40c440a9f19bb6f6a9aefc1b98696acb61603baa Mon Sep 17 00:00:00 2001 From: "Derek Chiang (Enchi Jiang)" Date: Mon, 23 Dec 2013 23:27:45 +0800 Subject: [PATCH 04/10] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a4f89ae..a4cca9e 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ Why? ---- With continuous deployment, software can be updated dozens, hundreds or even -thousands of times per day. It critical that service is not interrupted during +thousands of times per day. It is critical that service is not interrupted during upgrades. In an ideal world, all applications would support a mechanism for doing zero From ba54c48bdc11d265efec9d9f53fc3661d8bf3347 Mon Sep 17 00:00:00 2001 From: Adin Scannell Date: Mon, 23 Dec 2013 11:02:54 -0500 Subject: [PATCH 05/10] Fix example. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a4cca9e..e7bd910 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,9 @@ In a terminal, run: Then, in a second terminal: - while true; do curl http://localhost:8080 2>/dev/null || echo "fail"; done + while true; do curl http://localhost:8000 2>/dev/null || echo "fail"; done -Finally, in a third terminal: +Finally, in a third terminal (or back in the first): kill -HUP $PID From 945125457e6635a085efea970eca4813ed5e616d Mon Sep 17 00:00:00 2001 From: Adin Scannell Date: Mon, 23 Dec 2013 11:07:26 -0500 Subject: [PATCH 06/10] Fix header guards (fixes #2). TIL: Leading underscores followed by a capital letter is actually a reserved identifier. This replaces the header guards with a non-reserved identifier. --- src/fdinfo.h | 4 ++-- src/fdtable.h | 4 ++-- src/funcs.h | 4 ++-- src/impl.h | 4 ++-- src/stubs.h | 4 ++-- src/utils.h | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/fdinfo.h b/src/fdinfo.h index 6b5d388..09ace8f 100644 --- a/src/fdinfo.h +++ b/src/fdinfo.h @@ -19,8 +19,8 @@ * along with Huptime. If not, see . */ -#ifndef _FDINFO_H_ -#define _FDINFO_H_ +#ifndef HUPTIME_FDINFO_H +#define HUPTIME_FDINFO_H #include #include diff --git a/src/fdtable.h b/src/fdtable.h index 782d229..93cabc9 100644 --- a/src/fdtable.h +++ b/src/fdtable.h @@ -19,8 +19,8 @@ * along with Huptime. If not, see . */ -#ifndef _FDTABLE_H_ -#define _FDTABLE_H_ +#ifndef HUPTIME_FDTABLE_H +#define HUPTIME_FDTABLE_H #include "fdinfo.h" diff --git a/src/funcs.h b/src/funcs.h index 2322ba1..2771f88 100644 --- a/src/funcs.h +++ b/src/funcs.h @@ -19,8 +19,8 @@ * along with Huptime. If not, see . */ -#ifndef _FUNCS_H_ -#define _FUNCS_H_ +#ifndef HUPTIME_FUNCS_H +#define HUPTIME_FUNCS_H #include #include diff --git a/src/impl.h b/src/impl.h index 1535dd7..a3638d3 100644 --- a/src/impl.h +++ b/src/impl.h @@ -19,8 +19,8 @@ * along with Huptime. If not, see . */ -#ifndef _IMPL_H_ -#define _IMPL_H_ +#ifndef HUPTIME_IMPL_H +#define HUPTIME_IMPL_H #include "funcs.h" diff --git a/src/stubs.h b/src/stubs.h index e2b2041..89240d7 100644 --- a/src/stubs.h +++ b/src/stubs.h @@ -19,8 +19,8 @@ * along with Huptime. If not, see . */ -#ifndef _STUBS_H_ -#define _STUBS_H_ +#ifndef HUPTIME_STUBS_H +#define HUPTIME_STUBS_H #include "funcs.h" diff --git a/src/utils.h b/src/utils.h index 15c8e62..47cfff1 100644 --- a/src/utils.h +++ b/src/utils.h @@ -19,8 +19,8 @@ * along with Huptime. If not, see . */ -#ifndef _UTILS_H_ -#define _UTILS_H_ +#ifndef HUPTIME_UTILS_H +#define HUPTIME_UTILS_H #include #include From 386373e3c9f0b3180f6b2b6ecbc651835684e405 Mon Sep 17 00:00:00 2001 From: Amr Ali Date: Mon, 23 Dec 2013 18:21:36 +0200 Subject: [PATCH 07/10] Fix error setting env variable HYPTIME_UNLINK to None --- bin/huptime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/huptime b/bin/huptime index 543fc5c..4ff00d6 100755 --- a/bin/huptime +++ b/bin/huptime @@ -103,7 +103,7 @@ parser.add_argument('-w', '--wait', action='store_true', parser.add_argument('-m', '--multi', type=partial(positive, int), default=MULTI_COUNT, metavar='N', help='run N processes and wait for exit') -parser.add_argument('-u', '--unlink', metavar='FILE', +parser.add_argument('-u', '--unlink', metavar='FILE', default='', help='unlink the given file on restart') parser.add_argument('-t', '--timeout', type=partial(positive, float), default=STOP_TIMEOUT, metavar='T', From 3938b23235ea45b9dfb28acc5353b36ecaace009 Mon Sep 17 00:00:00 2001 From: Amr Ali Date: Mon, 23 Dec 2013 18:39:15 +0200 Subject: [PATCH 08/10] Remove short arguments support --- bin/huptime | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/bin/huptime b/bin/huptime index 4ff00d6..efab582 100755 --- a/bin/huptime +++ b/bin/huptime @@ -81,9 +81,9 @@ parser.add_argument('--version', action='version', # Mode arguments mutex_group = parser.add_mutually_exclusive_group() -mutex_group.add_argument('-e', '--exec', action='store_true', dest='mexec', +mutex_group.add_argument('--exec', action='store_true', dest='mexec', help='run using exec mode') -mutex_group.add_argument('-f', '--fork', action='store_true', +mutex_group.add_argument('--fork', action='store_true', help='run using fork mode') # Action arguments @@ -96,16 +96,16 @@ mutex_group.add_argument('--stop', action='store_true', help='send a SIGTERM signal to a running process') # Other arguments -parser.add_argument('-r', '--revive', action='store_true', +parser.add_argument('--revive', action='store_true', help='restart the process on exit') -parser.add_argument('-w', '--wait', action='store_true', +parser.add_argument('--wait', action='store_true', help='wait for child processes to finish') -parser.add_argument('-m', '--multi', type=partial(positive, int), +parser.add_argument('--multi', type=partial(positive, int), default=MULTI_COUNT, metavar='N', help='run N processes and wait for exit') -parser.add_argument('-u', '--unlink', metavar='FILE', default='', +parser.add_argument('--unlink', metavar='FILE', default='', help='unlink the given file on restart') -parser.add_argument('-t', '--timeout', type=partial(positive, float), +parser.add_argument('--timeout', type=partial(positive, float), default=STOP_TIMEOUT, metavar='T', help='timeout between TERM and KILL for --stop (default: {0})'.format( STOP_TIMEOUT)) From ec0657967558e522dbcedec6da81f5dca7cd2f5e Mon Sep 17 00:00:00 2001 From: Amr Ali Date: Mon, 23 Dec 2013 18:41:33 +0200 Subject: [PATCH 09/10] Assign 'true' or 'false' to HUPTIME_MULTI --- bin/huptime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/huptime b/bin/huptime index efab582..fa5fe80 100755 --- a/bin/huptime +++ b/bin/huptime @@ -229,7 +229,7 @@ else: ENV["HUPTIME_DEBUG"] = str(ARGS.debug).lower() ENV["HUPTIME_MODE"] = ARGS.mode ENV["HUPTIME_UNLINK"] = ARGS.unlink - ENV["HUPTIME_MULTI"] = str(ARGS.multi).lower() + ENV["HUPTIME_MULTI"] = 'true' if ARGS.multi > 1 else 'false' ENV["HUPTIME_REVIVE"] = str(ARGS.revive).lower() ENV["HUPTIME_WAIT"] = str(ARGS.wait).lower() From 46935f83ebe4083e0b02f5c2488c4465808a5033 Mon Sep 17 00:00:00 2001 From: Adin Scannell Date: Mon, 23 Dec 2013 12:08:57 -0500 Subject: [PATCH 10/10] Make --multi more useful by killing children when parent dies. This will automatically clean-up all the child processes spawned by --multi whenever the parent dies. This is much more useful for integration with a supervisor (like upstart) because you can easily kill the collection without needing to use killall or --stop. --- bin/huptime | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/bin/huptime b/bin/huptime index fa5fe80..786ca73 100755 --- a/bin/huptime +++ b/bin/huptime @@ -29,6 +29,7 @@ import argparse import logging from functools import partial +import ctypes REALPATH = os.path.realpath(sys.argv[0]) BINDIR = os.path.dirname(REALPATH) @@ -255,8 +256,22 @@ else: # to complete. child_pids = [] for _ in range(ARGS.multi): + parent_pid = os.getpid() pid = os.fork() if pid == 0: + # We setup a safe procedure here to ensure that the + # child will receive a SIGTERM when the parent exits. + libc = ctypes.CDLL("libc.so.6") + if libc: + # Setup the signal for the parent dying. + libc.prctl(1, signal.SIGTERM) + + # Check for a race condition. It's possible + # that the parent died between the fork() and + # the prtctl() above; we need to handle that. + if os.getppid() != parent_pid: + sys.exit(1) + do_exec() else: child_pids.append(pid)