blob: 76894044363e302e0e0b7854924cf9041648f373 [file] [log] [blame]
Brad Bishopc342db32019-05-15 21:57:59 -04001#
Patrick Williams92b42cb2022-09-03 06:53:57 -05002# Copyright BitBake Contributors
3#
Brad Bishopc342db32019-05-15 21:57:59 -04004# SPDX-License-Identifier: GPL-2.0-only
5#
6
Patrick Williamsc124f4f2015-09-15 14:41:29 -05007"""
8Python Daemonizing helper
9
Brad Bishopd7bf8c12018-02-25 22:55:05 -050010Originally based on code Copyright (C) 2005 Chad J. Schroeder but now heavily modified
11to allow a function to be daemonized and return for bitbake use by Richard Purdie
Patrick Williamsc124f4f2015-09-15 14:41:29 -050012"""
13
Brad Bishopd7bf8c12018-02-25 22:55:05 -050014import os
15import sys
16import io
17import traceback
Patrick Williamsc124f4f2015-09-15 14:41:29 -050018
Andrew Geisslerc9f78652020-09-18 14:11:35 -050019import bb
20
Patrick Williamsc124f4f2015-09-15 14:41:29 -050021def createDaemon(function, logfile):
22 """
23 Detach a process from the controlling terminal and run it in the
24 background as a daemon, returning control to the caller.
25 """
26
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080027 # Ensure stdout/stderror are flushed before forking to avoid duplicate output
28 sys.stdout.flush()
29 sys.stderr.flush()
30
Patrick Williamsc124f4f2015-09-15 14:41:29 -050031 try:
32 # Fork a child process so the parent can exit. This returns control to
33 # the command-line or shell. It also guarantees that the child will not
34 # be a process group leader, since the child receives a new process ID
35 # and inherits the parent's process group ID. This step is required
36 # to insure that the next call to os.setsid is successful.
37 pid = os.fork()
38 except OSError as e:
39 raise Exception("%s [%d]" % (e.strerror, e.errno))
40
41 if (pid == 0): # The first child.
42 # To become the session leader of this new session and the process group
43 # leader of the new process group, we call os.setsid(). The process is
44 # also guaranteed not to have a controlling terminal.
45 os.setsid()
Patrick Williamsc124f4f2015-09-15 14:41:29 -050046 try:
47 # Fork a second child and exit immediately to prevent zombies. This
48 # causes the second child process to be orphaned, making the init
49 # process responsible for its cleanup. And, since the first child is
50 # a session leader without a controlling terminal, it's possible for
51 # it to acquire one by opening a terminal in the future (System V-
52 # based systems). This second fork guarantees that the child is no
53 # longer a session leader, preventing the daemon from ever acquiring
54 # a controlling terminal.
55 pid = os.fork() # Fork a second child.
56 except OSError as e:
57 raise Exception("%s [%d]" % (e.strerror, e.errno))
58
Brad Bishopd7bf8c12018-02-25 22:55:05 -050059 if (pid != 0):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050060 # Parent (the first child) of the second child.
Brad Bishopd7bf8c12018-02-25 22:55:05 -050061 # exit() or _exit()?
62 # _exit is like exit(), but it doesn't call any functions registered
63 # with atexit (and on_exit) or any registered signal handlers. It also
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080064 # closes any open file descriptors, but doesn't flush any buffered output.
65 # Using exit() may cause all any temporary files to be unexpectedly
Brad Bishopd7bf8c12018-02-25 22:55:05 -050066 # removed. It's therefore recommended that child branches of a fork()
67 # and the parent branch(es) of a daemon use _exit().
Patrick Williamsc124f4f2015-09-15 14:41:29 -050068 os._exit(0)
69 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -050070 os.waitpid(pid, 0)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050071 return
72
Brad Bishopd7bf8c12018-02-25 22:55:05 -050073 # The second child.
Patrick Williamsc124f4f2015-09-15 14:41:29 -050074
Brad Bishopd7bf8c12018-02-25 22:55:05 -050075 # Replace standard fds with our own
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080076 with open('/dev/null', 'r') as si:
77 os.dup2(si.fileno(), sys.stdin.fileno())
Patrick Williamsc124f4f2015-09-15 14:41:29 -050078
Andrew Geissler595f6302022-01-24 19:11:47 +000079 with open(logfile, 'a+') as so:
80 try:
81 os.dup2(so.fileno(), sys.stdout.fileno())
82 os.dup2(so.fileno(), sys.stderr.fileno())
83 except io.UnsupportedOperation:
84 sys.stdout = so
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080085
Andrew Geissler595f6302022-01-24 19:11:47 +000086 # Have stdout and stderr be the same so log output matches chronologically
Andrew Geissler7e0e3c02022-02-25 20:34:39 +000087 # and there aren't two separate buffers
Andrew Geissler595f6302022-01-24 19:11:47 +000088 sys.stderr = sys.stdout
Patrick Williamsc124f4f2015-09-15 14:41:29 -050089
Andrew Geissler595f6302022-01-24 19:11:47 +000090 try:
91 function()
92 except Exception as e:
93 traceback.print_exc()
94 finally:
95 bb.event.print_ui_queue()
96 # os._exit() doesn't flush open files like os.exit() does. Manually flush
97 # stdout and stderr so that any logging output will be seen, particularly
98 # exception tracebacks.
99 sys.stdout.flush()
100 sys.stderr.flush()
101 os._exit(0)