[tor-commits] [stem/master] Support for pulling multiple pids with get_pid_by_name()

atagar at torproject.org atagar at torproject.org
Sun May 12 02:51:20 UTC 2013


commit 7c0b3ca25596123b5cfbc9251bdfbe630d23ffed
Author: Damian Johnson <atagar at 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.





More information about the tor-commits mailing list