Salome HOME
7e83d4696e5f6705b6db2e4329227693ce16ff5a
[modules/kernel.git] / bin / PortManager.py
1 #!/usr/bin/env python
2 #  -*- coding: iso-8859-1 -*-
3 # Copyright (C) 2007-2017  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:%(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="omniORB",
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), ".omniORB_PortManager.lock")
86   temp.close()
87
88   return (portmanager_config, lock_file)
89 #
90
91 def __isPortUsed(port, busy_ports):
92   return (port in busy_ports) or __isNetworkConnectionActiveOnPort(port)
93 #
94
95 def __isNetworkConnectionActiveOnPort(port):
96   # :NOTE: Under windows:
97   #        netstat options -l and -t are unavailable
98   #        grep command is unavailable
99   if sys.platform == "win32":
100     cmd = ['netstat','-a','-n','-p tcp']
101   else:
102     cmd = ['netstat','-ant']
103     pass
104
105   err = None
106   try:
107     from subprocess import Popen, PIPE, STDOUT
108     p = Popen(cmd, stdout=PIPE, stderr=STDOUT)
109     out, err = p.communicate()
110   except:
111     print "Error when trying to access active network connections."
112     if err: print err
113     import traceback
114     traceback.print_exc()
115     return False
116
117   import StringIO
118   buf = StringIO.StringIO(out)
119   ports = buf.readlines()
120   # search for TCP - LISTEN connections
121   import re
122   regObj = re.compile( ".*tcp.*:([0-9]+).*:.*listen", re.IGNORECASE );
123   for item in ports:
124     try:
125       p = int(regObj.match(item).group(1))
126       if p == port: return True
127     except:
128       pass
129   return False
130 #
131
132 def getPort(preferedPort=None):
133   logger.debug("GET PORT")
134
135   config_file, lock_file = _getConfigurationFilename()
136   oldmask = os.umask(0)
137   with open(lock_file, 'w') as lock:
138     # acquire lock
139     __acquire_lock(lock)
140
141     # read config
142     config = {'busy_ports':[]}
143     logger.debug("read configuration file")
144     try:
145       with open(config_file, 'r') as f:
146         config = pickle.load(f)
147     except:
148       logger.info("Problem loading PortManager file: %s"%config_file)
149       # In this case config dictionary is reset
150
151     logger.debug("load busy_ports: %s"%str(config["busy_ports"]))
152
153     # append port
154     busy_ports = config["busy_ports"]
155     port = preferedPort
156     if not port or __isPortUsed(port, busy_ports):
157       port = __PORT_MIN_NUMBER
158       while __isPortUsed(port, busy_ports):
159         if port == __PORT_MAX_NUMBER:
160           msg  = "\n"
161           msg += "Can't find a free port to launch omniNames\n"
162           msg += "Try to kill the running servers and then launch SALOME again.\n"
163           raise RuntimeError, msg
164         logger.debug("Port %s seems to be busy"%str(port))
165         port = port + 1
166     logger.debug("found free port: %s"%str(port))
167     config["busy_ports"].append(port)
168
169     # write config, for this application only (i.e. no 'other' ports)
170     logger.debug("write busy_ports: %s"%str(config["busy_ports"]))
171     try:
172       with open(config_file, 'w') as f:
173         pickle.dump(config, f)
174     except IOError:
175       pass
176
177     # release lock
178     __release_lock(lock)
179   #
180
181   os.umask(oldmask)
182   logger.debug("get port: %s"%str(port))
183   return port
184 #
185
186 def releasePort(port):
187   port = int(port)
188   logger.debug("RELEASE PORT (%s)"%port)
189
190   config_file, lock_file = _getConfigurationFilename()
191   oldmask = os.umask(0)
192   with open(lock_file, 'w') as lock:
193     # acquire lock
194     __acquire_lock(lock)
195
196     # read config
197     config = {'busy_ports':[]}
198     logger.debug("read configuration file")
199     try:
200       with open(config_file, 'r') as f:
201         config = pickle.load(f)
202     except IOError: # empty file
203       pass
204
205     logger.debug("load busy_ports: %s"%str(config["busy_ports"]))
206
207     # remove port from list
208     ports_info = config["busy_ports"]
209     config["busy_ports"] = [x for x in ports_info if x != port]
210
211     # write config, for this application only (i.e. no 'other' ports)
212     logger.debug("write busy_ports: %s"%str(config["busy_ports"]))
213     try:
214       with open(config_file, 'w') as f:
215         pickle.dump(config, f)
216     except IOError:
217       pass
218
219     # release lock
220     __release_lock(lock)
221
222     logger.debug("released port port: %s"%str(port))
223
224   os.umask(oldmask)
225 #
226
227 def getBusyPorts():
228   config_file, lock_file = _getConfigurationFilename()
229   oldmask = os.umask(0)
230   with open(lock_file, 'w') as lock:
231     # acquire lock
232     __acquire_lock(lock)
233
234     # read config
235     config = {'busy_ports':[]}
236     logger.debug("read configuration file")
237     try:
238       with open(config_file, 'r') as f:
239         config = pickle.load(f)
240     except IOError: # empty file
241       pass
242
243     logger.debug("load busy_ports: %s"%str(config["busy_ports"]))
244
245     # Scan all possible ports to determine which ones are owned by other applications
246     ports_info = { 'this': [], 'other': [] }
247     busy_ports = config["busy_ports"]
248     for port in range(__PORT_MIN_NUMBER, __PORT_MAX_NUMBER):
249       if __isPortUsed(port, busy_ports):
250         logger.debug("Port %s seems to be busy"%str(port))
251         if port in busy_ports:
252           ports_info["this"].append(port)
253         else:
254           ports_info["other"].append(port)
255
256     logger.debug("all busy_ports: %s"%str(ports_info))
257
258     sorted_ports = { 'this': sorted(ports_info['this']),
259                      'other': sorted(ports_info['other']) }
260
261     # release lock
262     __release_lock(lock)
263
264   os.umask(oldmask)
265   return sorted_ports
266 #