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