Fix 'execute' function
[slapos.git] / slapos / recipe / librecipe / execute.py
1 import sys
2 import os
3 import signal
4 import subprocess
5 import time
6
7 import inotifyx
8
9 def _wait_files_creation(file_list):
10 # Etablish a list of directory and subfiles
11 directories = dict()
12 for dirname, filename in [os.path.split(f) for f in file_list]:
13 directories.setdefault(dirname, dict())
14 directories[dirname][filename] = False
15
16 def all_files_exists():
17 return all([all(files.values()) for files in directories.values()])
18
19 fd = inotifyx.init()
20 try:
21 # Watch every directories where the file are
22 watchdescriptors = dict()
23 for dirname in directories.keys():
24 wd = inotifyx.add_watch(fd,
25 dirname,
26 inotifyx.IN_CREATE | inotifyx.IN_DELETE)
27 watchdescriptors[wd] = dirname
28
29 # Set to True the file wich exists
30 for dirname, filename in [os.path.split(f) for f in file_list]:
31 directories[dirname][filename] = os.path.exists(os.path.join(dirname,
32 filename))
33 # Let's wait for every file creation
34 while not all_files_exists():
35 events_list = inotifyx.get_events(fd)
36 for event in events_list:
37 dirname = watchdescriptors[event.wd]
38 if event.name in directories[dirname]:
39 # One of watched file was created or deleted
40 if event.mask & inotifyx.IN_DELETE:
41 directories[dirname][event.name] = False
42 else:
43 directories[dirname][event.name] = True
44
45 finally:
46 os.close(fd)
47
48 def execute(args):
49 """Portable execution with process replacement"""
50 # XXX: Kept for backward compatibility
51 generic_exec([args, None, None])
52
53 def execute_wait(args):
54 """Execution but after all files in args[1] exists"""
55 # XXX: Kept for backward compatibility
56 generic_exec([args[0], args[1], None])
57
58
59 child_pg = None
60
61
62 def executee(args):
63 """Portable execution with process replacement and environment manipulation"""
64 # XXX: Kept for backward compatibility
65 generic_exec([args[0], None, args[1]])
66
67 def executee_wait(args):
68 """Portable execution with process replacement and environment manipulation"""
69 # XXX: Kept for backward compatibility
70 generic_exec(args)
71
72 def generic_exec(args):
73 exec_list = list(args[0])
74 file_list = args[1]
75 environment_overriding = args[2]
76
77 exec_env = os.environ.copy()
78 if environment_overriding is not None:
79 exec_env.update(environment_overriding)
80
81 if file_list is not None:
82 _wait_files_creation(file_list)
83
84 os.execve(exec_list[0], exec_list + sys.argv[1:], exec_env)
85
86 def sig_handler(signal, frame):
87 print 'Received signal %r, killing children and exiting' % signal
88 if child_pg is not None:
89 os.killpg(child_pg, signal.SIGHUP)
90 os.killpg(child_pg, signal.SIGTERM)
91 sys.exit(0)
92
93 signal.signal(signal.SIGINT, sig_handler)
94 signal.signal(signal.SIGQUIT, sig_handler)
95 signal.signal(signal.SIGTERM, sig_handler)
96
97
98 def execute_with_signal_translation(args):
99 """Run process as children and translate from SIGTERM to another signal"""
100 child = subprocess.Popen(args, close_fds=True, preexec_fn=os.setsid)
101 child_pg = child.pid
102 try:
103 print 'Process %r started' % args
104 while True:
105 time.sleep(10)
106 finally:
107 os.killpg(child_pg, signal.SIGHUP)
108 os.killpg(child_pg, signal.SIGTERM)