]> SALOME platform Git repositories - modules/kernel.git/blob - bin/PortManager.py
Salome HOME
e0584ac81529000f029ad5267f0b1b6a66dbd8b9
[modules/kernel.git] / bin / PortManager.py
1 #!/usr/bin/env python3
2 #  -*- coding: iso-8859-1 -*-
3 # Copyright (C) 2007-2019  CEA/DEN, EDF R&D, OPEN CASCADE
4 #
5 # Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
6 # CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
7 #
8 # This library is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU Lesser General Public
10 # License as published by the Free Software Foundation; either
11 # version 2.1 of the License, or (at your option) any later version.
12 #
13 # This library is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 # Lesser General Public License for more details.
17 #
18 # You should have received a copy of the GNU Lesser General Public
19 # License along with this library; if not, write to the Free Software
20 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
21 #
22 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 #
24 import os
25 import sys
26
27 try:
28   import cPickle as pickle #@UnusedImport
29 except:
30   import pickle #@Reimport
31
32 __PORT_MIN_NUMBER = 2810
33 __PORT_MAX_NUMBER = 2910
34
35 import logging
36 def createLogger():
37   logger = logging.getLogger(__name__)
38   #logger.setLevel(logging.DEBUG)
39   logger.setLevel(logging.INFO)
40   ch = logging.StreamHandler()
41   ch.setLevel(logging.DEBUG)
42   formatter = logging.Formatter("%(levelname)s:%(threadName)s:%(pathname)s[%(lineno)s]%(message)s")
43   ch.setFormatter(formatter)
44   logger.addHandler(ch)
45   return logger
46 #
47 logger = createLogger()
48
49 #------------------------------------
50 # A file locker
51 def __acquire_lock(lock):
52   logger.debug("ACQUIRE LOCK")
53   if sys.platform == "win32":
54     import msvcrt
55     # lock 1 byte: file is supposed to be zero-byte long
56     msvcrt.locking(lock.fileno(), msvcrt.LK_LOCK, 1)
57   else:
58     import fcntl
59     fcntl.flock(lock, fcntl.LOCK_EX)
60   logger.debug("LOCK ACQUIRED")
61 #
62 def __release_lock(lock):
63   logger.debug("RELEASE LOCK")
64   if sys.platform == "win32":
65     import msvcrt
66     msvcrt.locking(lock.fileno(), msvcrt.LK_UNLCK, 1)
67   else:
68     import fcntl
69     fcntl.flock(lock, fcntl.LOCK_UN)
70   logger.debug("LOCK RELEASED")
71 #
72 #------------------------------------
73
74 def _getConfigurationFilename():
75   omniorbUserPath = os.getenv("OMNIORB_USER_PATH")
76
77   from salome_utils import generateFileName
78   portmanager_config = generateFileName(omniorbUserPath,
79                                         prefix="salome",
80                                         suffix="PortManager",
81                                         extension="cfg",
82                                         hidden=True)
83   import tempfile
84   temp = tempfile.NamedTemporaryFile()
85   lock_file = os.path.join(os.path.dirname(temp.name), ".salome_PortManager.lock")
86   try:
87     with open(lock_file, 'wb'):
88       pass
89   except IOError:
90     pass
91   temp.close()
92
93   return (portmanager_config, lock_file)
94 #
95
96 def __isPortUsed(port, config):
97   busy_ports = []
98   for ports in config.values():
99     busy_ports += ports
100   return (port in busy_ports) or __isNetworkConnectionActiveOnPort(port)
101 #
102
103 def __isNetworkConnectionActiveOnPort(port):
104   # :NOTE: Under windows:
105   #        netstat options -l and -t are unavailable
106   #        grep command is unavailable
107   if sys.platform == "win32":
108     cmd = ['netstat','-a','-n','-p','tcp']
109   else:
110     cmd = ['netstat','-ant']
111     pass
112
113   err = None
114   try:
115     from subprocess import Popen, PIPE, STDOUT
116     p = Popen(cmd, stdout=PIPE, stderr=STDOUT)
117     out, err = p.communicate()
118   except:
119     print("Error when trying to access active network connections.")
120     if err: print(err)
121     import traceback
122     traceback.print_exc()
123     return False
124
125   from io import StringIO
126   buf = StringIO(out.decode('utf-8', 'ignore'))
127   ports = buf.readlines()
128   # search for TCP - LISTEN connections
129   import re
130   regObj = re.compile( ".*tcp.*:([0-9]+).*:.*listen", re.IGNORECASE );
131   for item in ports:
132     try:
133       p = int(regObj.match(item).group(1))
134       if p == port: return True
135     except:
136       pass
137   return False
138 #
139
140 def getPort(preferredPort=None):
141   logger.debug("GET PORT")
142
143   config_file, lock_file = _getConfigurationFilename()
144   oldmask = os.umask(0)
145   with open(lock_file, 'rb') as lock:
146     # acquire lock
147     __acquire_lock(lock)
148
149     # read config
150     config = {}
151     logger.debug("read configuration file")
152     try:
153       with open(config_file, 'rb') as f:
154         config = pickle.load(f)
155     except:
156       logger.info("Problem loading PortManager file: %s"%config_file)
157       # In this case config dictionary is reset
158
159     logger.debug("load config: %s"%str(config))
160     appli_path = os.getenv("ABSOLUTE_APPLI_PATH", "unknown")
161     try:
162         config[appli_path]
163     except KeyError:
164         config[appli_path] = []
165
166     # append port
167     port = preferredPort
168     if not port or __isPortUsed(port, config):
169       port = __PORT_MIN_NUMBER
170       while __isPortUsed(port, config):
171         if port == __PORT_MAX_NUMBER:
172           msg  = "\n"
173           msg += "Can't find a free port to launch omniNames\n"
174           msg += "Try to kill the running servers and then launch SALOME again.\n"
175           raise RuntimeError(msg)
176         logger.debug("Port %s seems to be busy"%str(port))
177         port = port + 1
178     logger.debug("found free port: %s"%str(port))
179     config[appli_path].append(port)
180
181     # write config
182     logger.debug("write config: %s"%str(config))
183     try:
184       with open(config_file, 'wb') as f:
185         pickle.dump(config, f, protocol=0)
186     except IOError:
187       pass
188
189     # release lock
190     __release_lock(lock)
191   #
192
193   os.umask(oldmask)
194   logger.debug("get port: %s"%str(port))
195   return port
196 #
197
198 def releasePort(port):
199   port = int(port)
200   logger.debug("RELEASE PORT (%s)"%port)
201
202   config_file, lock_file = _getConfigurationFilename()
203   oldmask = os.umask(0)
204   with open(lock_file, 'rb') as lock:
205     # acquire lock
206     __acquire_lock(lock)
207
208     # read config
209     config = {}
210     logger.debug("read configuration file")
211     try:
212       with open(config_file, 'rb') as f:
213         config = pickle.load(f)
214     except IOError: # empty file
215       pass
216
217     logger.debug("load config: %s"%str(config))
218     appli_path = os.getenv("ABSOLUTE_APPLI_PATH", "unknown")
219     try:
220         config[appli_path]
221     except KeyError:
222         config[appli_path] = []
223
224     # remove port from list
225     ports_info = config[appli_path]
226     config[appli_path] = [x for x in ports_info if x != port]
227
228     # write config
229     logger.debug("write config: %s"%str(config))
230     try:
231       with open(config_file, 'wb') as f:
232         pickle.dump(config, f, protocol=0)
233     except IOError:
234       pass
235
236     # release lock
237     __release_lock(lock)
238
239     logger.debug("released port port: %s"%str(port))
240
241   os.umask(oldmask)
242 #
243
244 def getBusyPorts():
245   config_file, lock_file = _getConfigurationFilename()
246   oldmask = os.umask(0)
247   with open(lock_file, 'wb') as lock:
248     # acquire lock
249     __acquire_lock(lock)
250
251     # read config
252     config = {}
253     logger.debug("read configuration file")
254     try:
255       with open(config_file, 'rb') as f:
256         config = pickle.load(f)
257     except IOError: # empty file
258       pass
259
260     logger.debug("load config: %s"%str(config))
261     appli_path = os.getenv("ABSOLUTE_APPLI_PATH", "unknown")
262     try:
263         config[appli_path]
264     except KeyError:
265         config[appli_path] = []
266
267     # Scan all possible ports to determine which ones are owned by other applications
268     ports_info = { 'this': [], 'other': [] }
269     my_busy_ports = config[appli_path]
270     for port in range(__PORT_MIN_NUMBER, __PORT_MAX_NUMBER):
271       if __isPortUsed(port, config):
272         logger.debug("Port %s seems to be busy"%str(port))
273         if port in my_busy_ports:
274           ports_info["this"].append(port)
275         else:
276           ports_info["other"].append(port)
277
278     logger.debug("all busy_ports: %s"%str(ports_info))
279
280     sorted_ports = { 'this': sorted(ports_info['this']),
281                      'other': sorted(ports_info['other']) }
282
283     # release lock
284     __release_lock(lock)
285
286   os.umask(oldmask)
287   return sorted_ports
288 #