Index: scripts/slave/daemonizer.py |
diff --git a/scripts/slave/daemonizer.py b/scripts/slave/daemonizer.py |
index 2a75f98f7f4b9fda82b0ea80f3dfb21a5a1dce7d..288401ec9731c3feb423e198c1e1dd17a281bfca 100755 |
--- a/scripts/slave/daemonizer.py |
+++ b/scripts/slave/daemonizer.py |
@@ -12,7 +12,7 @@ USAGE: |
daemonizer.py [options] -- <script> [args] |
- - options are options to this script. Note, currently there are none! |
+ - options are options to this script. |
- script is the script to daemonize or run in the background |
- args are the arguments that one might want to pass the <script> |
""" |
@@ -20,12 +20,44 @@ USAGE: |
# TODO(sivachandra): Enhance this script by enforcing a protocol of |
# communication between the parent (this script) and the daemon script. |
+# TODO(bpastene): Improve file handling by adding flocks over file io |
+# as implemented in infra/libs/service_utils/_daemon_nix.py |
+ |
+import argparse |
+import logging |
import os |
+import signal |
import subprocess |
import sys |
-def daemonize(): |
+def restart(cmd, pid_file_path): |
+ # Check for the pid_file to see if the daemon's already running |
+ # and restart it if it is. |
+ if pid_file_path == None: |
+ logging.error('pid_file_path arg must be specified when ' |
+ 'restarting a daemon') |
+ return 1 |
+ try: |
+ with open(pid_file_path, 'r') as pid_file: |
+ pid = int(pid_file.readline()) |
+ except (IOError, ValueError): |
+ pid = None |
+ |
+ if pid: |
+ logging.info( |
+ "%s pid file already exists, attempting to kill process %d", |
+ pid_file_path, |
+ pid) |
+ try: |
+ os.kill(pid, signal.SIGTERM) |
+ except OSError: |
+ logging.exception("Unable to kill old daemon process") |
+ |
+ return daemonize(cmd, pid_file_path) |
+ |
+ |
+def daemonize(cmd, pid_file_path): |
"""This function is based on the Python recipe provided here: |
http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/ |
""" |
@@ -66,27 +98,64 @@ def daemonize(): |
os.dup2(so.fileno(), sys.stdout.fileno()) |
os.dup2(se.fileno(), sys.stderr.fileno()) |
+ proc = subprocess.Popen(cmd) |
-def print_usage(err_msg): |
- print >> sys.stderr, err_msg |
- sys.exit('Usage: daemonizer.py [options] -- arg0 [argN...]') |
+ # Write pid to file if applicable. |
+ if pid_file_path: |
+ try: |
+ with open(pid_file_path, 'w') as pid_file: |
+ pid_file.write('%s' % str(proc.pid)) |
+ except (IOError): |
+ logging.exception("Unable to write pid to file") |
+ proc.communicate() |
+ return proc.returncode |
-def main(): |
- try: |
- idx = sys.argv.index('--') |
- except ValueError: |
- print_usage('Separator -- not found') |
- cmd = sys.argv[idx+1:] |
- if not cmd: |
- print_usage('arg0 not specified for sub command') |
+def stop(pid_file_path): |
+ if pid_file_path == None: |
+ logging.error("pid_file_path arg must be specified when stopping a daemon") |
+ return 1 |
+ try: |
+ with open(pid_file_path) as pid_file: |
+ pid = int(pid_file.readline()) |
+ logging.info('Sending SIGTERM to %d', pid) |
+ os.kill(pid, signal.SIGTERM) |
+ os.remove(pid_file_path) |
+ except (IOError, OSError): |
+ logging.exception('Error terminating daemon process') |
- # TODO(sivachandra): When required in the future, use optparse to parse args |
- # from sys.argv[:idx] |
- daemonize() |
- return subprocess.call(cmd) |
+def main(): |
+ parser = argparse.ArgumentParser( |
+ description='Launch, or shutdown, a daemon process.') |
+ parser.add_argument( |
+ '--action', |
+ default='daemonize', |
+ choices=['restart','stop','daemonize'], |
+ help='What action to take. Both restart and stop attempt to write & read ' |
+ 'the pid to a file so it can kill or restart it, while daemonize simply ' |
+ 'fires and forgets.') |
+ parser.add_argument( |
+ '--pid-file-path', |
+ type=str, |
+ default=None, |
+ help='Path of tmp file to store the daemon\'s pid.') |
+ parser.add_argument( |
+ '--', dest='', |
+ required=False, |
+ help='Optional delimiter dividing daemonizer options with the command. ' |
+ 'This is here to ensure it\'s backwards compatible with the previous ' |
+ 'version of daemonizer.') |
+ parser.add_argument('cmd', help='Command (+ args) to daemonize', nargs='*') |
+ args = parser.parse_args() |
+ |
+ if args.action == 'restart': |
+ return restart(args.cmd, args.pid_file_path) |
+ elif args.action == 'daemonize': |
+ return daemonize(args.cmd, None) |
+ elif args.action == 'stop': |
+ return stop(args.pid_file_path) |
if __name__ == '__main__': |