You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
108 lines
3.2 KiB
108 lines
3.2 KiB
4 months ago
|
# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
|
||
|
# Use of this source code is governed by a BSD-style license that can be
|
||
|
# found in the LICENSE file.
|
||
|
|
||
|
import logging
|
||
|
import os
|
||
|
import pipes
|
||
|
import threading
|
||
|
|
||
|
from autotest_lib.client.common_lib import error
|
||
|
|
||
|
class _HelperThread(threading.Thread):
|
||
|
"""Make a thread to run the command in."""
|
||
|
def __init__(self, host, cmd):
|
||
|
super(_HelperThread, self).__init__()
|
||
|
self._host = host
|
||
|
self._cmd = cmd
|
||
|
self._result = None
|
||
|
self.daemon = True
|
||
|
|
||
|
|
||
|
def run(self):
|
||
|
logging.info('Helper thread running: %s', self._cmd)
|
||
|
# NB: set ignore_status as we're always terminated w/ pkill
|
||
|
self._result = self._host.run(self._cmd, ignore_status=True)
|
||
|
|
||
|
|
||
|
@property
|
||
|
def result(self):
|
||
|
"""
|
||
|
@returns string result of running our command if the command has
|
||
|
finished, and None otherwise.
|
||
|
|
||
|
"""
|
||
|
return self._result
|
||
|
|
||
|
|
||
|
class Command(object):
|
||
|
"""
|
||
|
Encapsulates a command run on a remote machine.
|
||
|
|
||
|
Future work is to have this get the PID (by prepending 'echo $$;
|
||
|
exec' to the command and parsing the output).
|
||
|
|
||
|
"""
|
||
|
def __init__(self, host, cmd, pkill_argument=None):
|
||
|
"""
|
||
|
Run a command on a remote host in the background.
|
||
|
|
||
|
@param host Host object representing the remote machine.
|
||
|
@param cmd String command to run on the remote machine.
|
||
|
@param pkill_argument String argument to pkill to kill the remote
|
||
|
process.
|
||
|
|
||
|
"""
|
||
|
if pkill_argument is None:
|
||
|
# Attempt to guess what a suitable pkill argument would look like.
|
||
|
pkill_argument = os.path.basename(cmd.split()[0])
|
||
|
self._command_name = pipes.quote(pkill_argument)
|
||
|
self._host = host
|
||
|
self._thread = _HelperThread(self._host, cmd)
|
||
|
self._thread.start()
|
||
|
|
||
|
|
||
|
def join(self, signal=None, timeout=5.0):
|
||
|
"""
|
||
|
Kills the remote command and waits until it dies. Takes an optional
|
||
|
signal argument to control which signal to send the process to be
|
||
|
killed.
|
||
|
|
||
|
@param signal Signal string to give to pkill (e.g. SIGNAL_INT).
|
||
|
@param timeout float number of seconds to wait for join to finish.
|
||
|
|
||
|
"""
|
||
|
if signal is None:
|
||
|
signal_arg = ''
|
||
|
else:
|
||
|
# In theory, it should be hard to pass something evil for signal if
|
||
|
# we make sure it's an integer before passing it to pkill.
|
||
|
signal_arg = '-' + str(int(signal))
|
||
|
|
||
|
# Ignore status because the command may have exited already
|
||
|
self._host.run("pkill %s %s" % (signal_arg, self._command_name),
|
||
|
ignore_status=True)
|
||
|
self._thread.join(timeout)
|
||
|
if self._thread.isAlive():
|
||
|
raise error.TestFail('Failed to kill remote command: %s' %
|
||
|
self._command_name)
|
||
|
|
||
|
|
||
|
def __enter__(self):
|
||
|
return self
|
||
|
|
||
|
|
||
|
def __exit__(self, exception, value, traceback):
|
||
|
self.join()
|
||
|
return False
|
||
|
|
||
|
|
||
|
@property
|
||
|
def result(self):
|
||
|
"""
|
||
|
@returns string result of running our command if the command has
|
||
|
finished, and None otherwise.
|
||
|
|
||
|
"""
|
||
|
return self._thread.result
|