blob: f7c8f433b6ddc5de607a2614e70d55896680e4fa [file] [log] [blame]
David Rochberg410ea822011-11-18 11:39:59 -05001# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Christopher Wileyffffab72013-03-06 16:14:11 -08005import logging
6import os
7import pipes
8import threading
9
Christopher Wileyc8769cf2013-04-03 11:40:09 -070010from autotest_lib.client.common_lib import error
David Rochberg410ea822011-11-18 11:39:59 -050011
12class _HelperThread(threading.Thread):
13 """Make a thread to run the command in."""
Christopher Wileyffffab72013-03-06 16:14:11 -080014 def __init__(self, host, cmd):
15 super(_HelperThread, self).__init__()
16 self._host = host
17 self._cmd = cmd
18 self._result = None
19 self.daemon = True
20
David Rochberg410ea822011-11-18 11:39:59 -050021
22 def run(self):
Christopher Wileyffffab72013-03-06 16:14:11 -080023 logging.info('Helper thread running: %s', self._cmd)
David Rochberg410ea822011-11-18 11:39:59 -050024 # NB: set ignore_status as we're always terminated w/ pkill
Christopher Wileyffffab72013-03-06 16:14:11 -080025 self._result = self._host.run(self._cmd, ignore_status=True)
26
27
28 @property
29 def result(self):
30 """
31 @returns string result of running our command if the command has
32 finished, and None otherwise.
33
34 """
35 return self._result
David Rochberg410ea822011-11-18 11:39:59 -050036
37
38class Command(object):
Christopher Wileyffffab72013-03-06 16:14:11 -080039 """
40 Encapsulates a command run on a remote machine.
David Rochberg410ea822011-11-18 11:39:59 -050041
42 Future work is to have this get the PID (by prepending 'echo $$;
43 exec' to the command and parsing the output).
44
David Rochberg410ea822011-11-18 11:39:59 -050045 """
Christopher Wileyffffab72013-03-06 16:14:11 -080046 def __init__(self, host, cmd, pkill_argument=None):
47 """
48 Run a command on a remote host in the background.
David Rochberg410ea822011-11-18 11:39:59 -050049
Christopher Wileyffffab72013-03-06 16:14:11 -080050 @param host Host object representing the remote machine.
51 @param cmd String command to run on the remote machine.
52 @param pkill_argument String argument to pkill to kill the remote
53 process.
54
55 """
56 if pkill_argument is None:
Christopher Wileyc8769cf2013-04-03 11:40:09 -070057 # Attempt to guess what a suitable pkill argument would look like.
58 pkill_argument = os.path.basename(cmd.split()[0])
Christopher Wileyffffab72013-03-06 16:14:11 -080059 self._command_name = pipes.quote(pkill_argument)
60 self._host = host
61 self._thread = _HelperThread(self._host, cmd)
62 self._thread.start()
63
64
Christopher Wileyc8769cf2013-04-03 11:40:09 -070065 def join(self, signal=None, timeout=5.0):
Christopher Wileyffffab72013-03-06 16:14:11 -080066 """
67 Kills the remote command and waits until it dies. Takes an optional
68 signal argument to control which signal to send the process to be
69 killed.
70
71 @param signal Signal string to give to pkill (e.g. SIGNAL_INT).
Christopher Wileyc8769cf2013-04-03 11:40:09 -070072 @param timeout float number of seconds to wait for join to finish.
73
Christopher Wileyffffab72013-03-06 16:14:11 -080074 """
75 if signal is None:
76 signal_arg = ''
77 else:
78 # In theory, it should be hard to pass something evil for signal if
79 # we make sure it's an integer before passing it to pkill.
80 signal_arg = '-' + str(int(signal))
81
David Rochberg410ea822011-11-18 11:39:59 -050082 # Ignore status because the command may have exited already
Christopher Wileyffffab72013-03-06 16:14:11 -080083 self._host.run("pkill %s %s" % (signal_arg, self._command_name),
84 ignore_status=True)
Christopher Wileyc8769cf2013-04-03 11:40:09 -070085 self._thread.join(timeout)
86 if self._thread.isAlive():
87 raise error.TestFail('Failed to kill remote command: %s' %
88 self._command_name)
Christopher Wileyffffab72013-03-06 16:14:11 -080089
David Rochberg410ea822011-11-18 11:39:59 -050090
91 def __enter__(self):
92 return self
93
Christopher Wileyffffab72013-03-06 16:14:11 -080094
David Rochberg410ea822011-11-18 11:39:59 -050095 def __exit__(self, exception, value, traceback):
Christopher Wileyffffab72013-03-06 16:14:11 -080096 self.join()
David Rochberg410ea822011-11-18 11:39:59 -050097 return False
Christopher Wileyffffab72013-03-06 16:14:11 -080098
99
100 @property
101 def result(self):
102 """
103 @returns string result of running our command if the command has
104 finished, and None otherwise.
105
106 """
107 return self._thread.result