1 /* 2 * libjingle 3 * Copyright 2004--2009, Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include "talk/base/posix.h" 29 30 #include <sys/wait.h> 31 #include <errno.h> 32 #include <unistd.h> 33 34 #ifdef LINUX 35 #include "talk/base/linuxfdwalk.h" 36 #endif 37 #include "talk/base/logging.h" 38 39 namespace talk_base { 40 41 #ifdef LINUX 42 static void closefds(void *close_errors, int fd) { 43 if (fd <= 2) { 44 // We leave stdin/out/err open to the browser's terminal, if any. 45 return; 46 } 47 if (close(fd) < 0) { 48 *static_cast<bool *>(close_errors) = true; 49 } 50 } 51 #endif 52 53 enum { 54 EXIT_FLAG_CHDIR_ERRORS = 1 << 0, 55 #ifdef LINUX 56 EXIT_FLAG_FDWALK_ERRORS = 1 << 1, 57 EXIT_FLAG_CLOSE_ERRORS = 1 << 2, 58 #endif 59 EXIT_FLAG_SECOND_FORK_FAILED = 1 << 3, 60 }; 61 62 bool RunAsDaemon(const char *file, const char *const argv[]) { 63 // Fork intermediate child to daemonize. 64 pid_t pid = fork(); 65 if (pid < 0) { 66 LOG_ERR(LS_ERROR) << "fork()"; 67 return false; 68 } else if (!pid) { 69 // Child. 70 71 // We try to close all fds and change directory to /, but if that fails we 72 // keep going because it's not critical. 73 int exit_code = 0; 74 if (chdir("/") < 0) { 75 exit_code |= EXIT_FLAG_CHDIR_ERRORS; 76 } 77 #ifdef LINUX 78 bool close_errors = false; 79 if (fdwalk(&closefds, &close_errors) < 0) { 80 exit_code |= EXIT_FLAG_FDWALK_ERRORS; 81 } 82 if (close_errors) { 83 exit_code |= EXIT_FLAG_CLOSE_ERRORS; 84 } 85 #endif 86 87 // Fork again to become a daemon. 88 pid = fork(); 89 // It is important that everything here use _exit() and not exit(), because 90 // exit() would call the destructors of all global variables in the whole 91 // process, which is both unnecessary and unsafe. 92 if (pid < 0) { 93 exit_code |= EXIT_FLAG_SECOND_FORK_FAILED; 94 _exit(exit_code); // if second fork failed 95 } else if (!pid) { 96 // Child. 97 // Successfully daemonized. Run command. 98 // POSIX requires the args to be typed as non-const for historical 99 // reasons, but it mandates that the actual implementation be const, so 100 // the cast is safe. 101 execvp(file, const_cast<char *const *>(argv)); 102 _exit(255); // if execvp failed 103 } 104 105 // Parent. 106 // Successfully spawned process, but report any problems to the parent where 107 // we can log them. 108 _exit(exit_code); 109 } 110 111 // Parent. Reap intermediate child. 112 int status; 113 pid_t child = waitpid(pid, &status, 0); 114 if (child < 0) { 115 LOG_ERR(LS_ERROR) << "Error in waitpid()"; 116 return false; 117 } 118 if (child != pid) { 119 // Should never happen (see man page). 120 LOG(LS_ERROR) << "waitpid() chose wrong child???"; 121 return false; 122 } 123 if (!WIFEXITED(status)) { 124 LOG(LS_ERROR) << "Intermediate child killed uncleanly"; // Probably crashed 125 return false; 126 } 127 128 int exit_code = WEXITSTATUS(status); 129 if (exit_code & EXIT_FLAG_CHDIR_ERRORS) { 130 LOG(LS_WARNING) << "Child reported probles calling chdir()"; 131 } 132 #ifdef LINUX 133 if (exit_code & EXIT_FLAG_FDWALK_ERRORS) { 134 LOG(LS_WARNING) << "Child reported problems calling fdwalk()"; 135 } 136 if (exit_code & EXIT_FLAG_CLOSE_ERRORS) { 137 LOG(LS_WARNING) << "Child reported problems calling close()"; 138 } 139 #endif 140 if (exit_code & EXIT_FLAG_SECOND_FORK_FAILED) { 141 LOG(LS_ERROR) << "Failed to daemonize"; 142 // This means the command was not launched, so failure. 143 return false; 144 } 145 return true; 146 } 147 148 } // namespace talk_base 149