OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "tools/android/forwarder2/daemon.h" | |
6 | |
7 #include <errno.h> | |
8 #include <fcntl.h> | |
9 #include <signal.h> | |
10 #include <stdio.h> | |
11 #include <sys/file.h> | |
12 #include <sys/stat.h> | |
13 #include <sys/types.h> | |
14 #include <sys/wait.h> | |
15 #include <unistd.h> | |
16 | |
17 #include <string> | |
18 | |
19 #include "base/basictypes.h" | |
20 #include "base/eintr_wrapper.h" | |
21 #include "base/file_path.h" | |
22 #include "base/file_util.h" | |
23 #include "base/logging.h" | |
24 #include "base/safe_strerror_posix.h" | |
25 #include "base/string_number_conversions.h" | |
26 #include "base/stringprintf.h" | |
27 #include "tools/android/forwarder2/common.h" | |
28 | |
29 namespace forwarder2 { | |
30 namespace { | |
31 | |
32 const char kLogFilePath[] = "/tmp/host_forwarder_log"; | |
33 | |
34 class FileDescriptorAutoCloser { | |
35 public: | |
36 explicit FileDescriptorAutoCloser(int fd) : fd_(fd) { | |
37 DCHECK(fd_ >= 0); | |
38 } | |
39 | |
40 ~FileDescriptorAutoCloser() { | |
41 if (fd_ > -1) | |
42 CloseFD(fd_); | |
43 } | |
44 | |
45 int Release() { | |
46 const int fd = fd_; | |
47 fd_ = -1; | |
48 return fd; | |
49 } | |
50 | |
51 private: | |
52 int fd_; | |
53 | |
54 DISALLOW_COPY_AND_ASSIGN(FileDescriptorAutoCloser); | |
55 }; | |
56 | |
57 // Handles creation and destruction of the PID file. | |
58 class PIDFile { | |
59 public: | |
60 static scoped_ptr<PIDFile> Create(const std::string& path) { | |
61 scoped_ptr<PIDFile> pid_file; | |
62 const int pid_file_fd = HANDLE_EINTR( | |
63 open(path.c_str(), O_CREAT | O_WRONLY, 0600)); | |
digit1
2012/11/06 10:39:24
Note: there is a slight race condition here betwee
Philippe
2012/11/06 16:13:31
I did what we talked about offline. I'm now fetchi
| |
64 if (pid_file_fd < 0) { | |
65 PError("open()"); | |
66 return pid_file.Pass(); | |
67 } | |
68 FileDescriptorAutoCloser fd_closer(pid_file_fd); | |
69 if (HANDLE_EINTR(flock(pid_file_fd, LOCK_EX | LOCK_NB)) < 0) { | |
70 if (errno == EAGAIN || errno == EACCES) { | |
71 LOG(ERROR) << "Daemon already running (PID file already locked)"; | |
72 return pid_file.Pass(); | |
73 } | |
74 PError("lockf()"); | |
75 return pid_file.Pass(); | |
76 } | |
77 const std::string pid_string = base::StringPrintf("%d\n", getpid()); | |
78 CHECK(HANDLE_EINTR(write(pid_file_fd, pid_string.c_str(), | |
79 pid_string.length()))); | |
80 pid_file.reset(new PIDFile(fd_closer.Release(), path)); | |
81 return pid_file.Pass(); | |
82 } | |
83 | |
84 ~PIDFile() { | |
85 CloseFD(fd_); // This also releases the lock. | |
86 if (remove(path_.c_str()) < 0) | |
87 PError("remove"); | |
88 } | |
89 | |
90 private: | |
91 PIDFile(int fd, const std::string& path) : fd_(fd), path_(path) { | |
92 DCHECK(fd_ >= 0); | |
93 } | |
94 | |
95 const int fd_; | |
96 const std::string path_; | |
97 | |
98 DISALLOW_COPY_AND_ASSIGN(PIDFile); | |
99 }; | |
100 | |
101 // Takes ownership of |data|. | |
102 void ReleaseDaemonResourcesAtExit(void* data) { | |
103 DCHECK(data); | |
104 delete reinterpret_cast<PIDFile*>(data); | |
105 } | |
106 | |
107 void InitLogging(const char* log_file) { | |
108 CHECK( | |
109 logging::InitLogging( | |
110 log_file, | |
111 logging::LOG_ONLY_TO_FILE, | |
112 logging::DONT_LOCK_LOG_FILE, | |
113 logging::APPEND_TO_OLD_LOG_FILE, | |
114 logging::ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS)); | |
115 } | |
116 | |
117 void SigChildHandler(int signal_number) { | |
118 DCHECK_EQ(signal_number, SIGCHLD); | |
119 // The daemon should not terminate while its parent is still running. | |
120 int status; | |
121 pid_t child_pid = waitpid(-1 /* any child */, &status, WNOHANG); | |
122 if (child_pid < 0) { | |
123 PError("waitpid"); | |
124 return; | |
125 } | |
126 if (child_pid == 0) | |
127 return; | |
128 // Avoid using StringAppendF() since it's unsafe in a signal handler due to | |
129 // its use of LOG(). | |
130 FixedSizeStringBuilder<256> string_builder; | |
131 string_builder.Append("Daemon (pid=%d) died unexpectedly with ", child_pid); | |
132 if (WIFEXITED(status)) | |
133 string_builder.Append("status %d.", WEXITSTATUS(status)); | |
134 else if (WIFSIGNALED(status)) | |
135 string_builder.Append("signal %d.", WTERMSIG(status)); | |
136 else | |
137 string_builder.Append("unknown reason."); | |
138 SIGNAL_SAFE_LOG(ERROR, string_builder.buffer()); | |
139 } | |
140 | |
141 } // namespace | |
142 | |
143 Daemon::Daemon(const std::string& pid_file_path) | |
144 : pid_file_path_(pid_file_path) { | |
145 } | |
146 | |
147 bool Daemon::Spawn(bool* is_daemon) { | |
148 switch (fork()) { | |
digit1
2012/11/06 10:39:24
Do you need a double-fork here to avoid zombies?
Philippe
2012/11/06 16:13:31
As we discussed offline I think the current implem
| |
149 case -1: | |
150 *is_daemon = false; | |
151 PError("fork()"); | |
152 return false; | |
153 case 0: { // Child. | |
154 *is_daemon = true; | |
155 scoped_ptr<PIDFile> pid_file = PIDFile::Create(pid_file_path_); | |
156 if (!pid_file) | |
157 return false; | |
158 base::AtExitManager::RegisterCallback( | |
159 &ReleaseDaemonResourcesAtExit, pid_file.release()); | |
160 if (setsid() < 0) { // Detach the child process from its parent. | |
161 PError("setsid"); | |
162 return false; | |
163 } | |
164 CloseFD(STDOUT_FILENO); | |
165 CloseFD(STDERR_FILENO); | |
166 InitLogging(kLogFilePath); | |
167 break; | |
168 } | |
169 default: // Parent. | |
170 *is_daemon = false; | |
171 signal(SIGCHLD, SigChildHandler); | |
172 } | |
173 return true; | |
174 } | |
175 | |
176 bool Daemon::Kill() { | |
177 std::string pid_string; | |
178 const FilePath pid_file_path(pid_file_path_); | |
179 if (!file_util::ReadFileToString(pid_file_path, &pid_string)) { | |
180 int error = errno; | |
181 if (file_util::PathExists(pid_file_path)) { | |
182 LOG(ERROR) << "Could not read file " << pid_file_path_ << ": " | |
183 << safe_strerror(error); | |
184 return false; | |
185 } | |
186 // Reasonably assume that the daemon is not running. | |
187 return true; | |
188 } | |
189 CHECK(pid_string.length() > 1); | |
190 // Remove the trailing \n. | |
191 pid_string.resize(pid_string.length() - 1); | |
192 pid_t pid; | |
193 CHECK(base::StringToInt(pid_string, &pid)); | |
194 CHECK_NE(pid, getpid()); | |
195 if (kill(pid, SIGTERM) < 0) { | |
196 DCHECK_EQ(ESRCH /* invalid PID */, errno); | |
197 // The daemon is not running and didn't remove its PID file for some reason. | |
198 // We don't treat this as an error. | |
199 if (remove(pid_file_path_.c_str()) < 0) | |
200 PError("remove"); | |
201 return true; | |
202 } | |
203 int pid_file_fd = HANDLE_EINTR(open(pid_file_path_.c_str(), O_WRONLY)); | |
204 if (pid_file_fd < 0) { | |
205 if (errno == ENOENT) | |
206 // Note that the file was present when ReadFileToString() was called above | |
207 // so assume that it was just removed by the daemon between the call to | |
208 // kill() above and here. | |
209 return true; | |
210 LOG(ERROR) << "Could not open " << pid_file_path_ << " in write mode: " | |
211 << safe_strerror(errno); | |
212 return false; | |
213 } | |
214 const FileDescriptorAutoCloser fd_closer(pid_file_fd); | |
215 // Wait until the daemon exits. Rely on the fact that the daemon releases the | |
216 // lock on the PID file when it exits. | |
217 // TODO(pliard): Consider using a mutex + condition in shared memory to avoid | |
218 // polling. | |
219 const int kTries = 20; | |
220 const int kIdleTimeMS = 100; | |
Philippe
2012/11/06 16:13:31
I decreased this value which was a bit high.
| |
221 for (int i = 0; i < kTries; ++i) { | |
222 struct flock lock_info = {}; | |
223 lock_info.l_type = F_WRLCK; | |
224 lock_info.l_whence = SEEK_CUR; | |
225 const int ret = HANDLE_EINTR(fcntl(pid_file_fd, F_GETLK, &lock_info)); | |
226 if (ret < 0) | |
227 PError("fcntl"); | |
228 else if (lock_info.l_type == F_UNLCK) | |
229 return true; | |
230 else { | |
231 CHECK_EQ(F_WRLCK /* exclusive lock */, lock_info.l_type); | |
232 if (lock_info.l_pid != pid) { | |
233 LOG(WARNING) << "Daemon (pid=" << pid | |
234 << ") was successfully killed but a new daemon (pid=" | |
235 << lock_info.l_pid << ") seems to be running now."; | |
236 return true; | |
237 } | |
238 } | |
239 usleep(kIdleTimeMS * 1000); | |
240 } | |
241 LOG(ERROR) << "Timed out while killing daemon. " | |
242 "It might still be tearing down."; | |
243 return false; | |
244 } | |
245 | |
246 } // namespace forwarder2 | |
OLD | NEW |