Read launch arguments from json file and launch the process.
Expected that json contains "args" key with string or list of strings.
Arguments are converted to string using list2cmdline. At the end is added & which will cause that launched process is detached and running as "background" process.
Notes
@iLLiCiT: This should be possible to do with 'disown' or double forking but I didn't find a way how to do it properly. Disown didn't work as expected for me and double forking killed parent process which is unexpected too.
Source code in app_launcher.py
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106 | def main(input_json_path):
"""Read launch arguments from json file and launch the process.
Expected that json contains "args" key with string or list of strings.
Arguments are converted to string using `list2cmdline`. At the end is added
`&` which will cause that launched process is detached and running as
"background" process.
## Notes
@iLLiCiT: This should be possible to do with 'disown' or double forking but
I didn't find a way how to do it properly. Disown didn't work as
expected for me and double forking killed parent process which is
unexpected too.
"""
with open(input_json_path, "r") as stream:
data = json.load(stream)
# Change environment variables
env = data.get("env") or {}
# Add AYON_PID_FILE environment variable if pid_file is specified
pid_file_path = data.get("pid_file")
if pid_file_path:
env["AYON_PID_FILE"] = pid_file_path
for key, value in env.items():
os.environ[key] = value
# Prepare launch arguments
args = data["args"]
if isinstance(args, list):
args = shlex.join(args)
# Run the command as background process and echo the pid to tempfile
with tempfile.NamedTemporaryFile(delete=False) as tmpfile:
pid_path = tmpfile.name
stdout_path = stderr_path = "/dev/null"
if "stdout" in data:
stdout_path = data["stdout"]
if "stderr" in data:
stderr_path = data["stderr"]
post_args = []
if stdout_path:
stdout = f">{stdout_path}"
post_args.append(stdout)
if stderr_path:
if stdout_path == stderr_path:
stderr_path = "&1"
stderr = f"2>{stderr_path}"
post_args.append(stderr)
post_args_s = ""
if post_args:
joined_args = " ".join(post_args)
post_args_s = f" {joined_args}"
shell_cmd = f"{args}{post_args_s} & echo $! > {pid_path}"
os.system(shell_cmd)
with open(pid_path, "r") as stream:
content = stream.read()
os.remove(pid_path)
try:
initial_pid = int(content)
except Exception:
initial_pid = None
# Check if shell script provided actual application PID via PID file
final_pid = initial_pid
if pid_file_path and final_pid is not None:
# Wait a short time for shell script to potentially write actual PID
time.sleep(0.5) # Give shell script time to launch app and write PID
with contextlib.suppress(OSError, ValueError):
with open(pid_file_path, "r") as pid_file:
script_pid_content = pid_file.read().strip()
if script_pid_content.isdigit():
script_pid = int(script_pid_content)
# Only use if different from shell PID
if script_pid != final_pid:
final_pid = script_pid
data["pid"] = final_pid
with open(input_json_path, "w") as stream:
json.dump(data, stream)
sys.exit(0)
|