Arrange to call func() in a grandchild of the current process. Return 0 for success, exit status for failure, negative if subprocess killed by signal.
(func: Callable[[], None], log_file: str | None = None)
| 75 | else: |
| 76 | |
| 77 | def _daemonize_cb(func: Callable[[], None], log_file: str | None = None) -> int: |
| 78 | """Arrange to call func() in a grandchild of the current process. |
| 79 | |
| 80 | Return 0 for success, exit status for failure, negative if |
| 81 | subprocess killed by signal. |
| 82 | """ |
| 83 | # See https://stackoverflow.com/questions/473620/how-do-you-create-a-daemon-in-python |
| 84 | sys.stdout.flush() |
| 85 | sys.stderr.flush() |
| 86 | pid = os.fork() |
| 87 | if pid: |
| 88 | # Parent process: wait for child in case things go bad there. |
| 89 | npid, sts = os.waitpid(pid, 0) |
| 90 | sig = sts & 0xFF |
| 91 | if sig: |
| 92 | print("Child killed by signal", sig) |
| 93 | return -sig |
| 94 | sts = sts >> 8 |
| 95 | if sts: |
| 96 | print("Child exit status", sts) |
| 97 | return sts |
| 98 | # Child process: do a bunch of UNIX stuff and then fork a grandchild. |
| 99 | try: |
| 100 | os.setsid() # Detach controlling terminal |
| 101 | os.umask(0o27) |
| 102 | devnull = os.open("/dev/null", os.O_RDWR) |
| 103 | os.dup2(devnull, 0) |
| 104 | os.dup2(devnull, 1) |
| 105 | os.dup2(devnull, 2) |
| 106 | os.close(devnull) |
| 107 | pid = os.fork() |
| 108 | if pid: |
| 109 | # Child is done, exit to parent. |
| 110 | os._exit(0) |
| 111 | # Grandchild: run the server. |
| 112 | if log_file: |
| 113 | sys.stdout = sys.stderr = open(log_file, "a", buffering=1) |
| 114 | fd = sys.stdout.fileno() |
| 115 | os.dup2(fd, 2) |
| 116 | os.dup2(fd, 1) |
| 117 | func() |
| 118 | finally: |
| 119 | # Make sure we never get back into the caller. |
| 120 | os._exit(1) |
| 121 | |
| 122 | def daemonize( |
| 123 | options: Options, status_file: str, timeout: int | None = None, log_file: str | None = None |