blob: c937675eb61c7133939ce5d83faaace65528df90 [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001"""
2Python Daemonizing helper
3
4Originally based on code Copyright (C) 2005 Chad J. Schroeder but now heavily modified
5to allow a function to be daemonized and return for bitbake use by Richard Purdie
6"""
7
8import os
9import sys
10import io
11import traceback
12
13def createDaemon(function, logfile):
14 """
15 Detach a process from the controlling terminal and run it in the
16 background as a daemon, returning control to the caller.
17 """
18
19 # Ensure stdout/stderror are flushed before forking to avoid duplicate output
20 sys.stdout.flush()
21 sys.stderr.flush()
22
23 try:
24 # Fork a child process so the parent can exit. This returns control to
25 # the command-line or shell. It also guarantees that the child will not
26 # be a process group leader, since the child receives a new process ID
27 # and inherits the parent's process group ID. This step is required
28 # to insure that the next call to os.setsid is successful.
29 pid = os.fork()
30 except OSError as e:
31 raise Exception("%s [%d]" % (e.strerror, e.errno))
32
33 if (pid == 0): # The first child.
34 # To become the session leader of this new session and the process group
35 # leader of the new process group, we call os.setsid(). The process is
36 # also guaranteed not to have a controlling terminal.
37 os.setsid()
38 try:
39 # Fork a second child and exit immediately to prevent zombies. This
40 # causes the second child process to be orphaned, making the init
41 # process responsible for its cleanup. And, since the first child is
42 # a session leader without a controlling terminal, it's possible for
43 # it to acquire one by opening a terminal in the future (System V-
44 # based systems). This second fork guarantees that the child is no
45 # longer a session leader, preventing the daemon from ever acquiring
46 # a controlling terminal.
47 pid = os.fork() # Fork a second child.
48 except OSError as e:
49 raise Exception("%s [%d]" % (e.strerror, e.errno))
50
51 if (pid != 0):
52 # Parent (the first child) of the second child.
53 # exit() or _exit()?
54 # _exit is like exit(), but it doesn't call any functions registered
55 # with atexit (and on_exit) or any registered signal handlers. It also
56 # closes any open file descriptors, but doesn't flush any buffered output.
57 # Using exit() may cause all any temporary files to be unexpectedly
58 # removed. It's therefore recommended that child branches of a fork()
59 # and the parent branch(es) of a daemon use _exit().
60 os._exit(0)
61 else:
62 os.waitpid(pid, 0)
63 return
64
65 # The second child.
66
67 # Replace standard fds with our own
68 with open('/dev/null', 'r') as si:
69 os.dup2(si.fileno(), sys.stdin.fileno())
70
71 try:
72 so = open(logfile, 'a+')
73 os.dup2(so.fileno(), sys.stdout.fileno())
74 os.dup2(so.fileno(), sys.stderr.fileno())
75 except io.UnsupportedOperation:
76 sys.stdout = open(logfile, 'a+')
77
78 # Have stdout and stderr be the same so log output matches chronologically
79 # and there aren't two seperate buffers
80 sys.stderr = sys.stdout
81
82 try:
83 function()
84 except Exception as e:
85 traceback.print_exc()
86 finally:
87 bb.event.print_ui_queue()
88 # os._exit() doesn't flush open files like os.exit() does. Manually flush
89 # stdout and stderr so that any logging output will be seen, particularly
90 # exception tracebacks.
91 sys.stdout.flush()
92 sys.stderr.flush()
93 os._exit(0)