daemonize.py 3.7 KB

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