Salome HOME
EDF bug 17743, salome.sg regressions in DEV (missing getAllSelected method)
[modules/kernel.git] / bin / killSalomeWithPort.py
index 4403a8bc2687442826ec7f35b46e6fbbf27d5880..fec9e4aa356850f7a1794f4bc39e881283a9febf 100755 (executable)
@@ -1,31 +1,43 @@
-#!/usr/bin/env python
-#  Copyright (C) 2007-2008  CEA/DEN, EDF R&D, OPEN CASCADE
+#! /usr/bin/env python3
+#  -*- coding: iso-8859-1 -*-
+# Copyright (C) 2007-2017  CEA/DEN, EDF R&D, OPEN CASCADE
 #
 #
-#  Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
-#  CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
+# Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
+# CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
 #
 #
-#  This library is free software; you can redistribute it and/or
-#  modify it under the terms of the GNU Lesser General Public
-#  License as published by the Free Software Foundation; either
-#  version 2.1 of the License.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
 #
 #
-#  This library is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-#  Lesser General Public License for more details.
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
 #
 #
-#  You should have received a copy of the GNU Lesser General Public
-#  License along with this library; if not, write to the Free Software
-#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 #
 #
-#  See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
 #
 #
-import os, sys, pickle, signal, commands,glob
-from launchConfigureParser import verbose
-import Utils_Identity
-import salome_utils
 
 
-def getPiDict(port,appname='salome',full=True,hidden=True):
+## \file killSalomeWithPort.py
+#  Stop all %SALOME servers from given sessions by killing them
+#
+#  The sessions are indicated by their ports on the command line as in :
+#  \code
+#  killSalomeWithPort.py 2811 2815
+#  \endcode
+#
+
+import os, sys, pickle, signal, subprocess,glob
+import subprocess
+import shlex
+from salome_utils import verbose
+
+
+def getPiDict(port,appname='salome',full=True,hidden=True,hostname=None):
     """
     Get file with list of SALOME processes.
     This file is located in the user's home directory
     """
     Get file with list of SALOME processes.
     This file is located in the user's home directory
@@ -42,69 +54,90 @@ def getPiDict(port,appname='salome',full=True,hidden=True):
     - hidden  : if True, file name is prefixed with . (dot) symbol; this internal parameter is used
     to support compatibility with older versions of SALOME
     """
     - hidden  : if True, file name is prefixed with . (dot) symbol; this internal parameter is used
     to support compatibility with older versions of SALOME
     """
-    from salome_utils import generateFileName, getTmpDir
+    # bug fix: ensure port is an integer
+    # Note: this function is also called with port='#####' !!!
+    try:
+        port = int(port)
+    except:
+        pass
+
+    from salome_utils import generateFileName, getLogDir
     dir = ""
     dir = ""
+    if not hostname:
+        hostname = os.getenv("NSHOST")
+        if hostname: hostname = hostname.split(".")[0]
+        pass
     if full:
         # full path to the pidict file is requested
         if hidden:
             # new-style dot-prefixed pidict files
             # are in the system-dependant temporary diretory
     if full:
         # full path to the pidict file is requested
         if hidden:
             # new-style dot-prefixed pidict files
             # are in the system-dependant temporary diretory
-            dir = getTmpDir()
+            dir = getLogDir()
         else:
             # old-style non-dot-prefixed pidict files
             # are in the user's home directory
             dir = os.getenv("HOME")
             pass
         pass
         else:
             # old-style non-dot-prefixed pidict files
             # are in the user's home directory
             dir = os.getenv("HOME")
             pass
         pass
+
     return generateFileName(dir,
                             suffix="pidict",
                             hidden=hidden,
                             with_username=True,
     return generateFileName(dir,
                             suffix="pidict",
                             hidden=hidden,
                             with_username=True,
-                            with_hostname=True,
+                            with_hostname=hostname or True,
                             with_port=port,
                             with_app=appname.upper())
 
 def appliCleanOmniOrbConfig(port):
     """
     Remove omniorb config files related to the port in SALOME application:
                             with_port=port,
                             with_app=appname.upper())
 
 def appliCleanOmniOrbConfig(port):
     """
     Remove omniorb config files related to the port in SALOME application:
-    - ${HOME}/${APPLI}/USERS/.omniORB_${USER}_${HOSTNAME}_${NSPORT}.cfg
-    - ${HOME}/${APPLI}/USERS/.omniORB_${USER}_last.cfg
+    - ${OMNIORB_USER_PATH}/.omniORB_${USER}_${HOSTNAME}_${NSPORT}.cfg
+    - ${OMNIORB_USER_PATH}/.omniORB_${USER}_last.cfg
     the last is removed only if the link points to the first file.
     """
     the last is removed only if the link points to the first file.
     """
-    from salome_utils import generateFileName
-    home  = os.getenv("HOME")
-    appli = os.getenv("APPLI")
-    if appli is None:
+    if verbose():
+        print("clean OmniOrb config for port %s"%port)
+
+    from salome_utils import generateFileName, getUserName
+    omniorbUserPath = os.getenv("OMNIORB_USER_PATH")
+    if omniorbUserPath is None:
         #Run outside application context
         pass
     else:
         #Run outside application context
         pass
     else:
-        dir = os.path.join(home, appli,"USERS")
-        omniorb_config      = generateFileName(dir, prefix="omniORB",
+        omniorb_config      = generateFileName(omniorbUserPath, prefix="omniORB",
                                                extension="cfg",
                                                hidden=True,
                                                with_username=True,
                                                with_hostname=True,
                                                with_port=port)
                                                extension="cfg",
                                                hidden=True,
                                                with_username=True,
                                                with_hostname=True,
                                                with_port=port)
-        last_running_config = generateFileName(dir, prefix="omniORB",
+        last_running_config = generateFileName(omniorbUserPath, prefix="omniORB",
                                                with_username=True,
                                                suffix="last",
                                                extension="cfg",
                                                hidden=True)
         if os.access(last_running_config,os.F_OK):
                                                with_username=True,
                                                suffix="last",
                                                extension="cfg",
                                                hidden=True)
         if os.access(last_running_config,os.F_OK):
-            pointedPath = os.readlink(last_running_config)
-            if pointedPath[0] != '/':
-                pointedPath=os.path.join(os.path.dirname(last_running_config), pointedPath)
-            if pointedPath == omniorb_config:
-                os.unlink(last_running_config)
+            if not sys.platform == 'win32':
+                pointedPath = os.readlink(last_running_config)
+                if pointedPath[0] != '/':
+                    pointedPath=os.path.join(os.path.dirname(last_running_config), pointedPath)
+                    pass
+                if pointedPath == omniorb_config:
+                    os.unlink(last_running_config)
+                    pass
+                pass
+            else:
+                os.remove(last_running_config)
                 pass
             pass
                 pass
             pass
+
         if os.access(omniorb_config,os.F_OK):
             os.remove(omniorb_config)
             pass
 
         if os.access(omniorb_config,os.F_OK):
             os.remove(omniorb_config)
             pass
 
+        if os.path.lexists(last_running_config):return
+
         #try to relink last.cfg to an existing config file if any
         #try to relink last.cfg to an existing config file if any
-        files = glob.glob(os.path.join(os.environ["HOME"],Utils_Identity.getapplipath(),
-                                       "USERS",".omniORB_"+salome_utils.getUserName()+"_*.cfg"))
+        files = glob.glob(os.path.join(omniorbUserPath,".omniORB_"+getUserName()+"_*.cfg"))
         current_config=None
         current=0
         for f in files:
         current_config=None
         current=0
         for f in files:
@@ -113,125 +146,271 @@ def appliCleanOmniOrbConfig(port):
             current=stat.st_atime
             current_config=f
         if current_config:
             current=stat.st_atime
             current_config=f
         if current_config:
-          os.symlink(os.path.normpath(current_config), last_running_config)
-
+          if sys.platform == "win32":
+            import shutil
+            shutil.copyfile(os.path.normpath(current_config), last_running_config)
+            pass
+          else:
+            os.symlink(os.path.normpath(current_config), last_running_config)
+            pass
+          pass
         pass
     pass
 
 ########## kills all salome processes with the given port ##########
 
         pass
     pass
 
 ########## kills all salome processes with the given port ##########
 
-def killMyPort(port):
+def shutdownMyPort(port, cleanup=True):
     """
     """
-    Kill SALOME session running on the specified port.
+    Shutdown SALOME session running on the specified port.
     Parameters:
     - port - port number
     """
     Parameters:
     - port - port number
     """
-    # new-style dot-prefixed pidict file
-    filedict = getPiDict(port, hidden=True)
-    # provide compatibility with old-style pidict file (not dot-prefixed)
-    if not os.path.exists(filedict): filedict = getPiDict(port, hidden=False)
-    #
+    if not port: return
+    # bug fix: ensure port is an integer
+    port = int(port)
+
     try:
     try:
-        fpid = open(filedict, 'r')
-        #
-        from salome_utils import generateFileName
-        fpidomniNames = generateFileName(os.path.join('/tmp/logs', os.getenv('USER')),
-                                         prefix="",
-                                         suffix="Pid_omniNames",
-                                         extension="log",
-                                         with_port=port)
-        if not sys.platform == 'win32':        
-            cmd = 'pid=`ps -eo pid,command | egrep "[0-9] omniNames -start %s"` ; echo $pid > %s' % ( str(port), fpidomniNames )
-            a = os.system(cmd)
-            pass
-        try:
-            fpidomniNamesFile = open(fpidomniNames)
-            lines = fpidomniNamesFile.readlines()
-            fpidomniNamesFile.close()
-            os.remove(fpidomniNames)
-            for l in lines:
-                try:
-                    pidfield = l.split()[0] # pid should be at the first position
-                    if sys.platform == "win32":
-                        import win32pm
-                        if verbose(): print 'stop process '+pidfield+' : omniNames'
-                        win32pm.killpid(int(pidfield),0)
-                    else:
-                        if verbose(): print 'stop process '+pidfield+' : omniNames'
-                        os.kill(int(pidfield),signal.SIGKILL)
-                        pass
-                    pass
-                except:
-                    pass
-                pass
-            pass
-        except:
+        from PortManager import releasePort
+        releasePort(port)
+    except ImportError:
+        pass
+
+    from salome_utils import generateFileName
+
+    # set OMNIORB_CONFIG variable to the proper file
+    omniorbUserPath = os.getenv("OMNIORB_USER_PATH")
+    kwargs = {}
+    if omniorbUserPath is not None:
+        kwargs["with_username"]=True
+    else:
+        omniorbUserPath = os.path.realpath(os.path.expanduser('~'))
+    omniorb_config = generateFileName(omniorbUserPath, prefix="omniORB",
+                                      extension="cfg",
+                                      hidden=True,
+                                      with_hostname=True,
+                                      with_port=port,
+                                      **kwargs)
+    os.environ['OMNIORB_CONFIG'] = omniorb_config
+    os.environ['NSPORT'] = str(port)
+
+    # give the chance to the servers to shutdown properly
+    try:
+        import time
+        from omniORB import CORBA
+
+        from LifeCycleCORBA import LifeCycleCORBA
+        # shutdown all
+        orb = CORBA.ORB_init([''], CORBA.ORB_ID)
+        lcc = LifeCycleCORBA(orb) # see (1)
+        print("Terminating SALOME on port %s..."%(port))
+        lcc.shutdownServers()
+        # give some time to shutdown to complete
+        time.sleep(1)
+        # shutdown omniNames
+        if cleanup:
+            lcc.killOmniNames()
+            time.sleep(1)
             pass
             pass
-        #
-        try:
+        pass
+    except:
+        pass
+    sys.exit(0) # see (1)
+    pass
+# (1) If --shutdown-servers option is set to 1, session close procedure is
+# called twice: first explicitly by salome command, second by automatic
+# atexit to handle Ctrl-C. During second call, LCC does not exist anymore and
+# a RuntimeError is raised; we explicitly exit this function with code 0 to
+# prevent parent thread from crashing.
+
+def __killMyPort(port, filedict):
+    # bug fix: ensure port is an integer
+    if port:
+        port = int(port)
+
+    try:
+        with open(filedict, 'rb') as fpid:
             process_ids=pickle.load(fpid)
             process_ids=pickle.load(fpid)
-            fpid.close()
             for process_id in process_ids:
             for process_id in process_ids:
-                for pid, cmd in process_id.items():
-                    if verbose(): print "stop process %s : %s"% (pid, cmd[0])
+                for pid, cmd in list(process_id.items()):
+                    if verbose(): print("stop process %s : %s"% (pid, cmd[0]))
                     try:
                     try:
-                        if sys.platform == "win32":
-                            import win32pm
-                            win32pm.killpid(int(pid),0)
-                        else:
-                            os.kill(int(pid),signal.SIGKILL)
-                            pass
-                        pass
+                        from salome_utils import killpid
+                        killpid(int(pid))
                     except:
                     except:
-                        if verbose(): print "  ------------------ process %s : %s not found"% (pid, cmd[0])
+                        if verbose(): print("  ------------------ process %s : %s not found"% (pid, cmd[0]))
                         pass
                         pass
-                    pass # for pid, cmd ...
+                    pass # for pid ...
                 pass # for process_id ...
                 pass # for process_id ...
-            pass # try...
-        except:
-            pass
-        #
-        os.remove(filedict)
-        cmd='ps -eo pid,command | egrep "[0-9] omniNames -start '+str(port)+'" | sed -e "s%[^0-9]*\([0-9]*\) .*%\\1%g"'
-        pid = commands.getoutput(cmd)
-        a = ""
-        while pid and len(a.split()) < 2:
-            a = commands.getoutput("kill -9 " + pid)
-            pid = commands.getoutput(cmd)
-            #print pid
-            pass
+            # end with
+    except:
+        print("Cannot find or open SALOME PIDs file for port", port)
         pass
         pass
+    os.remove(filedict)
+    pass
+#
+
+def __guessPiDictFilename(port):
+    from salome_utils import getShortHostName, getHostName
+    filedicts = [
+        # new-style dot-prefixed pidict file
+        getPiDict(port, hidden=True),
+        # provide compatibility with old-style pidict file (not dot-prefixed)
+        getPiDict(port, hidden=False),
+        # provide compatibility with old-style pidict file (short hostname)
+        getPiDict(port, hidden=True, hostname=getShortHostName()),
+        # provide compatibility with old-style pidict file (not dot-prefixed, short hostname
+        getPiDict(port, hidden=False, hostname=getShortHostName()),
+        # provide compatibility with old-style pidict file (long hostname)
+        getPiDict(port, hidden=True, hostname=getHostName()),
+        # provide compatibility with old-style pidict file (not dot-prefixed, long hostname)
+        getPiDict(port, hidden=False, hostname=getHostName())
+        ]
+
+    log_msg = ""
+    for filedict in filedicts:
+        log_msg += "Trying %s..."%filedict
+        if os.path.exists(filedict):
+            log_msg += "   ... OK\n"
+            break
+        else:
+            log_msg += "   ... not found\n"
+
+    if verbose():
+        print(log_msg)
+
+    return filedict
+#
+
+def killMyPort(port):
+    """
+    Kill SALOME session running on the specified port.
+    Parameters:
+    - port - port number
+    """
+    # bug fix: ensure port is an integer
+    if port:
+        port = int(port)
+
+    try:
+        import PortManager # do not remove! Test for PortManager availability!
+        filedict = getPiDict(port)
+        if not os.path.isfile(filedict): # removed by previous call, see (1)
+            if verbose():
+                print("SALOME on port %s: already removed by previous call"%port)
+            # Remove port from PortManager config file
+            try:
+                from PortManager import releasePort
+                if verbose():
+                    print("Removing port from PortManager configuration file")
+                releasePort(port)
+            except ImportError:
+                pass
+            return
     except:
     except:
-        print "Cannot find or open SALOME PIDs file for port", port
         pass
         pass
+
+    # try to shutdown session normally
+    import threading, time
+    threading.Thread(target=shutdownMyPort, args=(port,True)).start()
+    time.sleep(3) # wait a little, then kill processes (should be done if shutdown procedure hangs up)
+
+    try:
+        import PortManager # do not remove! Test for PortManager availability!
+        filedict = getPiDict(port)
+        #filedict = __guessPiDictFilename(port)
+        import glob
+        all_files = glob.glob("%s*"%filedict)
+        for f in all_files:
+            __killMyPort(port, f)
+    except ImportError:
+        filedict = __guessPiDictFilename(port)
+        __killMyPort(port, filedict)
     #
     #
+
     appliCleanOmniOrbConfig(port)
     pass
     appliCleanOmniOrbConfig(port)
     pass
-            
-def killNotifdAndClean(port):
+
+def cleanApplication(port):
     """
     """
-    Kill notifd daemon and clean application running on the specified port.
+    Clean application running on the specified port.
     Parameters:
     - port - port number
     """
     Parameters:
     - port - port number
     """
+    # bug fix: ensure port is an integer
+    if port:
+        port = int(port)
+
     try:
     try:
-      filedict=getPiDict(port)
-      f=open(filedict, 'r')
-      pids=pickle.load(f)
-      for d in pids:
-        for pid,process in d.items():
-          if 'notifd' in process:
-            cmd='kill -9 %d'% pid
-            os.system(cmd)
-      os.remove(filedict)
+        filedict=getPiDict(port)
+        os.remove(filedict)
     except:
       #import traceback
       #traceback.print_exc()
       pass
 
     appliCleanOmniOrbConfig(port)
     except:
       #import traceback
       #traceback.print_exc()
       pass
 
     appliCleanOmniOrbConfig(port)
-    
+    pass
+
+def killMyPortSpy(pid, port):
+    dt = 1.0
+    while 1:
+        from salome_utils import killpid
+        ret = killpid(int(pid), 0)
+        if ret == 0:
+            break
+        elif ret < 0:
+            return
+        from time import sleep
+        sleep(dt)
+        pass
+    filedict = getPiDict(port, hidden=True)
+    if not os.path.exists(filedict):
+        return
+    try:
+        import omniORB
+        orb = omniORB.CORBA.ORB_init(sys.argv, omniORB.CORBA.ORB_ID)
+        import SALOME_NamingServicePy
+        ns = SALOME_NamingServicePy.SALOME_NamingServicePy_i(orb)
+        import SALOME #@UnresolvedImport @UnusedImport
+        session = ns.Resolve("/Kernel/Session")
+        assert session
+    except:
+        return
+    try:
+        status = session.GetStatSession()
+    except:
+        # -- session is in naming service but has crash
+        status = None
+        pass
+    if status:
+        if not status.activeGUI:
+            return
+        pass
+    killMyPort(port)
+    return
+
 if __name__ == "__main__":
 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
+        sys.exit(0)
+        pass
+    try:
+        from salomeContextUtils import setOmniOrbUserPath #@UnresolvedImport
+        setOmniOrbUserPath()
+    except Exception as e:
+        print(e)
+        sys.exit(1)
     for port in sys.argv[1:]:
         killMyPort(port)
         pass
     for port in sys.argv[1:]:
         killMyPort(port)
         pass