1 # Utility library for spawning remote asynchronous tasks
2 from nepi.util import server
8 Special value that when given to remote_spawn in stderr causes stderr to
9 redirect to whatever stdout was redirected to.
14 Process is still running
24 Process hasn't started running yet (this should be very rare)
27 def remote_spawn(command, pidfile, stdout='/dev/null', stderr=STDOUT, stdin='/dev/null', home=None, create_home=False, sudo=False,
28 host = None, port = None, user = None, agent = None,
29 ident_key = None, server_key = None,
30 tty = False, hostip = None):
32 Spawn a remote command such that it will continue working asynchronously.
35 command: the command to run - it should be a single line.
37 pidfile: path of a (ideally unique to this task) pidfile for tracking the process.
39 stdout: path of a file to redirect standard output to - must be a string.
41 stderr: path of a file to redirect standard error to - string or the special STDOUT value
42 to redirect to the same file stdout was redirected to. Defaults to STDOUT.
43 stdin: path of a file with input to be piped into the command's standard input
45 home: path of a folder to use as working directory - should exist, unless you specify create_home
47 create_home: if True, the home folder will be created first with mkdir -p
49 sudo: whether the command needs to be executed as root
51 host/port/user/agent/ident_key: see nepi.util.server.popen_ssh_command
54 (stdout, stderr), process
56 Of the spawning process, which only captures errors at spawning time.
57 Usually only useful for diagnostics.
59 # Start process in a "daemonized" way, using nohup and heavy
60 # stdin/out redirection to avoid connection issues
66 daemon_command = '{ { %(command)s > %(stdout)s 2>%(stderr)s < %(stdin)s & } ; echo $! 1 > %(pidfile)s ; }' % {
68 'pidfile' : server.shell_escape(pidfile),
75 cmd = "%(create)s%(gohome)s rm -f %(pidfile)s ; %(sudo)s nohup bash -c %(command)s " % {
76 'command' : server.shell_escape(daemon_command),
78 'sudo' : 'sudo -S' if sudo else '',
80 'pidfile' : server.shell_escape(pidfile),
81 'gohome' : 'cd %s ; ' % (server.shell_escape(home),) if home else '',
82 'create' : 'mkdir -p %s ; ' % (server.shell_escape,) if create_home else '',
84 (out,err),proc = server.popen_ssh_command(
90 ident_key = ident_key,
91 server_key = server_key,
97 raise RuntimeError, "Failed to set up application: %s %s" % (out,err,)
102 def remote_check_pid(pidfile,
103 host = None, port = None, user = None, agent = None,
104 ident_key = None, server_key = None, hostip = None):
106 Check the pidfile of a process spawned with remote_spawn.
109 pidfile: the pidfile passed to remote_span
111 host/port/user/agent/ident_key: see nepi.util.server.popen_ssh_command
115 A (pid, ppid) tuple useful for calling remote_status and remote_kill,
116 or None if the pidfile isn't valid yet (maybe the process is still starting).
119 (out,err),proc = server.popen_ssh_command(
120 "cat %(pidfile)s" % {
127 ident_key = ident_key,
128 server_key = server_key,
137 return map(int,out.strip().split(' ',1))
139 # Ignore, many ways to fail that don't matter that much
144 def remote_status(pid, ppid,
145 host = None, port = None, user = None, agent = None,
146 ident_key = None, server_key = None, hostip = None):
148 Check the status of a process spawned with remote_spawn.
151 pid/ppid: pid and parent-pid of the spawned process. See remote_check_pid
153 host/port/user/agent/ident_key: see nepi.util.server.popen_ssh_command
157 One of NOT_STARTED, RUNNING, FINISHED
160 (out,err),proc = server.popen_ssh_command(
161 "ps --pid %(pid)d -o pid | grep -c %(pid)d ; true" % {
169 ident_key = ident_key,
170 server_key = server_key,
180 status = bool(int(out.strip()))
183 logging.warn("Error checking remote status:\n%s%s\n", out, err)
184 # Ignore, many ways to fail that don't matter that much
186 return RUNNING if status else FINISHED
190 def remote_kill(pid, ppid, sudo = False,
191 host = None, port = None, user = None, agent = None,
192 ident_key = None, server_key = None, hostip = None,
195 Kill a process spawned with remote_spawn.
197 First tries a SIGTERM, and if the process does not end in 10 seconds,
201 pid/ppid: pid and parent-pid of the spawned process. See remote_check_pid
203 sudo: whether the command was run with sudo - careful killing like this.
205 host/port/user/agent/ident_key: see nepi.util.server.popen_ssh_command
209 Nothing, should have killed the process
213 subkill = "$(ps --ppid %(pid)d -o pid h)" % { 'pid' : pid }
217 SUBKILL="%(subkill)s" ;
218 %(sudo)s kill -- -%(pid)d $SUBKILL || /bin/true
219 %(sudo)s kill %(pid)d $SUBKILL || /bin/true
220 for x in 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 ; do
222 if [ `ps --pid %(pid)d -o pid | grep -c %(pid)d` == '0' ]; then
225 %(sudo)s kill -- -%(pid)d $SUBKILL || /bin/true
226 %(sudo)s kill %(pid)d $SUBKILL || /bin/true
230 if [ `ps --pid %(pid)d -o pid | grep -c %(pid)d` != '0' ]; then
231 %(sudo)s kill -9 -- -%(pid)d $SUBKILL || /bin/true
232 %(sudo)s kill -9 %(pid)d $SUBKILL || /bin/true
236 cmd = "( %s ) >/dev/null 2>/dev/null </dev/null &" % (cmd,)
238 (out,err),proc = server.popen_ssh_command(
242 'sudo' : 'sudo -S' if sudo else '',
249 ident_key = ident_key,
250 server_key = server_key,
254 # wait, don't leave zombies around