Salome HOME
71143075f1f895e6557d6fa78e855445ca7a1b7a
[modules/kernel.git] / bin / PortManager.py
1 #!/usr/bin/env python
2 #  -*- coding: iso-8859-1 -*-
3 # Copyright (C) 2007-2014  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.
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 multiprocessing
25 import time
26 import socket
27
28 import os
29 import sys
30 import threading
31 import SocketServer
32
33 try:
34   import cPickle as pickle
35 except:
36   import pickle
37
38 import struct
39 import ctypes
40
41 import logging
42 def createLogger():
43   logger = logging.getLogger(__name__)
44 #  logger.setLevel(logging.DEBUG)
45   ch = logging.StreamHandler()
46   ch.setLevel(logging.DEBUG)
47   formatter = logging.Formatter("%(levelname)s:%(threadName)s:%(message)s")
48   ch.setFormatter(formatter)
49   logger.addHandler(ch)
50   return logger
51 #
52 logger = createLogger()
53
54
55 if sys.platform == 'win32':
56   import multiprocessing.reduction    # make sockets pickable/inheritable
57
58 multiprocessing.freeze_support() # Add support for when a program which uses multiprocessing has been frozen to produce a Windows executable.
59
60 #------------------------------------
61 # A file locker (Linux only)
62 import fcntl
63 class PortManagerLock:
64   def __init__(self, filename, readonly=False, blocking=True):
65     # This will create it if it does not exist already
66     logger.debug("Create lock on %s"%filename)
67     self.__readonly = readonly
68     self.__blocking = blocking
69     self.__filename = filename
70     flag = 'w'
71     if self.__readonly:
72       flag = 'r'
73     self.handle = open(self.__filename, 'a+')
74
75   def acquire(self):
76     mode = fcntl.LOCK_EX
77     if not self.__blocking: # Raise an IOError exception if file has already been locked
78       mode = mode | fcntl.LOCK_NB
79     fcntl.flock(self.handle, mode)
80     logger.debug("lock acquired %s"%self.__blocking)
81
82   def release(self):
83     fcntl.flock(self.handle, fcntl.LOCK_UN)
84     logger.debug("lock released")
85
86   def __del__(self):
87     logger.debug("Close lock file")
88     self.handle.close()
89     os.remove(self.__filename)
90 #
91
92 def _getConfigurationFilename():
93   omniorbUserPath = os.getenv("OMNIORB_USER_PATH")
94
95   from salome_utils import generateFileName
96   portmanager_config = generateFileName(omniorbUserPath,
97                                         prefix="omniORB",
98                                         suffix="PortManager",
99                                         extension="cfg",
100                                         hidden=True)
101   lock_file = portmanager_config + "-lock"
102   return (portmanager_config, lock_file)
103 #
104
105 def __isPortUsed(port, busy_ports):
106   return (port in busy_ports) or __isNetworkConnectionActiveOnPort(port)
107 #
108
109 def __isNetworkConnectionActiveOnPort(port):
110   # :NOTE: Under windows:
111   #        netstat options -l and -t are unavailable
112   #        grep command is unavailable
113   from subprocess import Popen, PIPE
114   (stdout, stderr) = Popen(['netstat','-an'], stdout=PIPE).communicate()
115   import StringIO
116   buf = StringIO.StringIO(stdout)
117   ports = buf.readlines()
118   # search for TCP - LISTEN connections
119   import re
120   regObj = re.compile( ".*tcp.*:([0-9]+).*:.*listen", re.IGNORECASE );
121   for item in ports:
122     try:
123       p = int(regObj.match(item).group(1))
124       if p == port: return True
125     except:
126       pass
127 #
128
129 def getPort(preferedPort=None):
130   logger.debug("GET PORT")
131
132   config_file, lock_file = _getConfigurationFilename()
133   with open(lock_file, 'w') as lock:
134     # acquire lock
135     fcntl.flock(lock, fcntl.LOCK_EX)
136
137     # read config
138     config = {'busy_ports':[]}
139     logger.debug("read configuration file")
140     try:
141       with open(config_file, 'r') as f:
142         config = pickle.load(f)
143     except IOError: # empty file
144       pass
145
146     logger.debug("load busy_ports: %s"%str(config["busy_ports"]))
147
148     # append port
149     busy_ports = config["busy_ports"]
150     port = preferedPort
151     if not port or __isPortUsed(port, busy_ports):
152       port = 2810
153       while __isPortUsed(port, busy_ports):
154         if port == 2810+100:
155           msg  = "\n"
156           msg += "Can't find a free port to launch omniNames\n"
157           msg += "Try to kill the running servers and then launch SALOME again.\n"
158           raise RuntimeError, msg
159         port = port + 1
160     logger.debug("found free port: %s"%str(port))
161     config["busy_ports"].append(port)
162
163     # write config
164     logger.debug("write busy_ports: %s"%str(config["busy_ports"]))
165     try:
166       with open(config_file, 'w') as f:
167         pickle.dump(config, f)
168     except IOError:
169       pass
170
171     # release lock
172     fcntl.flock(lock, fcntl.LOCK_UN)
173
174     logger.debug("get port: %s"%str(port))
175     return port
176 #
177
178 def releasePort(port):
179   port = int(port)
180   logger.debug("RELEASE PORT (%s)"%port)
181
182   config_file, lock_file = _getConfigurationFilename()
183   with open(lock_file, 'w') as lock:
184     # acquire lock
185     fcntl.flock(lock, fcntl.LOCK_EX)
186
187     # read config
188     config = {'busy_ports':[]}
189     logger.debug("read configuration file")
190     try:
191       with open(config_file, 'r') as f:
192         config = pickle.load(f)
193     except IOError: # empty file
194       pass
195
196     logger.debug("load busy_ports: %s"%str(config["busy_ports"]))
197
198     # remove port from list
199     busy_ports = config["busy_ports"]
200
201     if port in busy_ports:
202       busy_ports.remove(port)
203       config["busy_ports"] = busy_ports
204
205     # write config
206     logger.debug("write busy_ports: %s"%str(config["busy_ports"]))
207     try:
208       with open(config_file, 'w') as f:
209         pickle.dump(config, f)
210     except IOError:
211       pass
212
213     # release lock
214     fcntl.flock(lock, fcntl.LOCK_UN)
215
216     logger.debug("released port port: %s"%str(port))
217 #
218
219 def getBusyPorts():
220   config_file, lock_file = _getConfigurationFilename()
221   with open(lock_file, 'w') as lock:
222     # acquire lock
223     fcntl.flock(lock, fcntl.LOCK_EX)
224
225     # read config
226     config = {'busy_ports':[]}
227     logger.debug("read configuration file")
228     try:
229       with open(config_file, 'r') as f:
230         config = pickle.load(f)
231     except IOError: # empty file
232       pass
233
234     logger.debug("load busy_ports: %s"%str(config["busy_ports"]))
235
236     busy_ports = config["busy_ports"]
237     # release lock
238     fcntl.flock(lock, fcntl.LOCK_UN)
239
240     return busy_ports
241 #