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