Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(93)

Side by Side Diff: scripts/slave/daemonizer.py

Issue 1328623004: Add logic to daemonizer to be able to kill what it daemonizes (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/build.git@master
Patch Set: Parent of daemon waits again Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 # Copyright 2013 The Chromium Authors. All rights reserved. 2 # Copyright 2013 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """ 6 """
7 This script can daemonize another script. This is usefull in running 7 This script can daemonize another script. This is usefull in running
8 background processes from recipes. Lookat chromium_android module for 8 background processes from recipes. Lookat chromium_android module for
9 example. 9 example.
10 10
11 USAGE: 11 USAGE:
12 12
13 daemonizer.py [options] -- <script> [args] 13 daemonizer.py [options] -- <script> [args]
14 14
15 - options are options to this script. Note, currently there are none! 15 - options are options to this script.
16 - script is the script to daemonize or run in the background 16 - script is the script to daemonize or run in the background
17 - args are the arguments that one might want to pass the <script> 17 - args are the arguments that one might want to pass the <script>
18 """ 18 """
19 19
20 # TODO(sivachandra): Enhance this script by enforcing a protocol of 20 # TODO(sivachandra): Enhance this script by enforcing a protocol of
21 # communication between the parent (this script) and the daemon script. 21 # communication between the parent (this script) and the daemon script.
22 22
23 # TODO(bpastene): Improve file handling by adding flocks over file io
24 # as implemented in infra/libs/service_utils/_daemon_nix.py
25
26 import argparse
27 import logging
23 import os 28 import os
29 import signal
24 import subprocess 30 import subprocess
25 import sys 31 import sys
26 32
27 33
28 def daemonize(): 34 def restart(cmd, pid_file_path):
35 # Check for the pid_file to see if the daemon's already running
36 # and restart it if it is.
37 if pid_file_path == None:
38 logging.error('pid_file_path arg must be specified when '
39 'restarting a daemon')
40 return 1
41 try:
42 with open(pid_file_path, 'r') as pid_file:
43 pid = int(pid_file.readline())
44 except (IOError, ValueError):
45 pid = None
46
47 if pid:
48 logging.info(
49 "%s pid file already exists, attempting to kill process %d",
50 pid_file_path,
51 pid)
52 try:
53 os.kill(pid, signal.SIGTERM)
54 except OSError:
55 logging.exception("Unable to kill old daemon process")
56
57 return daemonize(cmd, pid_file_path)
58
59
60 def daemonize(cmd, pid_file_path):
29 """This function is based on the Python recipe provided here: 61 """This function is based on the Python recipe provided here:
30 http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/ 62 http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/
31 """ 63 """
32 # Spawn a detached child process. 64 # Spawn a detached child process.
33 try: 65 try:
34 pid = os.fork() 66 pid = os.fork()
35 if pid > 0: 67 if pid > 0:
36 # exit first parent 68 # exit first parent
37 sys.exit(0) 69 sys.exit(0)
38 except OSError, e: 70 except OSError, e:
(...skipping 20 matching lines...) Expand all
59 # redirect standard file descriptors 91 # redirect standard file descriptors
60 sys.stdout.flush() 92 sys.stdout.flush()
61 sys.stderr.flush() 93 sys.stderr.flush()
62 si = file('/dev/null', 'r') 94 si = file('/dev/null', 'r')
63 so = file('/dev/null', 'a+') 95 so = file('/dev/null', 'a+')
64 se = file('/dev/null', 'a+', 0) 96 se = file('/dev/null', 'a+', 0)
65 os.dup2(si.fileno(), sys.stdin.fileno()) 97 os.dup2(si.fileno(), sys.stdin.fileno())
66 os.dup2(so.fileno(), sys.stdout.fileno()) 98 os.dup2(so.fileno(), sys.stdout.fileno())
67 os.dup2(se.fileno(), sys.stderr.fileno()) 99 os.dup2(se.fileno(), sys.stderr.fileno())
68 100
101 proc = subprocess.Popen(cmd)
69 102
70 def print_usage(err_msg): 103 # Write pid to file if applicable.
71 print >> sys.stderr, err_msg 104 if pid_file_path:
72 sys.exit('Usage: daemonizer.py [options] -- arg0 [argN...]') 105 try:
106 with open(pid_file_path, 'w') as pid_file:
107 pid_file.write('%s' % str(proc.pid))
108 except (IOError):
109 logging.exception("Unable to write pid to file")
110
111 proc.communicate()
112 return proc.returncode
113
114
115 def stop(pid_file_path):
116 if pid_file_path == None:
117 logging.error("pid_file_path arg must be specified when stopping a daemon")
118 return 1
119 try:
120 with open(pid_file_path) as pid_file:
121 pid = int(pid_file.readline())
122 logging.info('Sending SIGTERM to %d', pid)
123 os.kill(pid, signal.SIGTERM)
124 os.remove(pid_file_path)
125 except (IOError, OSError):
126 logging.exception('Error terminating daemon process')
73 127
74 128
75 def main(): 129 def main():
76 try: 130 parser = argparse.ArgumentParser(
77 idx = sys.argv.index('--') 131 description='Launch, or shutdown, a daemon process.')
78 except ValueError: 132 parser.add_argument(
79 print_usage('Separator -- not found') 133 '--action',
134 default='daemonize',
135 choices=['restart','stop','daemonize'],
136 help='What action to take. Both restart and stop attempt to write & read '
137 'the pid to a file so it can kill or restart it, while daemonize simply '
138 'fires and forgets.')
139 parser.add_argument(
140 '--pid-file-path',
141 type=str,
142 default=None,
143 help='Path of tmp file to store the daemon\'s pid.')
144 parser.add_argument(
145 '--', dest='',
146 required=False,
147 help='Optional delimiter dividing daemonizer options with the command. '
148 'This is here to ensure it\'s backwards compatible with the previous '
149 'version of daemonizer.')
150 parser.add_argument('cmd', help='Command (+ args) to daemonize', nargs='*')
151 args = parser.parse_args()
80 152
81 cmd = sys.argv[idx+1:] 153 if args.action == 'restart':
82 if not cmd: 154 return restart(args.cmd, args.pid_file_path)
83 print_usage('arg0 not specified for sub command') 155 elif args.action == 'daemonize':
84 156 return daemonize(args.cmd, None)
85 # TODO(sivachandra): When required in the future, use optparse to parse args 157 elif args.action == 'stop':
86 # from sys.argv[:idx] 158 return stop(args.pid_file_path)
87
88 daemonize()
89 return subprocess.call(cmd)
90 159
91 160
92 if __name__ == '__main__': 161 if __name__ == '__main__':
93 sys.exit(main()) 162 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698