rda-python-common 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of rda-python-common might be problematic. Click here for more details.

@@ -0,0 +1,1157 @@
1
+ #
2
+ ###############################################################################
3
+ #
4
+ # Title : PgSIG.py
5
+ #
6
+ # Author : Zaihua Ji, zji@ucar.edu
7
+ # Date : 08/05/2020
8
+ # Purpose : python library module for start and control daemon process
9
+ #
10
+ # Work File : $DSSHOME/lib/python/PgSIG.py
11
+ # Github : https://github.com/NCAR/rda-shared-libraries.git
12
+ #
13
+ ###############################################################################
14
+ #
15
+ import os
16
+ import re
17
+ import sys
18
+ import errno
19
+ import signal
20
+ import time
21
+ from contextlib import contextmanager
22
+ import PgLOG
23
+ import PgDBI
24
+
25
+ VUSERS = [] # allow users to start this daemon
26
+ CPIDS = {} # allow upto 'mproc' processes at one time for daemon
27
+ CBIDS = {} # allow upto 'bproc' background processes at one time for each child
28
+ SDUMP = {
29
+ 'DEF' : '/dev/null',
30
+ 'ERR' : '',
31
+ 'OUT' : ''
32
+ }
33
+
34
+ PGSIG = {
35
+ 'QUIT' : 0, # 1 if QUIT signal received, quit server if no child
36
+ 'MPROC' : 1, # default number of multiple processes
37
+ 'BPROC' : 1, # default number of multiple background processes
38
+ 'ETIME' : 20, # default error waiting time (in seconds)
39
+ 'WTIME' : 120, # default waiting time (in seconds)
40
+ 'DTIME' : 600, # the daemon record refresh time (in seconds)
41
+ 'RTIME' : 2400, # the web rda config unlocking and unconfigured system down waiting time (in seconds)
42
+ 'CTIME' : 4800, # the lock cleaning & configued system down waiting time (in seconds)
43
+ 'PPID' : -1, # 1 - server, (> 1) - child, 0 - non-daemon mode
44
+ 'PID' : 0, # current process ID
45
+ 'DNAME' : '', # daemon name
46
+ 'DSTR' : '', # string for daemon with user login name
47
+ 'MTIME' : 0, # maximum daemon running time in seconds, 0 for unlimited
48
+ 'STIME' : 0, # time the daemon is started
49
+ 'STRTM' : '', # string format of 'STIME'
50
+ }
51
+
52
+ #
53
+ # add users for starting this daemon
54
+ #
55
+ def add_vusers(user = None, mores = None):
56
+
57
+ global VUSERS
58
+ if not user:
59
+ VUSERS = [] # clean all vusers
60
+ else:
61
+ VUSERS.append(user)
62
+
63
+ if mores: VUSERS.extend(mores)
64
+
65
+ #
66
+ # valid user for starting this daemon
67
+ #
68
+ def check_vuser(user, aname = None):
69
+
70
+ if user and VUSERS:
71
+ valid = 0;
72
+ for vuser in VUSERS:
73
+ if user == vuser:
74
+ valid = 1;
75
+ break
76
+
77
+ if valid == 0:
78
+ vuser = ', '.join(VUSERS)
79
+ PgLOG.pglog("{}: must be '{}' to run '{}' in Daemon mode".format(user, vuser, aname), PgLOG.LGEREX)
80
+
81
+ #
82
+ # turn this process into a daemon
83
+ #
84
+ # aname - application name, or daemon name
85
+ # uname - user login name to started the application
86
+ # mproc - upper limit of muiltiple child processes
87
+ # wtime - waiting time (in seconds) for next process for the daemon
88
+ # logon - turn on the logging if true
89
+ # bproc - multiple background processes if > 1
90
+ # mtime - maximum running time for the daemon if provided
91
+ #
92
+ def start_daemon(aname, uname, mproc = 1, wtime = 120, logon = 0, bproc = 1, mtime = 0):
93
+
94
+ dstr = "Daemon '{}'{} on {}".format(aname, (" By {}".format(uname) if uname else ''), PgLOG.PGLOG['HOSTNAME'])
95
+
96
+ pid = check_daemon(aname, uname)
97
+ if pid:
98
+ PgLOG.pglog("***************** WARNNING **************************\n" +
99
+ "** {} is running as PID={}\n".format(dstr, pid) +
100
+ "** You need stop it before starting a new one!\n" +
101
+ "*****************************************************" , PgLOG.WARNLG)
102
+ PgLOG.pglog("{} is already running as PID={}".format(dstr, pid), PgLOG.FRCLOG|PgLOG.MSGLOG)
103
+ sys.exit(0)
104
+
105
+ if mproc > 1: PGSIG['MPROC'] = mproc
106
+ if bproc > 1: PGSIG['BPROC'] = bproc
107
+ PGSIG['WTIME'] = get_wait_time(wtime, 120, "Polling Wait Time")
108
+ PGSIG['MTIME'] = get_wait_time(mtime, 0, "Maximum Running Time")
109
+
110
+ pid = process_fork(dstr)
111
+ cpid = pid if pid > 0 else os.getpid()
112
+ msg = "PID={},PL={},WI={}".format(cpid, PGSIG['MPROC'], PGSIG['WTIME'])
113
+ if PGSIG['MTIME']: msg += ",MT={}".format(PGSIG['MTIME'])
114
+ logmsg = "{}({}) started".format(dstr, msg)
115
+ if logon: logmsg += " With Logging On"
116
+ if pid > 0:
117
+ PgLOG.pglog(logmsg, PgLOG.WARNLG)
118
+ sys.exit(0)
119
+
120
+ os.setsid()
121
+ os.umask(0)
122
+
123
+ # setup to catch signals in daemon only
124
+ signal.signal(signal.SIGCHLD, clean_dead_child)
125
+ signal.signal(signal.SIGQUIT, signal_catch)
126
+ signal.signal(signal.SIGUSR1, signal_catch)
127
+ signal.signal(signal.SIGUSR2, signal_catch)
128
+ PGSIG['DSTR'] = dstr
129
+ PGSIG['DNAME'] = aname
130
+ PGSIG['STIME'] = int(time.time())
131
+ PGSIG['STRTM'] = PgLOG.current_datetime(PGSIG['STIME'])
132
+ PGSIG['PPID'] = 1
133
+ PGSIG['PID'] = cpid
134
+
135
+ sys.stdin = open(SDUMP['DEF'])
136
+ PgLOG.cmdlog("{} By {}".format(logmsg, PGSIG['STRTM']))
137
+
138
+ if logon:
139
+ PgLOG.PGLOG['LOGMASK'] &= ~(PgLOG.WARNLG|PgLOG.EMLLOG) # turn off warn/email in daemon
140
+ set_dump()
141
+ else:
142
+ PgLOG.PGLOG['LOGMASK'] &= ~(PgLOG.LGWNEM) # turn off log/warn/email in daemon
143
+ set_dump(SDUMP['DEF'])
144
+
145
+ PgLOG.PGLOG['BCKGRND'] = 1 # make sure the background flag is always on
146
+ PgDBI.pgdisconnect(1) # disconnect database in daemon
147
+
148
+ #
149
+ # set dump output file
150
+ #
151
+ def set_dump(default = None):
152
+
153
+ errdump = PgLOG.get_environment("ERRDUMP", default)
154
+ outdump = PgLOG.get_environment("OUTDUMP", default)
155
+
156
+ if not errdump:
157
+ if not PgLOG.PGLOG['ERRFILE']:
158
+ PgLOG.PGLOG['ERRFILE'] = re.sub(r'\.log$', '.err', PgLOG.PGLOG['LOGFILE'], 1)
159
+ errdump = "{}/{}".format(PgLOG.PGLOG['LOGPATH'], PgLOG.PGLOG['ERRFILE'])
160
+
161
+ if errdump != SDUMP['ERR']:
162
+ sys.stderr = open(errdump, 'a')
163
+ SDUMP['ERR'] = errdump
164
+
165
+ if not outdump: outdump = "{}/{}".format(PgLOG.PGLOG['LOGPATH'], PgLOG.PGLOG['LOGFILE'])
166
+ if outdump != SDUMP['OUT']:
167
+ sys.stdout = open(outdump, 'a')
168
+ SDUMP['OUT'] = outdump
169
+
170
+ #
171
+ # stop daemon and log the ending info
172
+ #
173
+ def stop_daemon(msg):
174
+
175
+ msg = " with " + msg if msg else ''
176
+ PgLOG.PGLOG['LOGMASK'] |= PgLOG.MSGLOG # turn on logging before daemon stops
177
+ PgLOG.pglog("{} Started at {}, Stopped gracefully{} by {}".format(PGSIG['DSTR'], PGSIG['STRTM'], msg, PgLOG.current_datetime()), PgLOG.LOGWRN)
178
+
179
+ #
180
+ # check if a daemon is running already
181
+ #
182
+ # aname - application name for the daemon
183
+ # uname - user login name who started the daemon
184
+ #
185
+ # return the process id if yes and 0 if not
186
+ #
187
+ def check_daemon(aname, uname = None):
188
+
189
+ if uname:
190
+ check_vuser(uname, aname)
191
+ pcmd = "ps -u {} -f | grep {} | grep ' 1 '".format(uname, aname)
192
+ mp = "^\s*{}\s+(\d+)\s+1\s+".format(uname)
193
+ else:
194
+ pcmd = "ps -C {} -f | grep ' 1 '".format(aname)
195
+ mp = "^\s*\w+\s+(\d+)\s+1\s+"
196
+
197
+ buf = PgLOG.pgsystem(pcmd, PgLOG.LOGWRN, 20+1024)
198
+ if buf:
199
+ cpid = os.getpid()
200
+ lines = buf.split('\n')
201
+ for line in lines:
202
+ ms = re.match(mp, line)
203
+ pid = int(ms.group(1)) if ms else 0
204
+ if pid > 0 and pid != cpid: return pid
205
+
206
+ return 0
207
+
208
+ #
209
+ # check if an application is running already; other than the current processs
210
+ #
211
+ # aname - application name
212
+ # uname - user login name who started the application
213
+ # argv - argument string
214
+ #
215
+ # return the process id if yes and 0 if not
216
+ #
217
+ def check_application(aname, uname = None, sargv = None):
218
+
219
+ if uname:
220
+ check_vuser(uname, aname)
221
+ pcmd = "ps -u {} -f | grep {} | grep -v ' grep '".format(uname, aname)
222
+ mp = "^\s*{}\s+(\d+)\s+(\d+)\s+.*{}\S*\s+(.*)$".format(uname, aname)
223
+ else:
224
+ pcmd = "ps -C {} -f".format(aname)
225
+ mp = "^\s*\w+\s+(\d+)\s+(\d+)\s+.*{}\S*\s+(.*)$".format(aname)
226
+
227
+ buf = PgLOG.pgsystem(pcmd, PgLOG.LOGWRN, 20+1024)
228
+ if not buf: return 0
229
+
230
+ cpids = [os.getpid(), os.getppid()]
231
+ pids = []
232
+ ppids = []
233
+ astrs = []
234
+ lines = buf.split('\n')
235
+ for line in lines:
236
+ ms = re.match(mp, line)
237
+ if not ms: continue
238
+ pid = int(ms.group(1))
239
+ ppid = int(ms.group(2))
240
+ if pid in cpids:
241
+ if ppid not in cpids: cpids.append(ppid)
242
+ continue
243
+ pids.append(pid)
244
+ ppids.append(ppid)
245
+ if sargv: astrs.append(ms.group(3))
246
+
247
+ pcnt = len(pids)
248
+ if not pcnt: return 0
249
+
250
+ i = 0
251
+ while i < pcnt:
252
+ pid = pids[i]
253
+ if pid and pid in cpids:
254
+ pids[i] = 0
255
+ ppid = ppids[i]
256
+ if ppid not in cpids: cpids.append(ppid)
257
+ i = 0
258
+ else:
259
+ i += 1
260
+
261
+ for i in range(pcnt):
262
+ pid = pids[i]
263
+ if pid and (not sargv or sargv.find(astrs[i]) > -1): return pid
264
+
265
+ return 0
266
+
267
+ #
268
+ # validate if the current process is a single one. Quit if not
269
+ #
270
+ def validate_single_process(aname, uname = None, sargv = None, logact = PgLOG.LOGWRN):
271
+
272
+ pid = check_application(aname, uname, sargv)
273
+ if pid:
274
+ msg = aname
275
+ if sargv: msg += ' ' + sargv
276
+ msg += ": already running as PID={} on {}".format(pid, PgLOG.PGLOG['HOSTNAME'])
277
+ if uname: msg += ' By ' + uname
278
+ PgLOG.pglog(msg + ', Quit Now', logact)
279
+ sys.exit(0)
280
+
281
+ #
282
+ # check how many processes are running for an application already
283
+ #
284
+ # aname - application name
285
+ # uname - user login name who started the application
286
+ # argv - argument string
287
+ #
288
+ # return the the number of processes (exclude the child one)
289
+ #
290
+ def check_multiple_application(aname, uname = None, sargv = None):
291
+
292
+ if uname:
293
+ check_vuser(uname, aname)
294
+ pcmd = "ps -u {} -f | grep {} | grep -v ' grep '".format(uname, aname)
295
+ mp = "^\s*{}\s+(\d+)\s+(\d+)\s+.*{}\S*\s+(.*)$".format(uname, aname)
296
+ else:
297
+ pcmd = "ps -C {} -f".format(aname)
298
+ mp = "^\s*\w+\s+(\d+)\s+(\d+)\s+.*{}\S*\s+(.*)$".format(aname)
299
+
300
+ buf = PgLOG.pgsystem(pcmd, PgLOG.LOGWRN, 20+1024)
301
+ if not buf: return 0
302
+
303
+ dpids = [os.getpid(), os.getppid()]
304
+ pids = []
305
+ ppids = []
306
+ astrs = []
307
+ lines = buf.split('\n')
308
+ for line in lines:
309
+ ms = re.match(mp, line)
310
+ if not ms: continue
311
+ pid = int(ms.group(1))
312
+ ppid = int(ms.group(2))
313
+ if pid in dpids:
314
+ if ppid > 1 and ppid not in dpids: dpids.append(ppid)
315
+ continue
316
+ elif ppid in pids:
317
+ if pid not in dpids: dpids.append(pid)
318
+ continue
319
+ pids.append(pid)
320
+ ppids.append(ppid)
321
+ if sargv: astrs.append(ms.group(3))
322
+
323
+ pcnt = len(pids)
324
+ if not pcnt: return 0
325
+
326
+ i = 0
327
+ while i < pcnt:
328
+ pid = pids[i]
329
+ ppid = ppids[i]
330
+ if pid:
331
+ if pid in dpids:
332
+ if ppid > 1 and ppid not in dpids: dpids.append(ppid)
333
+ i = pids[i] = 0
334
+ continue
335
+ elif ppid in pids:
336
+ if pid not in dpids: dpids.append(pid)
337
+ i = pids[i] = 0
338
+ continue
339
+ i += 1
340
+
341
+ ccnt = 0
342
+ for i in range(pcnt):
343
+ if pids[i] and (not sargv or sargv.find(astrs[i]) > -1): ccnt += 1
344
+
345
+ return ccnt
346
+
347
+ #
348
+ # validate if the running processes reach the limit for the given app; Quit if yes
349
+ #
350
+ def validate_multiple_process(aname, plimit, uname = None, sargv = None, logact = PgLOG.LOGWRN):
351
+
352
+ pcnt = check_multiple_application(aname, uname, sargv)
353
+ if pcnt >= plimit:
354
+ msg = aname
355
+ if sargv: msg += ' ' + sargv
356
+ msg += ": already running in {} processes on {}".format(pcnt, PgLOG.PGLOG['HOSTNAME'])
357
+ if uname: msg += ' By ' + uname
358
+ PgLOG.pglog(msg + ', Quit Now', logact)
359
+ sys.exit(0)
360
+
361
+ #
362
+ # fork process
363
+ #
364
+ # return the defined result from call of fork
365
+ #
366
+ def process_fork(dstr):
367
+
368
+ for i in range(10): # try 10 times
369
+ try:
370
+ pid = os.fork()
371
+ return pid
372
+ except OSError as e:
373
+ if e.errno == errno.EAGAIN:
374
+ os.sleep(5)
375
+ else:
376
+ PgLOG.pglog("{}: {}".format(dstr, str(e)), PgLOG.LGEREX)
377
+ break
378
+
379
+ PgLOG.pglog("{}: too many tries (10) for os.fork()".format(dstr), PgLOG.LGEREX)
380
+
381
+ #
382
+ # process the predefined signals
383
+ #
384
+ def signal_catch(signum, frame):
385
+
386
+ if PGSIG['PPID'] == 1:
387
+ tmp = 'Server'
388
+ elif PGSIG['PPID'] > 1:
389
+ tmp = 'Child'
390
+ else:
391
+ tmp = 'Process'
392
+
393
+ if signum == signal.SIGQUIT:
394
+ sname = "<{} - signal.SIGQUIT - Quit>".format(signum)
395
+ elif signum == signal.SIGUSR1:
396
+ linfo = 'Logging On'
397
+ if PgLOG.PGLOG['LOGMASK']&PgLOG.MSGLOG: linfo += ' & Debugging On'
398
+ sname = "<{} - signal.SIGUSR1 - {}>".format(signum, linfo)
399
+ elif signum == signal.SIGUSR2:
400
+ if PgLOG.PGLOG['DBGLEVEL']:
401
+ linfo = 'Logging off & Debugging Off'
402
+ else:
403
+ linfo = 'Logging Off'
404
+ sname = "<{} - signal.SIGUSR2 - {}>".format(signum, linfo)
405
+ else:
406
+ sname = "<{} - Signal Not Supports Yet>".format(signum)
407
+
408
+ dumpon = 1 if SDUMP['OUT'] and SDUMP['OUT'] != SDUMP['DEF'] else 0
409
+ if not dumpon: set_dump()
410
+ PgLOG.pglog("catches {} in {} {}".format(sname, tmp, PGSIG['DSTR']), PgLOG.LOGWRN|PgLOG.FRCLOG)
411
+
412
+ if signum == signal.SIGUSR1:
413
+ if PgLOG.PGLOG['LOGMASK']&PgLOG.MSGLOG:
414
+ PgLOG.PGLOG['DBGLEVEL'] = 1000 # turn logon twice
415
+ else:
416
+ PgLOG.PGLOG['LOGMASK'] |= PgLOG.MSGLOG # turn on logging
417
+ elif signum == signal.SIGUSR2:
418
+ PgLOG.PGLOG['LOGMASK'] &= ~(PgLOG.MSGLOG) # turn off logging
419
+ PgLOG.PGLOG['DBGLEVEL'] = 0 # turn off debugging
420
+ set_dump(SDUMP['DEF'])
421
+ else:
422
+ if not dumpon: set_dump(SDUMP['DEF'])
423
+ if signum == signal.SIGQUIT: PGSIG['QUIT'] = 1
424
+
425
+ if PGSIG['PPID'] <= 1 and len(CPIDS) > 0: # passing signal to child processes
426
+ for pid in CPIDS: kill_process(pid, signum)
427
+
428
+ #
429
+ # wrapper function to call os.kill() logging caught error based on logact
430
+ # return PgLOG.SUCCESS is success; PgLog.FAILURE if not
431
+ #
432
+ def kill_process(pid, signum, logact = 0):
433
+
434
+ try:
435
+ os.kill(pid, signum)
436
+ except Exception as e:
437
+ ret = PgLOG.FAILURE
438
+ if logact:
439
+ if type(signum) is int:
440
+ sigstr = str(signum)
441
+ else:
442
+ sigstr = "{}-{}".format(signum.name, int(signum))
443
+ PgLOG.pglog("Error pass signal {} to pid {}: {}".format(sigstr, pid, str(e)), logact)
444
+ else:
445
+ ret = PgLOG.SUCCESS
446
+
447
+ return ret
448
+
449
+ #
450
+ # wait child process to finish
451
+ #
452
+ def clean_dead_child(signum, frame):
453
+
454
+ live = 0
455
+
456
+ while True:
457
+ try:
458
+ dpid, status = os.waitpid(-1, os.WNOHANG)
459
+ except ChildProcessError as e:
460
+ break # no child process any more
461
+ except Exception as e:
462
+ PgLOG.PGLOG("Error check child process: {}".format(str(e)), PgLOG.ERRLOG)
463
+ break
464
+ else:
465
+ if dpid == 0:
466
+ if live > 0: break # wait twice if a process is still a live
467
+ live += 1
468
+ elif PGSIG['PPID'] < 2:
469
+ if dpid in CPIDS: del CPIDS[dpid]
470
+
471
+ #
472
+ # send signal to daemon and exit
473
+ #
474
+ def signal_daemon(sname, aname, uname):
475
+
476
+ dstr = "Daemon '{}'{} on {}".format(aname, ((" By " + uname) if uname else ""), PgLOG.PGLOG['HOSTNAME'])
477
+ pid = check_daemon(aname, uname)
478
+
479
+ if pid > 0:
480
+ dstr += " (PID = {})".format(pid)
481
+ if re.match(r'^(quit|stop)$', sname, re.I):
482
+ signum = signal.SIGQUIT
483
+ msg = "QUIT"
484
+ elif re.match(r'^(logon|on)$', sname, re.I):
485
+ signum = signal.SIGUSR1
486
+ msg = "Logging ON"
487
+ elif re.match(r'^(logoff|off)$', sname, re.I):
488
+ signum = signal.SIGUSR2
489
+ msg = "Logging OFF"
490
+ PgLOG.PGLOG['DBGLEVEL'] = 0
491
+ else:
492
+ PgLOG.pglog("{}: invalid Signal for {}".format(sname, dstr), PgLOG.LGEREX)
493
+
494
+ if kill_process(pid, signum, PgLOG.LOGERR) == PgLOG.SUCCESS:
495
+ PgLOG.pglog("{}: signal sent to {}".format(msg, dstr), PgLOG.LOGWRN|PgLOG.FRCLOG)
496
+ else:
497
+ PgLOG.pglog(dstr + ": not running currently", PgLOG.LOGWRN|PgLOG.FRCLOG)
498
+
499
+ sys.exit(0)
500
+
501
+ #
502
+ # start a time child to run the command in case hanging
503
+ #
504
+ def timeout_command(cmd, logact = PgLOG.LOGWRN, cmdopt = 4):
505
+
506
+ if logact&PgLOG.EXITLG: logact &= ~PgLOG.EXITLG
507
+
508
+ PgLOG.pglog("> " + cmd, logact)
509
+ if start_timeout_child(cmd, logact):
510
+ PgLOG.pgsystem(cmd, logact, cmdopt)
511
+ sys.exit(0)
512
+
513
+ #
514
+ # start a timeout child process
515
+ #
516
+ # return: 1 - in child, 0 - in parent
517
+ #
518
+ def start_timeout_child(msg, logact = PgLOG.LOGWRN):
519
+
520
+ pid = process_fork(msg)
521
+
522
+ if pid == 0: # in child
523
+ signal.signal(signal.SIGQUIT, signal_catch) # catch quit signal only
524
+ PGSIG['PPID'] = PGSIG['PID']
525
+ PGSIG['PID'] = pid = os.getpid()
526
+ PgLOG.cmdlog("Timeout child to " + msg, time.time(), 0)
527
+ PgDBI.pgdisconnect(0) # disconnect database in child
528
+ return 1
529
+
530
+ # in parent
531
+ for i in range(PgLOG.PGLOG['TIMEOUT']):
532
+ if not check_process(pid): break
533
+ sys.sleep(2)
534
+
535
+ if check_process(pid):
536
+ msg += ": timeout({} secs) in CPID {}".format(2*PgLOG.PGLOG['TIMEOUT'], pid)
537
+ pids = kill_children(pid, 0)
538
+ sys.sleep(6)
539
+ if kill_process(pid, signal.SIGKILL, PgLOG.LOGERR): pids.insert(0, pid)
540
+
541
+ if pids: msg += "\nProcess({}) Killed".format(','.join(map(str, pids)))
542
+ PgLOG.pglog(msg, logact)
543
+
544
+ return 0
545
+
546
+ #
547
+ # kill children recursively start from the deepest and return the pids got killed
548
+ #
549
+ def kill_children(pid, logact = PgLOG.LOGWRN):
550
+
551
+ buf = PgLOG.pgsystem("ps --ppid {} -o pid".format(pid), logact, 20)
552
+ pids = []
553
+ if buf:
554
+ lines = buf.split('\n')
555
+ for line in lines:
556
+ ms = re.match(r'^\s*(\d+)', line)
557
+ if not ms: continue
558
+ cid = int(ms.group(1))
559
+ if not check_process(cid): continue
560
+ cids = kill_children(cid, logact)
561
+ if cids: pids = cids + pids
562
+ if kill_process(cid, signal.SIGKILL, logact) == PgLOG.SUCCESS: pids.insert(0, cid)
563
+
564
+ if logact and len(pids): PgLOG.pglog("Process({}) Killed".format(','.join(map(str, pids))), logact)
565
+
566
+ return pids
567
+
568
+ #
569
+ # start a child process
570
+ # pname - unique process name
571
+ #
572
+ def start_child(pname, logact = PgLOG.LOGWRN, dowait = 0):
573
+
574
+ global CBIDS
575
+ if PGSIG['MPROC'] < 2: return 1 # no need child process
576
+
577
+ if logact&PgLOG.EXITLG: logact &= ~PgLOG.EXITLG
578
+ if logact&PgLOG.MSGLOG: logact |= PgLOG.FRCLOG
579
+
580
+ if PGSIG['QUIT']:
581
+ return PgLOG.pglog("{} is in QUIT mode, cannot start CPID for {}".format(PGSIG['DSTR'], pname), logact)
582
+ elif len(CPIDS) >= PGSIG['MPROC']:
583
+ i = 0
584
+ while True:
585
+ pcnt = check_child(None, 0, logact)
586
+ if pcnt < PGSIG['MPROC']: break
587
+ if dowait:
588
+ show_wait_message(i, "{}-{}: wait any {} child processes".format(PGSIG['DSTR'], pname, pcnt), logact, dowait)
589
+ i += 1
590
+ else:
591
+ return PgLOG.pglog("{}-{}: {} child processes already running at {}".format(PGSIG['DSTR'], pname, pcnt, PgLOG.current_datetime()), logact)
592
+
593
+ if check_child(pname): return -1 # process is running already
594
+
595
+ pid = process_fork(PGSIG['DSTR'])
596
+ if pid:
597
+ CPIDS[pid] = pname # record the child process id
598
+ PgLOG.pglog("{}: starts CPID {} for {}".format(PGSIG['DSTR'], pid, pname))
599
+ else:
600
+ signal.signal(signal.SIGQUIT, signal.SIG_DFL) # turn off catch QUIT signal in child
601
+ PgLOG.PGLOG['LOGMASK'] &= ~PgLOG.WARNLG # turn off warn in child
602
+ PGSIG['PPID'] = PGSIG['PID']
603
+ PGSIG['PID'] = pid = os.getpid()
604
+ PGSIG['MPROC'] = 1 # 1 in child process
605
+ CBIDS = {} # empty backgroud proces info in case not
606
+ PGSIG['DSTR'] += ": CPID {} for {}".format(pid, pname)
607
+ PgLOG.cmdlog("CPID {} for {}".format(pid, pname))
608
+ PgDBI.pgdisconnect(0) # disconnect database in child
609
+
610
+ return 1 # child started successfully
611
+
612
+ #
613
+ # get child process id for given pname
614
+ #
615
+ def pname2cpid(pname):
616
+
617
+ for cpid in CPIDS:
618
+ if CPIDS[cpid] == pname: return cpid
619
+
620
+ return 0
621
+
622
+ #
623
+ # check one or all child processes if they are still running
624
+ # pname - unique process name if given
625
+ # pid - check this specified process id if given
626
+ # dowait - 0 no wait, 1 wait all done, -1 wait only when all children are running
627
+ # return the number of running processes if dowait == 0 or 1
628
+ # return the number of none-running processes if dowait == -1
629
+ #
630
+ def check_child(pname, pid = 0, logact = PgLOG.LOGWRN, dowait = 0):
631
+
632
+ if PGSIG['MPROC'] < 2: return 0 # no child process
633
+
634
+ if logact&PgLOG.EXITLG: logact &= ~PgLOG.EXITLG
635
+ ccnt = i = 0
636
+ if dowait < 0: ccnt = 1 if (pid or pname) else PGSIG['MPROC']
637
+ while True:
638
+ pcnt = 0
639
+ if not pid and pname: pid = pname2cpid(pname)
640
+ if pid:
641
+ if check_process(pid): # process is not done yet
642
+ if pname:
643
+ PgLOG.pglog("{}({}): Child still running".format(pname, pid), logact)
644
+ else:
645
+ PgLOG.pglog("{}: Child still running".format(pid), logact)
646
+ pcnt = 1
647
+ elif pid in CPIDS:
648
+ del CPIDS[pid] # clean the saved info for the process
649
+ elif not pname:
650
+ cpids = list(CPIDS)
651
+ for cpid in cpids:
652
+ if check_process(cpid): # process is not done yet
653
+ pcnt += 1
654
+ elif cpid in CPIDS:
655
+ del CPIDS[cpid]
656
+
657
+ if pcnt == 0 or dowait == 0 or pcnt < ccnt: break
658
+ show_wait_message(i, "{}: wait {}/{} child processes".format(PGSIG['DSTR'], pcnt, PGSIG['MPROC']), logact, dowait)
659
+ i += 1
660
+
661
+ return (ccnt - pcnt) if ccnt else pcnt
662
+
663
+ #
664
+ # start this process in none daemon mode
665
+ #
666
+ # aname - application name, or daemon name
667
+ # cact - short action name
668
+ # uname - user login name to started the application
669
+ # mproc - upper limit of muiltiple child processes
670
+ # wtime - waiting time (in seconds) for next process
671
+ #
672
+ def start_none_daemon(aname, cact = None, uname = None, mproc = 1, wtime = 120, logon = 1, bproc = 1):
673
+
674
+ dstr = aname
675
+ if cact: dstr += " for Action " + cact
676
+ if uname:
677
+ dstr += " By " + uname
678
+ check_vuser(uname, aname)
679
+
680
+ signal.signal(signal.SIGQUIT, signal_catch) # catch quit signal only
681
+ signal.signal(signal.SIGCHLD, clean_dead_child)
682
+ PGSIG['DSTR'] = dstr
683
+ PGSIG['DNAME'] = aname
684
+ PGSIG['PPID'] = 0
685
+ PGSIG['PID'] = os.getpid()
686
+ PGSIG['MPROC'] = mproc
687
+ PGSIG['BPROC'] = bproc
688
+ PgLOG.PGLOG['CMDTIME'] = PGSIG['WTIME'] = get_wait_time(wtime, 120, "Polling Wait Time")
689
+ if PGSIG['MPROC'] > 1:
690
+ PgLOG.cmdlog("starts non-daemon {}(ML={},WI={})".format(aname, PGSIG['MPROC'], PGSIG['WTIME']))
691
+ if not logon: PgLOG.PGLOG['LOGMASK'] &= ~PgLOG.MSGLOG # turn off message logging
692
+
693
+ #
694
+ # check one process id other than the current one if it is still running
695
+ # pid - specified process id
696
+ # pmsg - process message if given
697
+ #
698
+ def check_process(pid):
699
+
700
+ buf = PgLOG.pgsystem("ps -p {} -o pid".format(pid), PgLOG.LGWNEX, 20)
701
+ if buf:
702
+ mp = r'^\s*{}$'.format(pid)
703
+ lines = buf.split('\n')
704
+ for line in lines:
705
+ if re.match(mp, line): return 1
706
+
707
+ return 0
708
+
709
+ #
710
+ # check a process id on give host
711
+ #
712
+ def check_host_pid(host, pid, pmsg = None, logact = PgLOG.LOGWRN):
713
+
714
+ cmd = 'rdaps'
715
+ if host: cmd += " -h " + host
716
+ cmd += " -p {}".format(pid)
717
+ buf = PgLOG.pgsystem(cmd, logact, 276) # 4+16+256
718
+ if not buf: return (-1 if PgLOG.PGLOG['SYSERR'] else 0)
719
+ if pmsg: PgLOG.pglog(pmsg, logact&(~PgLOG.EXITLG))
720
+ return 1
721
+
722
+ #
723
+ # check one process id on a given host name if it is still running, with default timeout
724
+ # pid - specified process id
725
+ # ppid - specified parent process id
726
+ # uname - user login name who started the daemon
727
+ # host - host name the pid supposed to be running on
728
+ # aname - application name
729
+ # pmsg - process message if given
730
+ #
731
+ # return 1 if process is steal live, 0 died already, -1 error checking
732
+ #
733
+ def check_host_process(host, pid, ppid = 0, uname = None, aname = None, pmsg = None, logact = PgLOG.LOGWRN):
734
+
735
+ cmd = "rdaps"
736
+ if host: cmd += " -h " + host
737
+ if pid: cmd += " -p {}".format(pid)
738
+ if ppid: cmd += " -P {}".format(ppid)
739
+ if uname: cmd += " -u " + uname
740
+ if aname: cmd += " -a " + aname
741
+ buf = PgLOG.pgsystem(cmd, logact, 276) # 4+16+256
742
+ if not buf: return (-1 if PgLOG.PGLOG['SYSERR'] else 0)
743
+ if pmsg: PgLOG.pglog(pmsg, logact&(~PgLOG.EXITLG))
744
+ return 1
745
+
746
+ #
747
+ # get a single slurm status record
748
+ #
749
+ def get_slurm_info(bcmd, logact = PgLOG.LOGWRN):
750
+
751
+ stat = {}
752
+ buf = PgLOG.pgsystem(bcmd, logact, 16)
753
+ if not buf: return stat
754
+
755
+ chkt = 1
756
+ lines = buf.split('\n')
757
+ for line in lines:
758
+ if chkt:
759
+ if re.match(r'^\s*JOBID\s', line, re.I):
760
+ ckeys = re.split(r'\s+', PgLOG.pgtrim(line))
761
+ kcnt = len(ckeys)
762
+ chkt = 0
763
+ else:
764
+ if re.match(r'^-----', line): continue
765
+ vals = re.split(r'\s+', PgLOG.pgtrim(line))
766
+ vcnt = len(vals)
767
+ if vcnt >= kcnt:
768
+ for i in range(kcnt):
769
+ ckeys[i] = ckeys[i].upper()
770
+ stat[ckeys[i]] = vals[i]
771
+
772
+ if vcnt > kcnt:
773
+ for i in range(kcnt, vcnt):
774
+ stat[ckeys[kcnt-1]] += ' ' + str(vals[i])
775
+ break
776
+
777
+ return stat
778
+
779
+ #
780
+ # get a single pbs status record via qstat
781
+ #
782
+ def get_pbs_info(qopts, multiple = 0, logact = 0, chkcnt = 1):
783
+
784
+ stat = {}
785
+ loop = 0
786
+ buf = None
787
+ while loop < chkcnt:
788
+ buf = PgLOG.pgsystem("qstat -n -w {}".format(qopts), logact, 16)
789
+ if buf: break
790
+ loop += 1
791
+ time.sleep(6)
792
+
793
+ if not buf: return stat
794
+
795
+ chkt = chkd = 1
796
+ lines = buf.split('\n')
797
+ for line in lines:
798
+ if chkt:
799
+ if re.match(r'^Job ID', line):
800
+ line = re.sub(r'^Job ID', 'JobID', line, 1)
801
+ ckeys = re.split(r'\s+', PgLOG.pgtrim(line))
802
+ ckeys[1] = 'UserName'
803
+ ckeys[3] = 'JobName'
804
+ ckeys[7] = 'Reqd' + ckeys[7]
805
+ ckeys[8] = 'Reqd' + ckeys[7]
806
+ ckeys[9] = 'State'
807
+ ckeys[10] = 'Elap' + ckeys[7]
808
+ ckeys.append('Node')
809
+ kcnt = len(ckeys)
810
+ if multiple:
811
+ for i in range(kcnt):
812
+ stat[ckeys[i]] = []
813
+ chkt = 0
814
+ elif chkd:
815
+ if re.match(r'^-----', line): chkd = 0
816
+ else:
817
+ vals = re.split(r'\s+', PgLOG.pgtrim(line))
818
+ if len(vals) == kcnt:
819
+ ms = re.match(r'^(\d+)', vals[0])
820
+ if ms: vals[0] = ms.group(1)
821
+ if multiple:
822
+ for i in range(kcnt):
823
+ stat[ckeys[i]].append(vals[i])
824
+ else:
825
+ for i in range(kcnt):
826
+ stat[ckeys[i]] = vals[i]
827
+ break
828
+
829
+ return stat
830
+
831
+ #
832
+ # get multiple slurn status record
833
+ #
834
+ def get_slurm_multiple(bcmd, logact = PgLOG.LOGWRN):
835
+
836
+ buf = PgLOG.pgsystem(bcmd, logact, 16)
837
+ if not buf: return 0
838
+
839
+ stat = {}
840
+ j = 0
841
+ chkt = chkd = 1
842
+ lines = buf.split('\n')
843
+ for line in lines:
844
+ if chkt:
845
+ if re.match(r'^\s*JOBID\s', line, re.I):
846
+ ckeys = re.split(r'\s+', PgLOG.pgtrim(line))
847
+ kcnt = len(ckeys)
848
+ for i in range(kcnt):
849
+ ckeys[i] = ckeys[i].upper()
850
+ stat[ckeys[i]] = []
851
+ chkt = 0
852
+ elif chkd:
853
+ if re.match(r'^-----', line): chkd = 0
854
+ else:
855
+ vals = re.split(r'\s+', PgLOG.pgtrim(line))
856
+ vcnt = len(vals)
857
+ if vcnt >= kcnt:
858
+ for i in range(kcnt):
859
+ stat[ckeys[i]].append(vals[i])
860
+
861
+ if vcnt > kcnt:
862
+ for i in range(kcnt, vcnt):
863
+ stat[ckeys[kcnt-1]][j] += ' ' + str(vals[i])
864
+ j += 1
865
+
866
+ return stat if j else 0
867
+
868
+ #
869
+ # check status of a slurm batch id
870
+ # bid - specified batch id
871
+ #
872
+ # return hash of batch status, 0 if cannot check any more
873
+ #
874
+ def check_slurm_status(bid, logact = PgLOG.LOGWRN):
875
+
876
+ return get_slurm_info("sacct -o jobid,user,totalcpu,elapsed,ncpus,state,jobname,nodelist -j {}".format(bid), logact)
877
+
878
+ #
879
+ # check status of a pbs batch id
880
+ # bid - specified batch id
881
+ #
882
+ # return hash of batch status, 0 if cannot check any more
883
+ #
884
+ def check_pbs_status(bid, logact = PgLOG.LOGWRN):
885
+
886
+ stat = {}
887
+ buf = PgLOG.pgsystem("qhist -w -j {}".format(bid), logact, 20)
888
+ if not buf: return stat
889
+
890
+ chkt = 1
891
+ lines = buf.split('\n')
892
+ for line in lines:
893
+ if chkt:
894
+ if re.match(r'^Job', line):
895
+ line = re.sub(r'^Job ID', 'JobID', line, 1)
896
+ line = re.sub(r'Finish Time', 'FinishTime', line, 1)
897
+ line = re.sub(r'Req Mem', 'ReqMem', line, 1)
898
+ line = re.sub(r'Used Mem\(GB\)', 'UsedMem(GB)', line, 1)
899
+ line = re.sub(r'Avg CPU \(%\)', 'AvgCPU(%)', line, 1)
900
+ line = re.sub(r'Elapsed \(h\)', 'WallTime(h)', line, 1)
901
+ line = re.sub(r'Job Name', 'JobName', line, 1)
902
+ ckeys = re.split(r'\s+', PgLOG.pgtrim(line))
903
+ ckeys[1] = 'UserName'
904
+ kcnt = len(ckeys)
905
+ chkt = 0
906
+ else:
907
+ vals = re.split(r'\s+', PgLOG.pgtrim(line))
908
+ for i in range(kcnt):
909
+ stat[ckeys[i]] = vals[i]
910
+ break
911
+
912
+ return stat
913
+
914
+ #
915
+ # check if a slurm batch id is live
916
+ # bid - specified batch id
917
+ #
918
+ # return 1 if process is steal live, 0 died already or error checking
919
+ #
920
+ def check_slurm_process(bid, pmsg = None, logact = PgLOG.LOGWRN):
921
+
922
+ stat = get_slurm_info("squeue -l -j {}".format(bid), logact)
923
+
924
+ if stat:
925
+ ms = re.match(r'^(RUNNING|PENDING|SUSPENDE|COMPLETI|CONFIGUR|REQUEUE_)$', stat['STATE'])
926
+ if ms:
927
+ if pmsg: PgLOG.pglog("{}, STATE={}".format(pmsg, ms.group(1)), logact&~PgLOG.EXITLG)
928
+ return 1
929
+ else:
930
+ return 0
931
+
932
+ return -1
933
+
934
+ #
935
+ # check if a pbs batch id is live
936
+ # bid - specified batch id
937
+ #
938
+ # return 1 if process is steal live, 0 died already or error checking
939
+ #
940
+ def check_pbs_process(bid, pmsg = None, logact = PgLOG.LOGWRN):
941
+
942
+ stat = get_pbs_info(bid, 0, logact)
943
+
944
+ ret = -1
945
+ if stat:
946
+ ms = re.match(r'^(B|R|Q|S|H|W|X)$', stat['State'])
947
+ if ms:
948
+ if pmsg: pmsg += ", STATE='{}' and returns 1".format(ms.group(1))
949
+ ret = 1
950
+ else:
951
+ if pmsg: pmsg += ", STATE='{}' and returns 0".format(stat['State'])
952
+ ret = 0
953
+ elif pmsg:
954
+ pmsg += ", Process Not Exists and returns -1"
955
+
956
+ if pmsg: PgLOG.pglog(pmsg, logact&~PgLOG.EXITLG)
957
+
958
+ return ret
959
+
960
+ #
961
+ # get wait time
962
+ #
963
+ def get_wait_time(wtime, default, tmsg):
964
+
965
+ if not wtime: wtime = default # use default time
966
+
967
+ if type(wtime) is int: return wtime
968
+ if re.match(r'^(\d*)$', wtime): return int(wtime)
969
+
970
+ ms = re.match(r'^(\d*)([DHMS])$', wtime, re.I)
971
+ if ms:
972
+ ret = int(ms.group(1))
973
+ unit = ms.group(2)
974
+ else:
975
+ PgLOG.pglog("{}: '{}' NOT in (D,H,M,S)".format(wtime, tmsg), PgLOG.LGEREX)
976
+
977
+ if unit != 'S':
978
+ ret *= 60 # seconds in a minute
979
+ if unit != 'M':
980
+ ret *= 60 # minutes in an hour
981
+ if unit != 'H':
982
+ ret *= 24 # hours in a day
983
+
984
+ return ret # in seconds
985
+
986
+ #
987
+ # start a background process and record its id; check PgLOG.pgsystem() in PgLOG.pm for
988
+ # valid cmdopt values
989
+ #
990
+ def start_background(cmd, logact = PgLOG.LOGWRN, cmdopt = 5, dowait = 0):
991
+
992
+ if PGSIG['BPROC'] < 2: return PgLOG.pgsystem(cmd, logact, cmdopt) # no background
993
+
994
+ act = logact&(~PgLOG.EXITLG)
995
+ if act&PgLOG.MSGLOG: act |= PgLOG.FRCLOG # make sure background calls always logged
996
+
997
+ if len(CBIDS) >= PGSIG['BPROC']:
998
+ i = 0
999
+ while True:
1000
+ bcnt = check_background(None, 0, act)
1001
+ if bcnt < PGSIG['BPROC']: break
1002
+ if dowait:
1003
+ show_wait_message(i, "{}-{}: wait any {} background calls".format(PGSIG['DSTR'], cmd, bcnt), act, dowait)
1004
+ i += 1
1005
+ else:
1006
+ return PgLOG.pglog("{}-{}: {} background calls already at {}".format(PGSIG['DSTR'], cmd, bcnt, PgLOG.current_datetime()), act)
1007
+
1008
+ cmdlog = (act if cmdopt&1 else PgLOG.WARNLG)
1009
+ if cmdopt&8:
1010
+ PgLOG.cmdlog("starts '{}'".format(cmd), None, cmdlog)
1011
+ else:
1012
+ PgLOG.pglog("{}({})-{} >{} &".format(PgLOG.PGLOG['HOSTNAME'], os.getpid(), PgLOG.current_datetime(), cmd), cmdlog)
1013
+ bckcmd = cmd
1014
+ if cmdopt&2:
1015
+ bckcmd += " >> {}/{}".format(PgLOG.PGLOG['LOGPATH'], PgLOG.PGLOG['LOGFILE'])
1016
+
1017
+ if cmdopt&4:
1018
+ if not PgLOG.PGLOG['ERRFILE']:
1019
+ PgLOG.PGLOG['ERRFILE'] = re.sub(r'\.log$', '.err', PgLOG.PGLOG['LOGFILE'], 1)
1020
+ bckcmd += " 2>> {}/{}".format(PgLOG.PGLOG['LOGPATH'], PgLOG.PGLOG['ERRFILE'])
1021
+
1022
+ bckcmd += " &"
1023
+ os.system(bckcmd)
1024
+ return record_background(cmd, logact)
1025
+
1026
+ #
1027
+ # get background process id for given bcmd
1028
+ #
1029
+ def bcmd2cbid(bcmd):
1030
+
1031
+ for cbid in CBIDS:
1032
+ if CBIDS[cbid] == bcmd: return cbid
1033
+
1034
+ return 0
1035
+
1036
+ #
1037
+ # check one or all child processes if they are still running
1038
+ # bid - check this specified background process id if given
1039
+ # return the number of processes are still running
1040
+ #
1041
+ def check_background(bcmd, bid = 0, logact = PgLOG.LOGWRN, dowait = 0):
1042
+
1043
+ if PGSIG['BPROC'] < 2: return 0 # no background process
1044
+
1045
+ if logact&PgLOG.EXITLG: logact &= ~PgLOG.EXITLG
1046
+ if not bid and bcmd: bid = bcmd2cbid(bcmd)
1047
+ bcnt = i = 0
1048
+ while True:
1049
+ if bid:
1050
+ if check_process(bid): # process is not done yet
1051
+ if bcmd:
1052
+ PgLOG.pglog("{}({}): Background process still running".format(bcmd, bid), logact)
1053
+ else:
1054
+ PgLOG.pglog("{}: Background process still running".format(bid), logact)
1055
+ bcnt = 1
1056
+ elif bid in CBIDS:
1057
+ del CBIDS[bid] # clean the saved info for the process
1058
+ elif not bcmd:
1059
+ for bid in CBIDS:
1060
+ if check_process(bid): # process is not done yet
1061
+ bcnt += 1
1062
+ else:
1063
+ del CBIDS[bid]
1064
+
1065
+ if not (bcnt and dowait): break
1066
+ show_wait_message(i, "{}: wait {}/{} background processes".format(PGSIG['DSTR'], bcnt, PGSIG['MPROC']), logact, dowait)
1067
+ i += 1
1068
+ bcnt = 0
1069
+
1070
+ return bcnt
1071
+
1072
+ #
1073
+ # check and record process id for background command; return 1 if success full;
1074
+ # 0 otherwise; -1 if done already
1075
+ #
1076
+ def record_background(bcmd, logact = PgLOG.LOGWRN):
1077
+
1078
+ ms = re.match(r'^(\S+)', bcmd)
1079
+ if ms:
1080
+ aname = ms.group(1)
1081
+ else:
1082
+ aname = bcmd
1083
+
1084
+ mp = r"^\s*(\S+)\s+(\d+)\s+1\s+.*{}(.*)$".format(aname)
1085
+ pc = "ps -u {},{} -f | grep ' 1 ' | grep {}".format(PgLOG.PGLOG['CURUID'], PgLOG.PGLOG['RDAUSER'], aname)
1086
+ for i in range(2):
1087
+ buf = PgLOG.pgsystem(pc, logact, 20+1024)
1088
+ if buf:
1089
+ lines = buf.split('\n')
1090
+ for line in lines:
1091
+ ms = re.match(mp, line)
1092
+ if not ms: continue
1093
+ (uid, sbid, acmd) = ms.groups()
1094
+ bid = int(sbid)
1095
+ if bid in CBIDS: return -1
1096
+ if uid == PgLOG.PGLOG['RDAUSER']:
1097
+ acmd = re.sub(r'^\.(pl|py)\s+', '', acmd, 1)
1098
+ if re.match(r'^{}{}'.format(aname, acmd), bcmd): continue
1099
+ CBIDS[bid] = bcmd
1100
+ return 1
1101
+ time.sleep(2)
1102
+
1103
+ return 0
1104
+
1105
+ #
1106
+ # sleep for given period for the daemon, stops if maximum running time reached
1107
+ #
1108
+ def sleep_daemon(wtime = 0, mtime = None):
1109
+
1110
+ if not wtime: wtime = PGSIG['WTIME']
1111
+ if mtime is None: mtime = PGSIG['MTIME']
1112
+
1113
+ if mtime > 0:
1114
+ rtime = int(time.time()) - PGSIG['STIME']
1115
+ if rtime >= mtime:
1116
+ PGSIG['QUIT'] = 1
1117
+ wtime = 0
1118
+
1119
+ if wtime: time.sleep(wtime)
1120
+ return wtime
1121
+
1122
+ #
1123
+ # show wait message every dintv and then sleep for PGSIG['WTIME']
1124
+ #
1125
+ def show_wait_message(loop, msg, logact = PgLOG.LOGWRN, dowait = 0):
1126
+
1127
+ if loop > 0 and (loop%30) == 0:
1128
+ PgLOG.pglog("{} at {}".format(msg, PgLOG.current_datetime()), logact)
1129
+
1130
+ if dowait: time.sleep(PGSIG['WTIME'])
1131
+
1132
+ #
1133
+ # register a time out function to raise a time out error
1134
+ #
1135
+ @contextmanager
1136
+ def pgtimeout(seconds = 0, logact = 0):
1137
+
1138
+ if not seconds: seconds = PgLOG.PGLOG['TIMEOUT']
1139
+ signal.signal(signal.SIGALRM, raise_pgtimeout)
1140
+ signal.alarm(seconds)
1141
+ try:
1142
+ yield
1143
+ except TimeoutError as e:
1144
+ pass
1145
+ finally:
1146
+ signal.signal(signal.SIGALRM, signal.SIG_IGN)
1147
+
1148
+ def raise_pgtimeout(signum, frame):
1149
+ raise TimeoutError
1150
+
1151
+ def timeout_func():
1152
+ # Add a timeout block.
1153
+ with pgtimeout(1):
1154
+ print('entering block')
1155
+ import time
1156
+ time.sleep(10)
1157
+ print('This should never get printed because the line before timed out')