commit 7c0b3ca25596123b5cfbc9251bdfbe630d23ffed Author: Damian Johnson atagar@torproject.org Date: Sat May 11 17:09:52 2013 -0700
Support for pulling multiple pids with get_pid_by_name()
A leading cause of test failures in our jenkins environment is test_get_pid_by_name_lsof. This is because the test assumes that there is only a single tor process, but that's often not the case on the dixie host.
This not only fixes the test, but expands get_pid_by_name() to include a 'multiple' flag to pull all pids for a given process name. This includes unit test coverage. --- docs/change_log.rst | 1 + stem/util/system.py | 80 ++++++++++++++++++++++++++++++-------------- test/integ/util/system.py | 8 +++- test/unit/util/system.py | 18 ++++++++++ 4 files changed, 79 insertions(+), 28 deletions(-)
diff --git a/docs/change_log.rst b/docs/change_log.rst index 5ddfcda..6189038 100644 --- a/docs/change_log.rst +++ b/docs/change_log.rst @@ -43,6 +43,7 @@ The following are only available within stem's `git repository * :class:`~stem.response.events.AddrMapEvent` support for the new CACHED argument (:trac:`8596`, :spec:`25b0d43`) * :func:`~stem.control.Controller.attach_stream` could encounter an undocumented 555 response (:trac:`8701`, :spec:`7286576`) * :class:`~stem.descriptor.server_descriptor.RelayDescriptor` digest validation was broken with python 3 (:trac:`8755`) + * :func:`~stem.util.system.get_pid_by_name` can now pull for all processes with a given name
* **Website**
diff --git a/stem/util/system.py b/stem/util/system.py index 645fbbe..6b767c7 100644 --- a/stem/util/system.py +++ b/stem/util/system.py @@ -219,7 +219,7 @@ def is_running(command): return None
-def get_pid_by_name(process_name): +def get_pid_by_name(process_name, multiple = False): """ Attempts to determine the process id for a running process, using...
@@ -231,11 +231,15 @@ def get_pid_by_name(process_name): ps axc | egrep " <name>$" (bsd) 4. lsof -tc <name>
- Results with multiple instances of the process are discarded. - :param str process_name: process name for which to fetch the pid + :param bool multiple: provides a list of all pids if **True**, otherwise + results with multiple processes are discarded
- :returns: **int** with the process id, **None** if it can't be determined + :returns: + Response depends upon the 'multiple' argument as follows... + + * if **False** then this provides an **int** with the process id or **None** if it can't be determined + * if **True** then this provides a **list** of all **int** process ids, and an empty list if it can't be determined """
# attempts to resolve using pgrep, failing if: @@ -249,11 +253,16 @@ def get_pid_by_name(process_name): if is_available("pgrep"): results = call(GET_PID_BY_NAME_PGREP % process_name)
- if results and len(results) == 1: - pid = results[0].strip() + if results: + try: + pids = map(int, results)
- if pid.isdigit(): - return int(pid) + if multiple: + return pids + elif len(pids) == 1: + return pids[0] + except ValueError: + pass
# attempts to resolve using pidof, failing if: # - we're running on bsd (command unavailable) @@ -265,11 +274,16 @@ def get_pid_by_name(process_name): if is_available("pidof"): results = call(GET_PID_BY_NAME_PIDOF % process_name)
- if results and len(results) == 1 and len(results[0].split()) == 1: - pid = results[0].strip() + if results and len(results) == 1: + try: + pids = map(int, results[0].split())
- if pid.isdigit(): - return int(pid) + if multiple: + return pids + elif len(pids) == 1: + return pids[0] + except ValueError: + pass
# attempts to resolve using ps, failing if: # - system's ps variant doesn't handle these flags (none known at the moment) @@ -292,11 +306,16 @@ def get_pid_by_name(process_name): # linux variant of ps results = call(GET_PID_BY_NAME_PS_LINUX % process_name)
- if results and len(results) == 2: - pid = results[1].strip() + if results: + try: + pids = map(int, results[1:])
- if pid.isdigit(): - return int(pid) + if multiple: + return pids + elif len(pids) == 1: + return pids[0] + except ValueError: + pass
if is_bsd(): # bsd variant of ps @@ -304,13 +323,17 @@ def get_pid_by_name(process_name):
if results: # filters results to those with our process name - results = [r for r in results if r.endswith(" %s" % process_name)] + results = [r.split()[0] for r in results if r.endswith(" %s" % process_name)]
- if len(results) == 1 and len(results[0].split()) > 0: - pid = results[0].split()[0] + try: + pids = map(int, results)
- if pid.isdigit(): - return int(pid) + if multiple: + return pids + elif len(pids) == 1: + return pids[0] + except ValueError: + pass
# resolves using lsof which works on both Linux and BSD, only failing if: # - lsof is unavailable (not included by default on OpenBSD) @@ -329,14 +352,19 @@ def get_pid_by_name(process_name): if is_available("lsof"): results = call(GET_PID_BY_NAME_LSOF % process_name)
- if results and len(results) == 1: - pid = results[0].strip() + if results: + try: + pids = map(int, results)
- if pid.isdigit(): - return int(pid) + if multiple: + return pids + elif len(pids) == 1: + return pids[0] + except ValueError: + pass
log.debug("failed to resolve a pid for '%s'" % process_name) - return None + return [] if multiple else None
def get_pid_by_port(port): diff --git a/test/integ/util/system.py b/test/integ/util/system.py index 2c2bc5d..63486a4 100644 --- a/test/integ/util/system.py +++ b/test/integ/util/system.py @@ -201,8 +201,12 @@ class TestSystem(unittest.TestCase): lsof_prefix = stem.util.system.GET_PID_BY_NAME_LSOF % "" mocking.mock(stem.util.system.call, filter_system_call([lsof_prefix]))
- tor_pid = test.runner.get_runner().get_pid() - self.assertEquals(tor_pid, stem.util.system.get_pid_by_name("tor")) + our_tor_pid = test.runner.get_runner().get_pid() + + all_tor_pids = stem.util.system.get_pid_by_name("tor", multiple = True) + + if len(all_tor_pids) == 1: + self.assertEquals(our_tor_pid, all_tor_pids[0])
def test_get_pid_by_port(self): """ diff --git a/test/unit/util/system.py b/test/unit/util/system.py index 4a3473c..fc961cd 100644 --- a/test/unit/util/system.py +++ b/test/unit/util/system.py @@ -37,6 +37,12 @@ GET_PID_BY_NAME_PS_BSD = [ " 11 ?? Ss 5:47.36 DirectoryService", " 12 ?? Ss 3:01.44 notifyd"]
+GET_PID_BY_NAME_PS_BSD_MULTIPLE = [ + " PID TT STAT TIME COMMAND", + " 1 ?? Ss 9:00.22 launchd", + " 10 ?? Ss 0:09.97 kextd", + " 41 ?? Ss 9:00.22 launchd"] + GET_PID_BY_PORT_NETSTAT_RESULTS = [ "Active Internet connections (only servers)", "Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name", @@ -141,6 +147,8 @@ class TestSystem(unittest.TestCase): expected_response = 1111 if test_input == "success" else None self.assertEquals(expected_response, system.get_pid_by_name(test_input))
+ self.assertEquals([123, 456, 789], system.get_pid_by_name("multiple_results", multiple = True)) + def test_get_pid_by_name_pidof(self): """ Tests the get_pid_by_name function with pidof responses. @@ -155,6 +163,8 @@ class TestSystem(unittest.TestCase): expected_response = 1111 if test_input == "success" else None self.assertEquals(expected_response, system.get_pid_by_name(test_input))
+ self.assertEquals([123, 456, 789], system.get_pid_by_name("multiple_results", multiple = True)) + def test_get_pid_by_name_ps_linux(self): """ Tests the get_pid_by_name function with the linux variant of ps. @@ -170,6 +180,8 @@ class TestSystem(unittest.TestCase): expected_response = 1111 if test_input == "success" else None self.assertEquals(expected_response, system.get_pid_by_name(test_input))
+ self.assertEquals([123, 456, 789], system.get_pid_by_name("multiple_results", multiple = True)) + def test_get_pid_by_name_ps_bsd(self): """ Tests the get_pid_by_name function with the bsd variant of ps. @@ -181,6 +193,10 @@ class TestSystem(unittest.TestCase): self.assertEquals(11, system.get_pid_by_name("DirectoryService")) self.assertEquals(None, system.get_pid_by_name("blarg"))
+ mocking.mock(system.call, mock_call(system.GET_PID_BY_NAME_PS_BSD, GET_PID_BY_NAME_PS_BSD_MULTIPLE)) + + self.assertEquals([1, 41], system.get_pid_by_name("launchd", multiple = True)) + def test_get_pid_by_name_lsof(self): """ Tests the get_pid_by_name function with lsof responses. @@ -195,6 +211,8 @@ class TestSystem(unittest.TestCase): expected_response = 1111 if test_input == "success" else None self.assertEquals(expected_response, system.get_pid_by_name(test_input))
+ self.assertEquals([123, 456, 789], system.get_pid_by_name("multiple_results", multiple = True)) + def test_get_pid_by_port_netstat(self): """ Tests the get_pid_by_port function with a netstat response.
tor-commits@lists.torproject.org