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

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: 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 import argparse
24 import logging
23 import os 25 import os
26 import signal
24 import subprocess 27 import subprocess
25 import sys 28 import sys
26 29
30 def start(cmd, pid_file_path):
luqui 2015/09/08 18:18:10 seems like this action ought to be called 'restart
bpastene 2015/09/08 20:50:47 Done.
31 # Check for the pid_file to see if the daemon's already running
32 # and restart it if it is
33 if (pid_file_path == None):
luqui 2015/09/08 18:18:10 You don't need parens around if's conditional in p
bpastene 2015/09/08 20:50:47 Done.
34 logging.error('pid_file_path arg must be specified when starting a daemon')
35 return 1
36 try:
37 with open(pid_file_path, 'r') as pid_file:
38 pid = int(pid_file.readline())
39 except (IOError, ValueError):
40 pid = None
27 41
28 def daemonize(): 42 if (pid):
luqui 2015/09/08 18:18:10 no parens
bpastene 2015/09/08 20:50:47 Done.
43 logging.info(
44 "%s pid file already exists, attempting to kill process %d",
45 pid_file_path,
46 pid)
47 try:
48 os.kill(pid, signal.SIGTERM)
49 except (OSError):
luqui 2015/09/08 18:18:10 no parens
bpastene 2015/09/08 20:50:47 Done.
50 logging.exception("Unable to kill old daemon process")
51
52 return daemonize(cmd, pid_file_path)
53
54 def daemonize(cmd, pid_file_path):
29 """This function is based on the Python recipe provided here: 55 """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/ 56 http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/
31 """ 57 """
32 # Spawn a detached child process. 58 # Spawn a detached child process.
33 try: 59 try:
34 pid = os.fork() 60 pid = os.fork()
35 if pid > 0: 61 if pid > 0:
36 # exit first parent 62 # exit first parent
37 sys.exit(0) 63 sys.exit(0)
38 except OSError, e: 64 except OSError, e:
(...skipping 20 matching lines...) Expand all
59 # redirect standard file descriptors 85 # redirect standard file descriptors
60 sys.stdout.flush() 86 sys.stdout.flush()
61 sys.stderr.flush() 87 sys.stderr.flush()
62 si = file('/dev/null', 'r') 88 si = file('/dev/null', 'r')
63 so = file('/dev/null', 'a+') 89 so = file('/dev/null', 'a+')
64 se = file('/dev/null', 'a+', 0) 90 se = file('/dev/null', 'a+', 0)
65 os.dup2(si.fileno(), sys.stdin.fileno()) 91 os.dup2(si.fileno(), sys.stdin.fileno())
66 os.dup2(so.fileno(), sys.stdout.fileno()) 92 os.dup2(so.fileno(), sys.stdout.fileno())
67 os.dup2(se.fileno(), sys.stderr.fileno()) 93 os.dup2(se.fileno(), sys.stderr.fileno())
68 94
95 proc = subprocess.Popen(cmd)
69 96
70 def print_usage(err_msg): 97 # write pid to file if applicable
71 print >> sys.stderr, err_msg 98 if (pid_file_path):
72 sys.exit('Usage: daemonizer.py [options] -- arg0 [argN...]') 99 try:
100 with open(pid_file_path, 'w+') as pid_file:
luqui 2015/09/08 18:18:10 I think you mean 'w' mode instead of 'w+'.
bpastene 2015/09/08 20:50:47 Done.
101 pid_file.write('%s' % str(proc.pid))
102 except (IOError):
103 logging.exception("Unable to write pid to file")
73 104
105 proc.communicate()
luqui 2015/09/08 18:18:10 This will make daemonize block, which is the oppos
bpastene 2015/09/08 20:50:47 Previously, it would do just that, and return the
luqui 2015/09/08 21:15:57 Oh I misunderstood, I missed the fork() above. I
106 return proc.returncode
107
108 def stop(pid_file_path):
109 if (pid_file_path == None):
110 logging.error("pid_file_path arg must be specified when stopping a daemon")
111 return 1
112 try:
113 with open(pid_file_path) as pid_file:
114 pid = int(pid_file.readline())
115 logging.info('Sending SIGTERM to %d', pid)
116 os.kill(pid, signal.SIGTERM)
117 os.remove(pid_file_path)
118 except (IOError, OSError):
119 logging.exception('Error terminating daemon process')
luqui 2015/09/08 18:18:10 Log the exception here so we have some idea what t
bpastene 2015/09/08 20:50:47 logging.exception() automatically logs the excepti
luqui 2015/09/08 21:15:57 Oh cool :-)
74 120
75 def main(): 121 def main():
76 try: 122 parser = argparse.ArgumentParser(
77 idx = sys.argv.index('--') 123 description='Launch, or shutdown, a daemon process.')
78 except ValueError: 124 parser.add_argument(
79 print_usage('Separator -- not found') 125 '--action',
126 default='daemonize',
127 choices=['start','stop','daemonize'],
128 help='What action to take. Both start and stop attempt to write & read '
129 'the pid to a file so it can kill or restart it, while daemonize simply '
130 'fires and forgets.')
131 parser.add_argument(
132 '--pid-file-path',
133 type=str,
134 default=None,
135 help='Path of tmp file to store the daemon\'s pid.')
136 parser.add_argument(
137 '--', dest='',
138 required=False,
139 help='Optional delimiter dividing daemonizer options with the command. '
140 'This is here to ensure it\'s backwards compatible with the previous '
141 'version of daemonizer.')
142 parser.add_argument('cmd', help='Command (+ args) to daemonize', nargs='*')
143 args = parser.parse_args()
80 144
81 cmd = sys.argv[idx+1:] 145 if (args.action == 'start'):
82 if not cmd: 146 return start(args.cmd, args.pid_file_path)
83 print_usage('arg0 not specified for sub command') 147 elif (args.action == 'daemonize'):
84 148 return daemonize(args.cmd, None)
85 # TODO(sivachandra): When required in the future, use optparse to parse args 149 elif (args.action == 'stop'):
86 # from sys.argv[:idx] 150 return stop(args.pid_file_path)
87
88 daemonize()
89 return subprocess.call(cmd)
90
91 151
92 if __name__ == '__main__': 152 if __name__ == '__main__':
93 sys.exit(main()) 153 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