From d01354e956a5fff270c8384fce1745eae6fe2662 2014-07-20 20:20:48
From: Min RK <benjaminrk@gmail.com>
Date: 2014-07-20 20:20:48
Subject: [PATCH] Merge pull request #6099 from takluyver/check-nbservers-pid

Check process existence when listing nbserver processes
---

diff --git a/IPython/html/notebookapp.py b/IPython/html/notebookapp.py
index 2069be1..a18860a 100644
--- a/IPython/html/notebookapp.py
+++ b/IPython/html/notebookapp.py
@@ -74,6 +74,7 @@ from IPython.kernel.zmq.session import default_secure, Session
 from IPython.nbformat.sign import NotebookNotary
 from IPython.utils.importstring import import_item
 from IPython.utils import submodule
+from IPython.utils.process import check_pid
 from IPython.utils.traitlets import (
     Dict, Unicode, Integer, List, Bool, Bytes, Instance,
     DottedObjectName, TraitError,
@@ -841,6 +842,7 @@ class NotebookApp(BaseIPythonApplication):
                 'secure': bool(self.certfile),
                 'base_url': self.base_url,
                 'notebook_dir': os.path.abspath(self.notebook_dir),
+                'pid': os.getpid()
                }
 
     def write_server_info_file(self):
@@ -914,8 +916,17 @@ def list_running_servers(profile='default'):
     for file in os.listdir(pd.security_dir):
         if file.startswith('nbserver-'):
             with io.open(os.path.join(pd.security_dir, file), encoding='utf-8') as f:
-                yield json.load(f)
+                info = json.load(f)
 
+            # Simple check whether that process is really still running
+            if check_pid(info['pid']):
+                yield info
+            else:
+                # If the process has died, try to delete its info file
+                try:
+                    os.unlink(file)
+                except OSError:
+                    pass  # TODO: This should warn or log or something
 #-----------------------------------------------------------------------------
 # Main entry point
 #-----------------------------------------------------------------------------
diff --git a/IPython/parallel/apps/baseapp.py b/IPython/parallel/apps/baseapp.py
index fa645fd..fc06304 100644
--- a/IPython/parallel/apps/baseapp.py
+++ b/IPython/parallel/apps/baseapp.py
@@ -36,6 +36,7 @@ from IPython.core.application import (
     base_flags as base_ip_flags
 )
 from IPython.utils.path import expand_path
+from IPython.utils.process import check_pid
 from IPython.utils import py3compat
 from IPython.utils.py3compat import unicode_type
 
@@ -249,28 +250,11 @@ class BaseParallelApplication(BaseIPythonApplication):
             raise PIDFileError('pid file not found: %s' % pid_file)
     
     def check_pid(self, pid):
-        if os.name == 'nt':
-            try:
-                import ctypes
-                # returns 0 if no such process (of ours) exists
-                # positive int otherwise
-                p = ctypes.windll.kernel32.OpenProcess(1,0,pid)
-            except Exception:
-                self.log.warn(
-                    "Could not determine whether pid %i is running via `OpenProcess`. "
-                    " Making the likely assumption that it is."%pid
-                )
-                return True
-            return bool(p)
-        else:
-            try:
-                p = Popen(['ps','x'], stdout=PIPE, stderr=PIPE)
-                output,_ = p.communicate()
-            except OSError:
-                self.log.warn(
-                    "Could not determine whether pid %i is running via `ps x`. "
-                    " Making the likely assumption that it is."%pid
-                )
-                return True
-            pids = list(map(int, re.findall(br'^\W*\d+', output, re.MULTILINE)))
-            return pid in pids
+        try:
+            return check_pid(pid)
+        except Exception:
+            self.log.warn(
+                "Could not determine whether pid %i is running. "
+                " Making the likely assumption that it is."%pid
+            )
+            return True
diff --git a/IPython/utils/_process_posix.py b/IPython/utils/_process_posix.py
index 9c6054f..07be022 100644
--- a/IPython/utils/_process_posix.py
+++ b/IPython/utils/_process_posix.py
@@ -16,6 +16,8 @@ This file is only meant to be imported by process.py, not by end-users.
 from __future__ import print_function
 
 # Stdlib
+import errno
+import os
 import subprocess as sp
 import sys
 
@@ -209,5 +211,15 @@ class ProcessHandler(object):
 # (ls is a good example) that makes them hard.
 system = ProcessHandler().system
 
-
-
+def check_pid(pid):
+    try:
+        os.kill(pid, 0)
+    except OSError as err:
+        if err.errno == errno.ESRCH:
+            return False
+        elif err.errno == errno.EPERM:
+            # Don't have permission to signal the process - probably means it exists
+            return True
+        raise
+    else:
+        return True
diff --git a/IPython/utils/_process_win32.py b/IPython/utils/_process_win32.py
index 8b30c23..3ac59b2 100644
--- a/IPython/utils/_process_win32.py
+++ b/IPython/utils/_process_win32.py
@@ -185,3 +185,8 @@ try:
         return result
 except AttributeError:
     arg_split = py_arg_split
+
+def check_pid(pid):
+    # OpenProcess returns 0 if no such process (of ours) exists
+    # positive int otherwise
+    return bool(ctypes.windll.kernel32.OpenProcess(1,0,pid))
diff --git a/IPython/utils/process.py b/IPython/utils/process.py
index a7a54ea..2a19945 100644
--- a/IPython/utils/process.py
+++ b/IPython/utils/process.py
@@ -21,11 +21,11 @@ import sys
 
 # Our own
 if sys.platform == 'win32':
-    from ._process_win32 import _find_cmd, system, getoutput, arg_split
+    from ._process_win32 import _find_cmd, system, getoutput, arg_split, check_pid
 elif sys.platform == 'cli':
     from ._process_cli import _find_cmd, system, getoutput, arg_split
 else:
-    from ._process_posix import _find_cmd, system, getoutput, arg_split
+    from ._process_posix import _find_cmd, system, getoutput, arg_split, check_pid
 
 from ._process_common import getoutputerror, get_output_error_code, process_handler
 from . import py3compat