Salome HOME
implementation with psutil
authorViktor UZLOV <vuzlov@debian10-01.nnov.opencascade.com>
Tue, 27 Oct 2020 09:21:59 +0000 (12:21 +0300)
committerViktor UZLOV <vuzlov@debian10-01.nnov.opencascade.com>
Tue, 27 Oct 2020 09:21:59 +0000 (12:21 +0300)
bin/PortManager.py
bin/killSalome.py
bin/killSalomeWithPort.py
bin/salome_utils.py

index aace9e17bcd9e8aac5353abb572d4aa35bd750a4..0095b39063e548bbd840f4a4c5a1e6188c178480 100644 (file)
@@ -23,6 +23,9 @@
 #
 import os
 import sys
+import psutil
+
+from socket import AF_INET, SOCK_STREAM
 
 try:
   import cPickle as pickle #@UnusedImport
@@ -103,55 +106,17 @@ def __isPortUsed(port, config):
 #
 
 def __isNetworkConnectionActiveOnPort(port):
-  # :NOTE: Under windows:
-  #        netstat options -l and -t are unavailable
-  #        grep command is unavailable
-  if sys.platform == "win32":
-    cmd = ['netstat','-a','-n','-p','tcp']
+  # psutil realization
+  ports =[]
+  template = (AF_INET, SOCK_STREAM, "LISTEN")
+  for c in psutil.net_connections(kind='inet'):
+    if (c.family, c.type, c.status) == template:
+      ports.append(c.laddr.port)
+  if port in ports:
+    return True
   else:
-    cmd = ['netstat','-ant']
-    pass
-
-  err = None
-  try:
-    from subprocess import Popen, PIPE, STDOUT
-    p = Popen(cmd, stdout=PIPE, stderr=STDOUT)
-    out, err = p.communicate()
-  except:
-    print("Error when trying to access active network connections.")
-    if err: print(err)
-    print("... Presumably this package is not installed...Please install netstat if available for your distribution.")
-    print("... Trying socket based approach")
-    try:
-      import socket
-      sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-      result = sock.connect_ex(("127.0.0.1", port))
-      if result == 0:
-        print("Port %r:      Closed" % (port))
-        sock.close()
-        return True
-      else:
-        sock.close()
-        return False
-    except:
-      import traceback
-      traceback.print_exc()
-      return False
-
-  from io import StringIO
-  buf = StringIO(out.decode('utf-8', 'ignore'))
-  ports = buf.readlines()
-  # search for TCP - LISTEN connections
-  import re
-  regObj = re.compile( ".*tcp.*:([0-9]+).*:.*listen", re.IGNORECASE );
-  for item in ports:
-    try:
-      p = int(regObj.match(item).group(1))
-      if p == port: return True
-    except:
-      pass
-  return False
-#
+    return False
+  #
 
 def getPort(preferredPort=None):
   logger.debug("GET PORT")
index d7b8c392eaf975767e73af7294a858aa47414203..39e51e173de59d9ad39b832452f5a9a4d9fe2c76 100755 (executable)
@@ -28,7 +28,8 @@
 
 import os, sys, re, signal
 
-from killSalomeWithPort import killMyPort, getPiDict, checkUnkilledProcess
+from contextlib import  suppress
+from killSalomeWithPort import killMyPort, getPiDict, checkUnkilledProcesses, killProcesses
 #from salome_utils import getHostName, getShortHostName
 from salome_utils import getUserName
 
@@ -37,61 +38,27 @@ def killAllPorts():
     Kill all SALOME sessions belonging to the user.
     """
     user = getUserName()
-    #hostname  = getHostName()
-    #shostname = getShortHostName()
-    # new-style dot-prefixed pidict file
-    #fpidict   = getPiDict('(\d*)',hidden=True)
-    #problem with WIN32 path slashes
-    fpidict   = getPiDict('#####',hidden=True)
-    dirpidict = os.path.dirname(fpidict)
-    fpidict   = os.path.basename(fpidict)
-    #if hostname in fpidict:
-    #    fpidict = fpidict.replace(hostname, shostname+".*")
-    fpidict   = fpidict.replace('#####', '(\d*)')
-    fnamere   = re.compile("^%s" % fpidict)
-    try:
-        for f in os.listdir(dirpidict):
-            mo = fnamere.match(f)
-            try:
-                killMyPort(mo.group(1))
-            except:
-                pass
-            pass
-        pass
-    except:
-        pass
-    # provide compatibility with old-style pidict file (not dot-prefixed)
-    #fpidict   = getPiDict('(\d*)',hidden=False)
-    fpidict   = getPiDict('#####',hidden=False)
-    dirpidict = os.path.dirname(fpidict)
-    fpidict   = os.path.basename(fpidict)
-    #if hostname in fpidict:
-    #    fpidict = fpidict.replace(hostname, shostname+".*")
-    fpidict = fpidict.replace('#####', '(\d*)')
-    fnamere   = re.compile("^%s$" % fpidict)
-    try:
-        for f in os.listdir(dirpidict):
-            mo = fnamere.match(f)
-            try:
-                killMyPort(mo.group(1))
-            except:
-                pass
-            pass
-        pass
-    except:
-        pass
+
+    def _getPortsFromPiDict(hidden, pattern):
+        fpidict = getPiDict('#####', hidden=hidden)
+        dirpidict = os.path.dirname(fpidict)
+        fpidict = os.path.basename(fpidict)
+        fpidict = fpidict.replace('#####', r'(\d*)')
+        fnamere = re.compile(pattern.format(fpidict))
+        with suppress(IOError):
+            for _f in os.listdir(dirpidict):
+                _mo = fnamere.match(_f)
+                if _mo:
+                    killMyPort(_mo.group(1))
+
+    _getPortsFromPiDict(True, "^{}")
+    _getPortsFromPiDict(False, "^{}$")
     # kill other processes
-    for pid in checkUnkilledProcess():
-        try:
-            os.kill(pid, signal.SIGKILL)
-        except:
-            pass
-        pass
+    killProcesses(checkUnkilledProcesses())
     if sys.platform != 'win32':
         # delete uri files needed by ompi-server
         cmd = "rm -f " + os.path.expanduser("~") + "/.urifile_*"
         os.system(cmd)
-    pass
 
 if __name__ == "__main__":
     try:
index 2ffbdd42a5876e5f57d3d1a426fdd1655a448ec1..eec6a44658a87ed2f89885e746b6867205283598 100755 (executable)
 #  \endcode
 #
 
-import re, os, sys, pickle, signal, glob
+import argparse, re, os, sys, pickle, signal, glob, psutil
 import subprocess
 import shlex
 from salome_utils import verbose
-
+from contextlib import suppress
 
 def getPiDict(port,appname='salome',full=True,hidden=True,hostname=None):
     """
@@ -227,7 +227,26 @@ def shutdownMyPort(port, cleanup=True):
 # a RuntimeError is raised; we explicitly exit this function with code 0 to
 # prevent parent thread from crashing.
 
+def killProcesses(_procs):
+    '''
+    Terminate and kill all psutil.Process()
+    :param _procs: list psutil.Process()
+    '''
+    for _p in _procs:
+        _p.terminate()
+    _, alive = psutil.wait_procs(_procs, timeout=5)
+    for _p in alive:
+        _p.kill()
+
 def __killMyPort(port, filedict):
+    def _killPiDictProcesses(_process_id):
+        procs = []
+        for _p in _process_id:
+            try:
+                procs.append(psutil.Process(_p))
+            except psutil.NoSuchProcess:
+                if verbose(): print("  ------------------ process {} : not found".format(_p))
+        killProcesses(procs)
     # bug fix: ensure port is an integer
     if port:
         port = int(port)
@@ -236,23 +255,7 @@ def __killMyPort(port, filedict):
         with open(filedict, 'rb') as fpid:
             process_ids=pickle.load(fpid)
             for process_id in process_ids:
-                for pid, cmd in list(process_id.items()):
-                    pid = int(pid)
-                    if verbose(): print("stop process %s : %s"% (pid, cmd[0]))
-                    try:
-                        from salome_utils import killpid
-                        killpid(pid)
-                    except:
-                        if verbose(): print("  ------------------ process %s : %s not found"% (pid, cmd[0]))
-                        if pid in checkUnkilledProcess():
-                            try:
-                                killpid(pid)
-                            except:
-                                pass
-                        pass
-                    pass # for pid ...
-                pass # for process_id ...
-            # end with
+                _killPiDictProcesses(process_id)
     except:
         print("Cannot find or open SALOME PIDs file for port", port)
         pass
@@ -366,7 +369,7 @@ def killMyPortSpy(pid, port):
     dt = 1.0
     while 1:
         from salome_utils import killpid
-        ret = killpid(pid, 0)
+        ret = killpid(int(pid), 0)
         if ret == 0:
             break
         elif ret < 0:
@@ -400,66 +403,58 @@ def killMyPortSpy(pid, port):
     killMyPort(port)
     return
 
-def checkUnkilledProcess():
-    #check processes in system after kill
+def checkUnkilledProcesses():
+    '''
+    Check all unkilled SALOME processes
+    :return: list unkilled processes
+    '''
     from salome_utils import getUserName
-    user = getUserName()
-    processes = dict()
-
-    if sys.platform != 'win32':
-
-        def _getDictfromOutput(_output, _dic, _cond = None):
-            _pids = dict(zip(list(map(int, _output[::2])), _output[1::2]))
-            if _cond:
-                _pids = {pid:cmd for pid,cmd in _pids.items() if re.match(_cond, cmd)}
-            _dic.update(_pids)
-
-        # 1. SALOME servers plus omniNames
-        cmd = 'ps --noheading -U {user} -o pid,cmd | awk \'{{printf("%s %s\\n", $1, $2)}}\''.format(user=user)
-        _getDictfromOutput(subprocess.getoutput(cmd).split(), processes, '^(SALOME_|omniNames)')
-        # 2. ghs3d
-        cmd = 'ps -fea | grep \'{user}\' | grep \'ghs3d\' | grep \'f /tmp/GHS3D_\' | grep -v \'grep\' | awk \'{{print("%s %s\\n", $2, $8)}}\''.format(user=user)
-        _getDictfromOutput(subprocess.getoutput(cmd).split(), processes)
-        # 3. ompi-server
-        cmd = 'ps -fea | grep \'{user}\' | grep \'ompi-server\' | grep -v \'grep\' | awk \'{{print("%s %s\\n", $2, $8)}}\''.format(user=user)
-        _getDictfromOutput(subprocess.getoutput(cmd).split(), processes)
-    else:
-        cmd = 'tasklist /fo csv | findstr /i "SALOME_ omniNames"'
-        prc = subprocess.getoutput(cmd)
-        try:
-            prc = prc.split()
-            prc = [prc[i].split(',') for i in range(0, len(prc)) if i % 2 == 0]
-            prc = dict([(int(prc[j][1].replace('"', '')), prc[j][0].replace('"', '')) for j in range(0, len(prc))])
-            processes.update(prc)
-        except:
-            pass
+    processes = list()
+    def compareNames(_process):
+        # On OS Windows: psutil.Process().username() get 'usergroup' + 'username'
+        return getUserName() == _process.username()
+
+    def _getDictfromOutput(_prlist, _cond=None):
+        for _p in psutil.process_iter(['name', 'username']):
+            with suppress(psutil.AccessDenied):
+                if (compareNames(_p) and re.match(_cond, _p.info['name'])):
+                    _prlist.append(_p)
+
+    _getDictfromOutput(processes, '(SALOME_*)')
+    _getDictfromOutput(processes, '(omniNames)')
+    _getDictfromOutput(processes, '(ghs3d)')
+    _getDictfromOutput(processes, '(ompi-server)')
+
     return processes
 
-if __name__ == "__main__":
-    if len(sys.argv) < 2:
-        print("Usage: ")
-        print("  %s <port>" % os.path.basename(sys.argv[0]))
-        print()
-        print("Kills SALOME session running on specified <port>.")
-        sys.exit(1)
-        pass
-    if sys.argv[1] == "--spy":
-        if len(sys.argv) > 3:
-            pid = sys.argv[2]
-            port = sys.argv[3]
-            killMyPortSpy(pid, port)
-            pass
+def main():
+    '''
+    Main function
+    '''
+    parser = argparse.ArgumentParser(description="killSalomeWithPort utility")
+    parser.add_argument("ports",
+                        help="ports of Salome services to kill",
+                        nargs="*", type=int)
+    parser.add_argument("--spy",
+                        help="kill spy port by pair PID, PORT", nargs=2,
+                        type=int, metavar=("PID", "PORT"))
+    parser.add_argument("--find",
+                        help="Get unkilled processes for SALOME",
+                        action='store_true')
+    args = parser.parse_args()
+
+    if args.spy:
+        killMyPortSpy(args.spy[0], args.spy[1])
         sys.exit(0)
-        pass
-    elif sys.argv[1] == "--find":
-        processes = checkUnkilledProcess()
-        if processes:
+    if args.find:
+        pidcmd = checkUnkilledProcesses()
+        if pidcmd:
             print("Unkilled processes: ")
             print(" --------------------")
             print(" PID   : Process name")
             print(" --------------------")
-            for pair in processes.items():
-                print('%6d : %s' % pair)
+            for p in pidcmd:
+                print("{0:6} : {1}".format(p.pid, p.name()))
         sys.exit(0)
     try:
         from salomeContextUtils import setOmniOrbUserPath #@UnresolvedImport
@@ -467,7 +462,8 @@ if __name__ == "__main__":
     except Exception as e:
         print(e)
         sys.exit(1)
-    for port in sys.argv[1:]:
+    for port in args.ports:
         killMyPort(port)
-        pass
-    pass
+
+if __name__ == "__main__":
+    main()
index a80132894c0eb8b3d562612ef15d377dd1ff264c..c5d55fcee8f1e4f05549f2a9f509b72dbb502662 100644 (file)
@@ -549,34 +549,15 @@ def getOmniNamesPid(port):
     """
     Return OmniNames pid by port number.
     """
-    import sys,subprocess,re
-    if sys.platform == "win32":
-        # Get process list by WMI Command Line Utility(WMIC)
-        # Output is formatted with each value listed on a separate line and with the name of the property:
-        #   ...
-        #   Caption=<caption0>
-        #   CommandLine=<commandline0>
-        #   ProcessId=<processid0>
-        #
-        #
-        #
-        #   Caption=<caption1>
-        #   CommandLine=<commandline1>
-        #   ProcessId=<processid1>
-        #   ...
-        cmd = 'WMIC PROCESS get Caption,Commandline,Processid /VALUE'
-        proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
-        # Get stdout
-        allProc = proc.communicate()[0].decode()
-        # find Pid of omniNames
-        pid = re.findall(r'Caption=.*omniNames.*\n?CommandLine=.*omniNames.*\D%s\D.*\n?ProcessId=(\d*)'%(port),allProc)[0]
-    else:        
-        cmd = "ps -eo pid,command | grep -v grep | grep -E \"omniNames.*%s\" | awk '{print $1}'"%(port)
-        proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
-        pid = proc.communicate()[0]
-        pass
-
-    return pid
+    import psutil
+    
+    proc_names = {}
+    template = (port, "omniNames")
+    for p in psutil.process_iter(['pid', 'name']):
+        proc_names[p.info['pid']] = p.info['name']
+    for c in psutil.net_connections(kind='inet'):
+        if (c.laddr.port, proc_names[c.pid]) == template:
+            return c.pid
 # --
 
 def killOmniNames(port):