Salome HOME
Copyright update 2021
[modules/kernel.git] / bin / salome_utils.py
1 #  -*- coding: iso-8859-1 -*-
2 # Copyright (C) 2007-2021  CEA/DEN, EDF R&D, OPEN CASCADE
3 #
4 # This library is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU Lesser General Public
6 # License as published by the Free Software Foundation; either
7 # version 2.1 of the License, or (at your option) any later version.
8 #
9 # This library is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 # Lesser General Public License for more details.
13 #
14 # You should have received a copy of the GNU Lesser General Public
15 # License along with this library; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
17 #
18 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
19 #
20
21 # ---
22 # File   : salome_utils.py
23 # Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
24 # ---
25
26 ## @package salome_utils
27 # \brief Set of utility functions used by SALOME python scripts.
28
29 #
30 # Exported functions
31 #
32
33 __all__ = [
34     'getORBcfgInfo',
35     'getHostFromORBcfg',
36     'getPortFromORBcfg',
37     'getUserName',
38     'getHostName',
39     'getShortHostName',
40     'getAppName',
41     'getPortNumber',
42     'getLogDir',
43     'getTmpDir',
44     'getHomeDir',
45     'generateFileName',
46     'makeTmpDir',
47     'uniteFiles',
48     ]
49
50 # ---
51
52 def _try_bool( arg ):
53     """
54     Check if specified parameter represents boolean value and returns its value.
55     String values like 'True', 'TRUE', 'YES', 'Yes', 'y', 'NO', 'false', 'n', etc
56     are supported.
57     If <arg> does not represent a boolean, an exception is raised.
58     """
59     if isinstance(arg, bool)  :
60         return arg
61     elif isinstance(arg, (str, bytes)):
62         v = str( arg ).lower()
63         if   v in [ "yes", "y", "true"  ]: return True
64         elif v in [ "no",  "n", "false" ]: return False
65         pass
66     raise Exception("Not boolean value")
67
68 # ---
69
70 def getORBcfgInfo():
71     """
72     Get omniORB current configuration.
73     Returns a list of three values: [ orb_version, host_name, port_number ].
74
75     The information is retrieved from the omniORB configuration file defined
76     by the OMNIORB_CONFIG environment variable.
77     If omniORB configuration file can not be accessed, a list of three empty
78     strings is returned.
79     """
80     import os, re
81     ret = [ "", "", "" ]
82     try:
83         f = open( os.getenv( "OMNIORB_CONFIG" ) )
84         lines = f.readlines()
85         f.close()
86         regvar = re.compile( "(ORB)?InitRef.*corbaname::(.*):(\d+)\s*$" )
87         for l in lines:
88             try:
89                 m = regvar.match( l )
90                 if m:
91                     if m.group(1) is None:
92                         ret[0] = "4"
93                     else:
94                         ret[0] = "3"
95                         pass
96                     ret[1] = m.group(2)
97                     ret[2] = m.group(3)
98                     break
99                 pass
100             except:
101                 pass
102             pass
103         pass
104     except:
105         pass
106     return ret
107
108 # ---
109
110 def getHostFromORBcfg():
111     """
112     Get current omniORB host.
113     """
114     return getORBcfgInfo()[1]
115 # ---
116
117 def getPortFromORBcfg():
118     """
119     Get current omniORB port.
120     """
121     return getORBcfgInfo()[2]
122
123 # ---
124
125 def getUserName():
126     """
127     Get user name:
128     1. try USER environment variable (USERNAME on windows)
129     2. if fails, try LOGNAME (un*x)
130     3. if fails return 'unknown' as default user name
131     """
132     import os, sys
133     if sys.platform == "win32":
134         return os.getenv("USERNAME", "unknown")
135     else:
136         user = os.getenv("USER")
137         if user:
138             return user
139         return os.getenv("LOGNAME", "unknown")
140 # ---
141
142 def getHostName():
143     """
144     Get host name:
145     1. try socket python module gethostname() function
146     2. if fails, try HOSTNAME environment variable
147     3. if fails, try HOST environment variable
148     4. if fails, return 'unknown' as default host name
149     """
150     try:
151         import socket
152         host = socket.gethostname()
153     except:
154         host = None
155         pass
156     if not host: host = os.getenv("HOSTNAME")
157     if not host: host = os.getenv("HOST")
158     if not host: host = "unknown"           # 'unknown' is default host name
159     try:
160         socket.gethostbyname(host)
161     except:
162         host = "localhost"
163     pass
164     return host
165
166 # ---
167
168 def getShortHostName():
169     """
170     Get short host name:
171     1. try socket python module gethostname() function
172     2. if fails, try HOSTNAME environment variable
173     3. if fails, try HOST environment variable
174     4. if fails, return 'unknown' as default host name
175     """
176     try:
177         return getHostName().split('.')[0]
178     except:
179         pass
180     return "unknown"           # 'unknown' is default host name
181
182 # ---
183
184 def getAppName():
185     """
186     Get application name:
187     1. try APPNAME environment variable
188     2. if fails, return 'SALOME' as default application name
189     """
190     import os
191     return os.getenv( "APPNAME", "SALOME" ) # 'SALOME' is default user name
192
193 # ---
194
195 def getPortNumber(use_default=True):
196     """
197     Get current naming server port number:
198     1. try NSPORT environment variable
199     1. if fails, try to parse config file defined by OMNIORB_CONFIG environment variable
200     2. if fails, return 2809 as default port number (if use_default is True) or None (id use_default is False)
201     """
202     import os
203     try:
204         return int( os.getenv( "NSPORT" ) )
205     except:
206         pass
207     try:
208         port = int( getPortFromORBcfg() )
209         if port is not None: return port
210     except:
211         pass
212     if use_default: return 2809 # '2809' is default port number
213     return None
214
215 # ---
216
217 def getHomeDir():
218     """
219     Get home directory.
220     """
221     import os
222     return os.path.realpath(os.path.expanduser('~'))
223 # ---
224
225 def getLogDir():
226     """
227     Get directory to be used for the log files.
228     """
229     import os
230     return os.path.join(getTmpDir(), "logs", getUserName())
231 # ---
232
233 def getTmpDir():
234     """
235     Get directory to be used for the temporary files.
236     """
237     import os, tempfile
238     f = tempfile.NamedTemporaryFile()
239     tmpdir = os.path.dirname(f.name)
240     f.close()
241     return tmpdir
242 # ---
243
244 def generateFileName( dir, prefix = None, suffix = None, extension = None,
245                       unique = False, separator = "_", hidden = False, **kwargs ):
246     """
247     Generate file name by specified parameters. If necessary, file name
248     can be generated to be unique.
249
250     Parameters:
251     - dir       : directory path
252     - prefix    : file prefix (not added by default)
253     - suffix    : file suffix (not added by default)
254     - extension : file extension (not added by default)
255     - unique    : if this parameter is True, the unique file name is generated:
256     in this case, if the file with the generated name already exists
257     in the <dir> directory, an integer suffix is added to the end of the
258     file name. This parameter is False by default.
259     - separator : separator of the words ('_' by default)
260     - hidden    : if this parameter is True, the file name is prepended by . (dot)
261     symbol. This parameter is False by default.
262
263     Other keyword parameters are:
264     - with_username : 'add user name' flag/option:
265       * boolean value can be passed to determine user name automatically
266       * string value to be used as user name
267     - with_hostname : 'add host name' flag/option:
268       * boolean value can be passed to determine host name automatically
269       * string value to be used as host name
270     - with_port     : 'add port number' flag/option:
271       * boolean value can be passed to determine port number automatically
272       * string value to be used as port number
273     - with_app      : 'add application name' flag/option:
274       * boolean value can be passed to determine application name automatically
275       * string value to be used as application name
276     All <with_...> parameters are optional.
277     """
278     supported = [ 'with_username', 'with_hostname', 'with_port', 'with_app' ]
279     from launchConfigureParser import verbose
280     filename = []
281     # separator
282     if separator is None:
283         separator = ""
284         pass
285     else:
286         separator = str( separator )
287         pass
288     # prefix (if specified)
289     if prefix is not None:
290         filename.append( str( prefix ) )
291         pass
292     # additional keywords
293     ### check unsupported parameters
294     for kw in kwargs:
295         if kw not in supported and verbose():
296             print('Warning! salome_utilitie.py: generateFileName(): parameter %s is not supported' % kw)
297             pass
298         pass
299     ### process supported keywords
300     for kw in supported:
301         if kw not in kwargs: continue
302         ### user name
303         if kw == 'with_username':
304             try:
305                 # auto user name ?
306                 if _try_bool( kwargs[kw] ): filename.append( getUserName() )
307                 pass
308             except:
309                 # user name given as parameter
310                 filename.append( kwargs[kw] )
311                 pass
312             pass
313         ### host name
314         elif kw == 'with_hostname':
315             try:
316                 # auto host name ?
317                 if _try_bool( kwargs[kw] ): filename.append( getShortHostName() )
318                 pass
319             except:
320                 # host name given as parameter
321                 filename.append( kwargs[kw] )
322                 pass
323             pass
324         ### port number
325         elif kw == 'with_port':
326             try:
327                 # auto port number ?
328                 if _try_bool( kwargs[kw] ): filename.append( str( getPortNumber() ) )
329                 pass
330             except:
331                 # port number given as parameter
332                 filename.append( str( kwargs[kw] ) )
333                 pass
334             pass
335         ### application name
336         elif kw == 'with_app':
337             try:
338                 # auto application name ?
339                 if _try_bool( kwargs[kw] ): filename.append( getAppName() )
340                 pass
341             except:
342                 # application name given as parameter
343                 filename.append( kwargs[kw] )
344                 pass
345             pass
346         pass
347     # suffix (if specified)
348     if suffix is not None:
349         filename.append( str( suffix ) )
350         pass
351     # raise an exception if file name is empty
352     if not filename:
353         raise Exception("Empty file name")
354     #
355     if extension is not None and extension.startswith("."): extension = extension[1:]
356     #
357     import os
358     name = separator.join( filename )
359     if hidden: name = "." + name                       # add dot for hidden files
360     if extension: name = name + "." + str( extension ) # add extension if defined
361     name = os.path.join( dir, name )
362     if unique:
363         # create unique file name
364         index = 0
365         while os.path.exists( name ):
366             index = index + 1
367             name = separator.join( filename ) + separator + str( index )
368             if hidden: name = "." + name                       # add dot for hidden files
369             if extension: name = name + "." + str( extension ) # add extension if defined
370             name = os.path.join( dir, name )
371             pass
372         pass
373     return os.path.normpath(name)
374
375 # ---
376
377 def makeTmpDir( path, mode=0o777 ):
378     """
379     Make temporary directory with the specified path.
380     If the directory exists then clear its contents.
381
382     Parameters:
383     - path : absolute path to the directory to be created.
384     - mode : access mode
385     """
386     import os
387     if os.path.exists( path ):
388         import sys
389         if sys.platform == "win32":
390             os.system( "rmdir /S /Q " + '"' + path + '"' )
391             os.system( "mkdir " + '"' + path + '"' )
392         else:
393             os.system( "rm -rf " + path + "/*" )
394     else:
395         dirs = path.split("/")
396         shift1 = shift2 = 0
397         if not dirs[0]: shift1 = 1
398         if dirs[-1]: shift2 = 1
399         for i in range(1+shift1,len(dirs)+shift2):
400             p = "/".join(dirs[:i])
401             try:
402                 os.mkdir(p, mode)
403                 os.chmod(p, mode)
404             except:
405                 pass
406
407 # ---
408
409 def uniteFiles( src_file, dest_file ):
410     """
411     Unite contents of the source file with contents of the destination file
412     and put result of the uniting to the destination file.
413     If the destination file does not exist then the source file is simply
414     copied to its path.
415
416     Parameters:
417     - src_file  : absolute path to the source file
418     - dest_file : absolute path to the destination file
419     """
420     import os
421
422     if not os.path.exists( src_file ):
423         return
424         pass
425
426     if os.path.exists( dest_file ):
427         # add a symbol of new line to contents of the destination file (just in case)
428         dest = open( dest_file, 'r' )
429         dest_lines = dest.readlines()
430         dest.close()
431
432         dest_lines.append( "\n" )
433
434         dest = open( dest_file, 'w' )
435         dest.writelines( dest_lines )
436         dest.close()
437
438         import sys
439         if sys.platform == "win32":
440             command = "type " + '"' + src_file + '"' + " >> " + '"' + dest_file + '"'
441         else:
442             command = "cat " + src_file + " >> " + dest_file
443             pass
444         pass
445     else:
446         import sys
447         if sys.platform == "win32":
448             command = "copy " + '"' + src_file + '"' + " " + '"' + dest_file + '"' + " > nul"
449         else:
450             command = "cp " + src_file + " " + dest_file
451             pass
452         pass
453
454     os.system( command )
455
456 # --
457
458 _verbose = None
459
460 def verbose():
461     """
462     Get verbosity level. Default verbosity level is specified via the environment variable
463     SALOME_VERBOSE, e.g.:
464     [bash %] export SALOME_VERBOSE=1
465     The function setVerbose() can be used to change verbosity level explicitly.
466     """
467     global _verbose
468     # verbose has already been called
469     if _verbose is not None:
470         return _verbose
471     # first time
472     try:
473         from os import getenv
474         _verbose = int(getenv('SALOME_VERBOSE'))
475     except:
476         _verbose = 0
477         pass
478     #
479     return _verbose
480 # --
481
482 def setVerbose(level):
483     """
484     Change verbosity level. The function verbose() can be used to get current verbosity level.
485     """
486     global _verbose
487     _verbose = level
488     return
489 # --
490
491 import signal
492 def killpid(pid, sig = 9):
493     """
494     Send signal sig to the process by pid.
495
496     Parameters:
497     - pid : PID of process
498     - sig : signal for sending
499             Possible values of signals: 
500             9 means kill the process
501             0 only check existing of the process
502             NOTE: Other values are not processed on Windows
503     Returns:
504      1 Success
505      0 Fail, no such process
506     -1 Fail, another reason
507
508     """
509     if not pid: return
510     import os, sys
511     if sig != 0:
512         if verbose(): print("######## killpid pid = ", pid)
513     try:
514         if sys.platform == "win32":
515             import ctypes
516             if sig == 0:
517                 # PROCESS_QUERY_INFORMATION (0x0400)    Required to retrieve certain information about a process
518                 SYNCHRONIZE = 0x100000
519                 handle = ctypes.windll.kernel32.OpenProcess(SYNCHRONIZE, False, int(pid))
520                 waitObj = ctypes.windll.kernel32.WaitForSingleObject(handle, 0)
521                 if waitObj:
522                     ret = 1
523                     ctypes.windll.kernel32.CloseHandle(handle)
524                 else:
525                     ret = 0
526             if sig == 9:
527                 # PROCESS_TERMINATE (0x0001)    Required to terminate a process using TerminateProcess.
528                 handle = ctypes.windll.kernel32.OpenProcess(0x0001, False, int(pid))
529                 ret = ctypes.windll.kernel32.TerminateProcess(handle, -1)
530                 ctypes.windll.kernel32.CloseHandle(handle)
531                 pass
532             pass
533         else:
534             # Default: signal.SIGKILL = 9
535             os.kill(int(pid),sig)
536             ret = 1
537             pass
538         pass
539     except OSError as e:
540         # errno.ESRCH == 3 is 'No such process'
541         if e.errno == 3:
542             ret = 0
543         else:
544             ret = -1
545             pass
546         pass
547     return ret
548 # --
549
550 def getOmniNamesPid(port):
551     """
552     Return OmniNames pid by port number.
553     """
554     import sys,subprocess,re
555     if sys.platform == "win32":
556         # Get process list by WMI Command Line Utility(WMIC)
557         # Output is formatted with each value listed on a separate line and with the name of the property:
558         #   ...
559         #   Caption=<caption0>
560         #   CommandLine=<commandline0>
561         #   ProcessId=<processid0>
562         #
563         #
564         #
565         #   Caption=<caption1>
566         #   CommandLine=<commandline1>
567         #   ProcessId=<processid1>
568         #   ...
569         cmd = 'WMIC PROCESS get Caption,Commandline,Processid /VALUE'
570         proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
571         # Get stdout
572         allProc = proc.communicate()[0].decode()
573         # find Pid of omniNames
574         pid = re.findall(r'Caption=.*omniNames.*\n?CommandLine=.*omniNames.*\D%s\D.*\n?ProcessId=(\d*)'%(port),allProc)[0]
575     else:        
576         cmd = "ps -eo pid,command | grep -v grep | grep -E \"omniNames.*%s\" | awk '{print $1}'"%(port)
577         proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
578         pid = proc.communicate()[0]
579         pass
580
581     return pid
582 # --
583
584 def killOmniNames(port):
585     """
586     Kill OmniNames process by port number.
587     """
588     try:
589         pid = getOmniNamesPid(port)
590         if pid: killpid(pid)
591     except:
592         pass
593     pass
594 # --