1/*
2 *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "webrtc/base/posix.h"
12
13#include <sys/wait.h>
14#include <errno.h>
15#include <unistd.h>
16
17#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)
18#include "webrtc/base/linuxfdwalk.h"
19#endif
20#include "webrtc/base/logging.h"
21
22namespace rtc {
23
24#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)
25static void closefds(void *close_errors, int fd) {
26  if (fd <= 2) {
27    // We leave stdin/out/err open to the browser's terminal, if any.
28    return;
29  }
30  if (close(fd) < 0) {
31    *static_cast<bool *>(close_errors) = true;
32  }
33}
34#endif
35
36enum {
37  EXIT_FLAG_CHDIR_ERRORS       = 1 << 0,
38#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)
39  EXIT_FLAG_FDWALK_ERRORS      = 1 << 1,
40  EXIT_FLAG_CLOSE_ERRORS       = 1 << 2,
41#endif
42  EXIT_FLAG_SECOND_FORK_FAILED = 1 << 3,
43};
44
45bool RunAsDaemon(const char *file, const char *const argv[]) {
46  // Fork intermediate child to daemonize.
47  pid_t pid = fork();
48  if (pid < 0) {
49    LOG_ERR(LS_ERROR) << "fork()";
50    return false;
51  } else if (!pid) {
52    // Child.
53
54    // We try to close all fds and change directory to /, but if that fails we
55    // keep going because it's not critical.
56    int exit_code = 0;
57    if (chdir("/") < 0) {
58      exit_code |= EXIT_FLAG_CHDIR_ERRORS;
59    }
60#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)
61    bool close_errors = false;
62    if (fdwalk(&closefds, &close_errors) < 0) {
63      exit_code |= EXIT_FLAG_FDWALK_ERRORS;
64    }
65    if (close_errors) {
66      exit_code |= EXIT_FLAG_CLOSE_ERRORS;
67    }
68#endif
69
70    // Fork again to become a daemon.
71    pid = fork();
72    // It is important that everything here use _exit() and not exit(), because
73    // exit() would call the destructors of all global variables in the whole
74    // process, which is both unnecessary and unsafe.
75    if (pid < 0) {
76      exit_code |= EXIT_FLAG_SECOND_FORK_FAILED;
77      _exit(exit_code);  // if second fork failed
78    } else if (!pid) {
79      // Child.
80      // Successfully daemonized. Run command.
81      // WEBRTC_POSIX requires the args to be typed as non-const for historical
82      // reasons, but it mandates that the actual implementation be const, so
83      // the cast is safe.
84      execvp(file, const_cast<char *const *>(argv));
85      _exit(255);  // if execvp failed
86    }
87
88    // Parent.
89    // Successfully spawned process, but report any problems to the parent where
90    // we can log them.
91    _exit(exit_code);
92  }
93
94  // Parent. Reap intermediate child.
95  int status;
96  pid_t child = waitpid(pid, &status, 0);
97  if (child < 0) {
98    LOG_ERR(LS_ERROR) << "Error in waitpid()";
99    return false;
100  }
101  if (child != pid) {
102    // Should never happen (see man page).
103    LOG(LS_ERROR) << "waitpid() chose wrong child???";
104    return false;
105  }
106  if (!WIFEXITED(status)) {
107    LOG(LS_ERROR) << "Intermediate child killed uncleanly";  // Probably crashed
108    return false;
109  }
110
111  int exit_code = WEXITSTATUS(status);
112  if (exit_code & EXIT_FLAG_CHDIR_ERRORS) {
113    LOG(LS_WARNING) << "Child reported probles calling chdir()";
114  }
115#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)
116  if (exit_code & EXIT_FLAG_FDWALK_ERRORS) {
117    LOG(LS_WARNING) << "Child reported problems calling fdwalk()";
118  }
119  if (exit_code & EXIT_FLAG_CLOSE_ERRORS) {
120    LOG(LS_WARNING) << "Child reported problems calling close()";
121  }
122#endif
123  if (exit_code & EXIT_FLAG_SECOND_FORK_FAILED) {
124    LOG(LS_ERROR) << "Failed to daemonize";
125    // This means the command was not launched, so failure.
126    return false;
127  }
128  return true;
129}
130
131}  // namespace rtc
132