daemonize.py 3.7 KB

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