-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- coding: iso-8859-1 -*-
-# Copyright (C) 2007-2014 CEA/DEN, EDF R&D, OPEN CASCADE
+# Copyright (C) 2007-2023 CEA, EDF, OPEN CASCADE
#
# 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.
+# 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
#
# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
#
-import multiprocessing
-import time
-import socket
-
import os
import sys
-import threading
-import SocketServer
+import psutil
+
+from socket import AF_INET, SOCK_STREAM
-try:
- import cPickle as pickle
-except:
- import pickle
+import pickle
-import struct
-import ctypes
+__PORT_MIN_NUMBER = 2810
+__PORT_MAX_NUMBER = 2910
import logging
def createLogger():
logger = logging.getLogger(__name__)
-# logger.setLevel(logging.DEBUG)
+ #logger.setLevel(logging.DEBUG)
+ logger.setLevel(logging.INFO)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
- formatter = logging.Formatter("%(levelname)s:%(threadName)s:%(message)s")
+ formatter = logging.Formatter("%(levelname)s:%(threadName)s:%(pathname)s[%(lineno)s]%(message)s")
ch.setFormatter(formatter)
logger.addHandler(ch)
return logger
#
logger = createLogger()
-
-if sys.platform == 'win32':
- import multiprocessing.reduction # make sockets pickable/inheritable
-
-multiprocessing.freeze_support() # Add support for when a program which uses multiprocessing has been frozen to produce a Windows executable.
-
#------------------------------------
-# A file locker (Linux only)
-import fcntl
-class PortManagerLock:
- def __init__(self, filename, readonly=False, blocking=True):
- # This will create it if it does not exist already
- logger.debug("Create lock on %s"%filename)
- self.__readonly = readonly
- self.__blocking = blocking
- self.__filename = filename
- flag = 'w'
- if self.__readonly:
- flag = 'r'
- self.handle = open(self.__filename, 'a+')
-
- def acquire(self):
- mode = fcntl.LOCK_EX
- if not self.__blocking: # Raise an IOError exception if file has already been locked
- mode = mode | fcntl.LOCK_NB
- fcntl.flock(self.handle, mode)
- logger.debug("lock acquired %s"%self.__blocking)
-
- def release(self):
- fcntl.flock(self.handle, fcntl.LOCK_UN)
- logger.debug("lock released")
-
- def __del__(self):
- logger.debug("Close lock file")
- self.handle.close()
- os.remove(self.__filename)
+# A file locker
+def __acquire_lock(lock):
+ logger.debug("ACQUIRE LOCK")
+ if sys.platform == "win32":
+ import msvcrt
+ # lock 1 byte: file is supposed to be zero-byte long
+ msvcrt.locking(lock.fileno(), msvcrt.LK_LOCK, 1)
+ else:
+ import fcntl
+ fcntl.flock(lock, fcntl.LOCK_EX)
+ logger.debug("LOCK ACQUIRED")
#
+def __release_lock(lock):
+ logger.debug("RELEASE LOCK")
+ if sys.platform == "win32":
+ import msvcrt
+ msvcrt.locking(lock.fileno(), msvcrt.LK_UNLCK, 1)
+ else:
+ import fcntl
+ fcntl.flock(lock, fcntl.LOCK_UN)
+ logger.debug("LOCK RELEASED")
+#
+#------------------------------------
def _getConfigurationFilename():
omniorbUserPath = os.getenv("OMNIORB_USER_PATH")
from salome_utils import generateFileName
portmanager_config = generateFileName(omniorbUserPath,
- prefix="omniORB",
+ prefix="salome",
suffix="PortManager",
extension="cfg",
hidden=True)
- lock_file = portmanager_config + "-lock"
+ import tempfile
+ temp = tempfile.NamedTemporaryFile()
+ lock_file = os.path.join(os.path.dirname(temp.name), ".salome", ".PortManager.lock")
+ try:
+ oldmask = os.umask(0)
+ os.makedirs(os.path.dirname(lock_file))
+ except IOError:
+ pass
+ finally:
+ os.umask(oldmask)
+ temp.close()
+
return (portmanager_config, lock_file)
#
-def __isPortUsed(port, busy_ports):
+def __isPortUsed(port, config):
+ busy_ports = []
+ for ports in config.values():
+ busy_ports += ports
return (port in busy_ports) or __isNetworkConnectionActiveOnPort(port)
#
def __isNetworkConnectionActiveOnPort(port):
- # :NOTE: Under windows:
- # netstat options -l and -t are unavailable
- # grep command is unavailable
- from subprocess import Popen, PIPE
- (stdout, stderr) = Popen(['netstat','-an'], stdout=PIPE).communicate()
- import StringIO
- buf = StringIO.StringIO(stdout)
- 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
-#
+ # psutil realization
+ return port in [c.laddr.port for c in psutil.net_connections(kind='inet') if \
+ (c.family, c.type, c.status) == (AF_INET, SOCK_STREAM, "LISTEN")]
+ #
-def getPort(preferedPort=None):
+def getPort(preferredPort=None):
logger.debug("GET PORT")
config_file, lock_file = _getConfigurationFilename()
- with open(lock_file, 'w') as lock:
+ oldmask = os.umask(0)
+ with open(lock_file, 'wb') as lock:
# acquire lock
- fcntl.flock(lock, fcntl.LOCK_EX)
+ __acquire_lock(lock)
# read config
- config = {'busy_ports':[]}
+ config = {}
logger.debug("read configuration file")
try:
- with open(config_file, 'r') as f:
+ with open(config_file, 'rb') as f:
config = pickle.load(f)
- except IOError: # empty file
- pass
+ except Exception:
+ logger.debug("Problem loading PortManager file: %s"%config_file)
+ # In this case config dictionary is reset
- logger.debug("load busy_ports: %s"%str(config["busy_ports"]))
+ logger.debug("load config: %s"%str(config))
+ appli_path = os.getenv("ABSOLUTE_APPLI_PATH", "unknown")
+ try:
+ config[appli_path]
+ except KeyError:
+ config[appli_path] = []
# append port
- busy_ports = config["busy_ports"]
- port = preferedPort
- if not port or __isPortUsed(port, busy_ports):
- port = 2810
- while __isPortUsed(port, busy_ports):
- if port == 2810+100:
+ port = preferredPort
+ if not port or __isPortUsed(port, config):
+ port = __PORT_MIN_NUMBER
+ while __isPortUsed(port, config):
+ if port == __PORT_MAX_NUMBER:
msg = "\n"
msg += "Can't find a free port to launch omniNames\n"
msg += "Try to kill the running servers and then launch SALOME again.\n"
- raise RuntimeError, msg
+ raise RuntimeError(msg)
+ logger.debug("Port %s seems to be busy"%str(port))
port = port + 1
logger.debug("found free port: %s"%str(port))
- config["busy_ports"].append(port)
+ config[appli_path].append(port)
# write config
- logger.debug("write busy_ports: %s"%str(config["busy_ports"]))
+ logger.debug("write config: %s"%str(config))
try:
- with open(config_file, 'w') as f:
- pickle.dump(config, f)
+ with open(config_file, 'wb') as f:
+ pickle.dump(config, f, protocol=0)
except IOError:
pass
# release lock
- fcntl.flock(lock, fcntl.LOCK_UN)
+ __release_lock(lock)
+ #
- logger.debug("get port: %s"%str(port))
- return port
+ os.umask(oldmask)
+ logger.debug("get port: %s"%str(port))
+ return port
#
def releasePort(port):
logger.debug("RELEASE PORT (%s)"%port)
config_file, lock_file = _getConfigurationFilename()
- with open(lock_file, 'w') as lock:
+ oldmask = os.umask(0)
+ with open(lock_file, 'wb') as lock:
# acquire lock
- fcntl.flock(lock, fcntl.LOCK_EX)
+ __acquire_lock(lock)
# read config
- config = {'busy_ports':[]}
+ config = {}
logger.debug("read configuration file")
try:
- with open(config_file, 'r') as f:
+ with open(config_file, 'rb') as f:
config = pickle.load(f)
except IOError: # empty file
pass
- logger.debug("load busy_ports: %s"%str(config["busy_ports"]))
+ logger.debug("load config: %s"%str(config))
+ appli_path = os.getenv("ABSOLUTE_APPLI_PATH", "unknown")
+ try:
+ config[appli_path]
+ except KeyError:
+ config[appli_path] = []
# remove port from list
- busy_ports = config["busy_ports"]
-
- if port in busy_ports:
- busy_ports.remove(port)
- config["busy_ports"] = busy_ports
+ ports_info = config[appli_path]
+ config[appli_path] = [x for x in ports_info if x != port]
# write config
- logger.debug("write busy_ports: %s"%str(config["busy_ports"]))
+ logger.debug("write config: %s"%str(config))
try:
- with open(config_file, 'w') as f:
- pickle.dump(config, f)
+ with open(config_file, 'wb') as f:
+ pickle.dump(config, f, protocol=0)
except IOError:
pass
# release lock
- fcntl.flock(lock, fcntl.LOCK_UN)
+ __release_lock(lock)
logger.debug("released port port: %s"%str(port))
+
+ os.umask(oldmask)
#
def getBusyPorts():
config_file, lock_file = _getConfigurationFilename()
- with open(lock_file, 'w') as lock:
+ oldmask = os.umask(0)
+ with open(lock_file, 'wb') as lock:
# acquire lock
- fcntl.flock(lock, fcntl.LOCK_EX)
+ __acquire_lock(lock)
# read config
- config = {'busy_ports':[]}
+ config = {}
logger.debug("read configuration file")
try:
- with open(config_file, 'r') as f:
+ with open(config_file, 'rb') as f:
config = pickle.load(f)
except IOError: # empty file
pass
- logger.debug("load busy_ports: %s"%str(config["busy_ports"]))
+ logger.debug("load config: %s"%str(config))
+ appli_path = os.getenv("ABSOLUTE_APPLI_PATH", "unknown")
+ try:
+ config[appli_path]
+ except KeyError:
+ config[appli_path] = []
+
+ # Scan all possible ports to determine which ones are owned by other applications
+ ports_info = { 'this': [], 'other': [] }
+ my_busy_ports = config[appli_path]
+ for port in range(__PORT_MIN_NUMBER, __PORT_MAX_NUMBER):
+ if __isPortUsed(port, config):
+ logger.debug("Port %s seems to be busy"%str(port))
+ if port in my_busy_ports:
+ ports_info["this"].append(port)
+ else:
+ ports_info["other"].append(port)
+
+ logger.debug("all busy_ports: %s"%str(ports_info))
+
+ sorted_ports = { 'this': sorted(ports_info['this']),
+ 'other': sorted(ports_info['other']) }
- busy_ports = config["busy_ports"]
# release lock
- fcntl.flock(lock, fcntl.LOCK_UN)
+ __release_lock(lock)
- return busy_ports
+ os.umask(oldmask)
+ return sorted_ports
#