| | 279 | /** |
|---|
| | 280 | * Daemonize the current process, making it a background process. The current |
|---|
| | 281 | * process will exit if daemonizing is successful. |
|---|
| | 282 | * |
|---|
| | 283 | * @returns iprt status code. |
|---|
| | 284 | * @param fNoChDir Pass false to change working directory to "/". |
|---|
| | 285 | * @param fNoClose Pass false to redirect standard file streams to the null device. |
|---|
| | 286 | * @param pszPidfile Path to a file to write the process id of the daemon |
|---|
| | 287 | * process to. Daemonizing will fail if this file already |
|---|
| | 288 | * exists or cannot be written. May be NULL. |
|---|
| | 289 | */ |
|---|
| | 290 | RTR3DECL(int) RTProcDaemonize(bool fNoChDir, bool fNoClose, const char *pszPidfile) |
|---|
| | 291 | { |
|---|
| | 292 | /* |
|---|
| | 293 | * Fork the child process in a new session and quit the parent. |
|---|
| | 294 | * |
|---|
| | 295 | * - fork once and create a new session (setsid). This will detach us |
|---|
| | 296 | * from the controlling tty meaning that we won't receive the SIGHUP |
|---|
| | 297 | * (or any other signal) sent to that session. |
|---|
| | 298 | * - The SIGHUP signal is ignored because the session/parent may throw |
|---|
| | 299 | * us one before we get to the setsid. |
|---|
| | 300 | * - When the parent exit(0) we will become an orphan and re-parented to |
|---|
| | 301 | * the init process. |
|---|
| | 302 | * - Because of the sometimes unexpected semantics of assigning the |
|---|
| | 303 | * controlling tty automagically when a session leader first opens a tty, |
|---|
| | 304 | * we will fork() once more to get rid of the session leadership role. |
|---|
| | 305 | */ |
|---|
| | 306 | |
|---|
| | 307 | /* We start off by opening the pidfile, so that we can fail straight away |
|---|
| | 308 | * if it already exists. */ |
|---|
| | 309 | int fdPidfile = -1; |
|---|
| | 310 | if (pszPidfile != NULL) |
|---|
| | 311 | { |
|---|
| | 312 | /* @note the exclusive create is not guaranteed on all file |
|---|
| | 313 | * systems (e.g. NFSv2) */ |
|---|
| | 314 | if ((fdPidfile = open(pszPidfile, O_RDWR | O_CREAT | O_EXCL, 0644)) == -1) |
|---|
| | 315 | return RTErrConvertFromErrno(errno); |
|---|
| | 316 | } |
|---|
| | 317 | |
|---|
| | 318 | /* Ignore SIGHUP straight away. */ |
|---|
| | 319 | struct sigaction OldSigAct; |
|---|
| | 320 | struct sigaction SigAct; |
|---|
| | 321 | memset(&SigAct, 0, sizeof(SigAct)); |
|---|
| | 322 | SigAct.sa_handler = SIG_IGN; |
|---|
| | 323 | int rcSigAct = sigaction(SIGHUP, &SigAct, &OldSigAct); |
|---|
| | 324 | |
|---|
| | 325 | /* First fork, to become independent process. */ |
|---|
| | 326 | pid_t pid = fork(); |
|---|
| | 327 | if (pid == -1) |
|---|
| | 328 | return RTErrConvertFromErrno(errno); |
|---|
| | 329 | if (pid != 0) |
|---|
| | 330 | { |
|---|
| | 331 | /* Parent exits, no longer necessary. Child creates gets reparented |
|---|
| | 332 | * to the init process. */ |
|---|
| | 333 | exit(0); |
|---|
| | 334 | } |
|---|
| | 335 | |
|---|
| | 336 | /* Create new session, fix up the standard file descriptors and the |
|---|
| | 337 | * current working directory. */ |
|---|
| | 338 | pid_t newpgid = setsid(); |
|---|
| | 339 | int SavedErrno = errno; |
|---|
| | 340 | if (rcSigAct != -1) |
|---|
| | 341 | sigaction(SIGHUP, &OldSigAct, NULL); |
|---|
| | 342 | if (newpgid == -1) |
|---|
| | 343 | return RTErrConvertFromErrno(SavedErrno); |
|---|
| | 344 | |
|---|
| | 345 | if (!fNoClose) |
|---|
| | 346 | { |
|---|
| | 347 | /* Open stdin(0), stdout(1) and stderr(2) as /dev/null. */ |
|---|
| | 348 | int fd = open("/dev/null", O_RDWR); |
|---|
| | 349 | if (fd == -1) /* paranoia */ |
|---|
| | 350 | { |
|---|
| | 351 | close(STDIN_FILENO); |
|---|
| | 352 | close(STDOUT_FILENO); |
|---|
| | 353 | close(STDERR_FILENO); |
|---|
| | 354 | fd = open("/dev/null", O_RDWR); |
|---|
| | 355 | } |
|---|
| | 356 | if (fd != -1) |
|---|
| | 357 | { |
|---|
| | 358 | dup2(fd, STDIN_FILENO); |
|---|
| | 359 | dup2(fd, STDOUT_FILENO); |
|---|
| | 360 | dup2(fd, STDERR_FILENO); |
|---|
| | 361 | if (fd > 2) |
|---|
| | 362 | close(fd); |
|---|
| | 363 | } |
|---|
| | 364 | } |
|---|
| | 365 | |
|---|
| | 366 | if (!fNoChDir) |
|---|
| | 367 | chdir("/"); |
|---|
| | 368 | |
|---|
| | 369 | /* Second fork to lose session leader status. */ |
|---|
| | 370 | pid = fork(); |
|---|
| | 371 | if (pid == -1) |
|---|
| | 372 | return RTErrConvertFromErrno(errno); |
|---|
| | 373 | if (pid != 0) |
|---|
| | 374 | { |
|---|
| | 375 | /* Write the pid file, this is done in the parent, before exiting. */ |
|---|
| | 376 | if (fdPidfile != -1) |
|---|
| | 377 | { |
|---|
| | 378 | char szBuf[256]; |
|---|
| | 379 | size_t cbPid = RTStrPrintf(szBuf, sizeof(szBuf), "%d\n", pid); |
|---|
| | 380 | write(fdPidfile, szBuf, cbPid); |
|---|
| | 381 | close(fdPidfile); |
|---|
| | 382 | } |
|---|
| | 383 | exit(0); |
|---|
| | 384 | } |
|---|
| | 385 | |
|---|
| | 386 | return VINF_SUCCESS; |
|---|
| | 387 | } |
|---|
| | 388 | |
|---|