Home | History | Annotate | Download | only in base
      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 
     22 namespace rtc {
     23 
     24 #if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)
     25 static 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 
     36 enum {
     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 
     45 bool 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