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 | |
28 namespace { | |
29 | |
30 const char kLogFilePath[] = "/tmp/host_forwarder_log"; | |
digit1
2012/10/29 12:07:32
Generally speaking, it's better to create UID-spec
Philippe
2012/10/30 13:54:37
I see your point. I would like to prevent this use
| |
31 | |
32 void PError(const char* msg) { | |
33 LOG(ERROR) << msg << ": " << safe_strerror(errno); | |
34 } | |
35 | |
36 void CloseFD(int fd) { | |
37 if (HANDLE_EINTR(close(fd)) < 0) | |
digit1
2012/10/29 12:07:32
Would you want to preserve errno here?
Philippe
2012/10/30 13:54:37
Good point.
| |
38 PError("close"); | |
39 } | |
40 | |
41 class FileDescriptorAutoCloser { | |
42 public: | |
43 explicit FileDescriptorAutoCloser(int fd) : fd_(fd) { | |
44 DCHECK(fd_ >= 0); | |
45 } | |
46 | |
47 ~FileDescriptorAutoCloser() { | |
48 if (fd_ > -1) | |
49 CloseFD(fd_); | |
50 } | |
51 | |
52 int Release() { | |
53 const int fd = fd_; | |
54 fd_ = -1; | |
55 return fd; | |
56 } | |
57 | |
58 private: | |
59 int fd_; | |
60 | |
61 DISALLOW_COPY_AND_ASSIGN(FileDescriptorAutoCloser); | |
62 }; | |
63 | |
64 // Handles creation and destruction of the PID file. | |
65 class PIDFile { | |
66 public: | |
67 static scoped_ptr<PIDFile> Create(const std::string& path) { | |
68 scoped_ptr<PIDFile> pid_file; | |
69 const int pid_file_fd = HANDLE_EINTR( | |
70 open(path.c_str(), O_CREAT | O_WRONLY, 0600)); | |
71 if (pid_file_fd < 0) { | |
72 PError("open()"); | |
73 return pid_file.Pass(); | |
74 } | |
75 FileDescriptorAutoCloser fd_closer(pid_file_fd); | |
76 if (HANDLE_EINTR(flock(pid_file_fd, LOCK_EX | LOCK_NB)) < 0) { | |
77 if (errno == EAGAIN || errno == EACCES) { | |
78 LOG(ERROR) << "Daemon already running (PID file already locked)"; | |
79 return pid_file.Pass(); | |
80 } | |
81 PError("lockf()"); | |
82 return pid_file.Pass(); | |
83 } | |
84 const std::string pid_string = base::StringPrintf("%d\n", getpid()); | |
85 CHECK(HANDLE_EINTR(write(pid_file_fd, pid_string.c_str(), | |
86 pid_string.length()))); | |
87 pid_file.reset(new PIDFile(fd_closer.Release(), path)); | |
88 return pid_file.Pass(); | |
89 } | |
90 | |
91 ~PIDFile() { | |
92 CloseFD(fd_); // This also releases the lock. | |
93 if (remove(path_.c_str()) < 0) | |
94 PError("remove"); | |
95 } | |
96 | |
97 private: | |
98 PIDFile(int fd, const std::string& path) : fd_(fd), path_(path) { | |
99 DCHECK(fd_ >= 0); | |
100 } | |
101 | |
102 const int fd_; | |
103 const std::string path_; | |
104 | |
105 DISALLOW_COPY_AND_ASSIGN(PIDFile); | |
106 }; | |
107 | |
108 // Takes ownership of |data|. | |
109 void ReleaseDaemonResourcesAtExit(void* data) { | |
110 DCHECK(data); | |
111 delete reinterpret_cast<PIDFile*>(data); | |
112 } | |
113 | |
114 void InitLogging(const char* log_file) { | |
115 CHECK( | |
116 logging::InitLogging( | |
117 log_file, | |
118 logging::LOG_ONLY_TO_FILE, | |
119 logging::DONT_LOCK_LOG_FILE, | |
120 logging::APPEND_TO_OLD_LOG_FILE, | |
121 logging::ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS)); | |
122 } | |
123 | |
124 void SigChildHandler(int signal_number) { | |
125 DCHECK_EQ(signal_number, SIGCHLD); | |
126 // The daemon should not terminate while its parent is still running. | |
127 int status; | |
128 pid_t child_pid = waitpid(-1 /* any child */, &status, WNOHANG); | |
129 if (child_pid < 0) { | |
130 PError("waitpid"); | |
131 return; | |
132 } | |
133 if (child_pid == 0) | |
134 return; | |
135 LOG(ERROR) << "Daemon (pid=" << child_pid << ") died unexpectedly with "; | |
digit1
2012/10/29 12:07:32
I'm not sure you can use LOG() in a signal handler
Philippe
2012/10/30 13:54:37
Good catch, indeed. I added common.{cc,h} that pro
| |
136 if (WIFEXITED(status)) | |
137 LOG(ERROR) << "status " << WEXITSTATUS(status); | |
138 else if (WIFSIGNALED(status)) | |
139 LOG(ERROR) << "signal " << WTERMSIG(status); | |
140 else | |
141 LOG(ERROR) << "unknown reason"; | |
142 } | |
143 | |
144 } // namespace | |
145 | |
146 namespace forwarder2 { | |
147 | |
148 Daemon::Daemon(const std::string& pid_file_path) | |
149 : pid_file_path_(pid_file_path) { | |
150 } | |
151 | |
152 bool Daemon::Spawn(bool* is_daemon) { | |
153 switch (fork()) { | |
154 case -1: | |
155 *is_daemon = false; | |
156 PError("fork()"); | |
157 return false; | |
158 case 0: { // Child. | |
159 *is_daemon = true; | |
160 scoped_ptr<PIDFile> pid_file = PIDFile::Create(pid_file_path_); | |
161 if (!pid_file) | |
162 return false; | |
163 base::AtExitManager::RegisterCallback( | |
164 &ReleaseDaemonResourcesAtExit, pid_file.release()); | |
165 if (setsid() < 0) { // Detach the child process from its parent. | |
166 PError("setsid"); | |
167 return false; | |
168 } | |
169 CloseFD(STDOUT_FILENO); | |
170 CloseFD(STDERR_FILENO); | |
171 InitLogging(kLogFilePath); | |
172 break; | |
173 } | |
174 default: // Parent. | |
175 *is_daemon = false; | |
176 signal(SIGCHLD, SigChildHandler); | |
177 } | |
178 return true; | |
179 } | |
180 | |
181 bool Daemon::Kill() { | |
182 std::string pid_string; | |
183 const FilePath pid_file_path(pid_file_path_); | |
184 if (!file_util::ReadFileToString(pid_file_path, &pid_string)) { | |
185 int error = errno; | |
186 if (file_util::PathExists(pid_file_path)) { | |
187 LOG(ERROR) << "Could not read file " << pid_file_path_ << ": " | |
188 << safe_strerror(error); | |
189 return false; | |
190 } | |
191 // Reasonably assume that the daemon is not running. | |
192 return true; | |
193 } | |
194 CHECK(pid_string.length() > 1); | |
195 // Remove the trailing \n. | |
196 pid_string.resize(pid_string.length() - 1); | |
197 pid_t pid; | |
198 CHECK(base::StringToInt(pid_string, &pid)); | |
199 CHECK_NE(pid, getpid()); | |
200 CHECK_EQ(0, kill(pid, SIGTERM)); | |
201 int pid_file_fd = HANDLE_EINTR(open(pid_file_path_.c_str(), O_WRONLY)); | |
202 if (pid_file_fd < 0) { | |
203 LOG(ERROR) << "Could not open " << pid_file_path_ << " in write mode: " | |
204 << safe_strerror(errno); | |
205 return false; | |
206 } | |
207 const FileDescriptorAutoCloser fd_closer(pid_file_fd); | |
208 // Wait until the daemon exits. Rely on the fact that the daemon releases the | |
209 // lock on the PID file when it exits. | |
210 // TODO(pliard): Consider using a mutex + condition in shared memory to avoid | |
211 // polling. | |
212 const int kTries = 20; | |
213 const int kIdleTimeMS = 100; | |
214 for (int i = 0; i < kTries; ++i) { | |
215 struct flock lock_info = {}; | |
216 lock_info.l_type = F_WRLCK; | |
217 lock_info.l_whence = SEEK_CUR; | |
218 const int ret = HANDLE_EINTR(fcntl(pid_file_fd, F_GETLK, &lock_info)); | |
219 if (ret < 0) | |
220 PError("fcntl"); | |
221 else if (lock_info.l_type == F_UNLCK) | |
222 return true; | |
223 else { | |
224 CHECK_EQ(F_WRLCK /* exclusive lock */, lock_info.l_type); | |
225 if (lock_info.l_pid != pid) { | |
226 LOG(WARNING) << "Daemon (pid=" << pid | |
227 << ") was successfully killed but a new daemon (pid=" | |
228 << lock_info.l_pid << ") seems to be running now."; | |
229 return true; | |
230 } | |
231 } | |
232 usleep(kIdleTimeMS * 1000); | |
233 } | |
234 LOG(ERROR) << "Timed out while killing daemon. " | |
235 "It might still be tearing down."; | |
236 return false; | |
237 } | |
238 | |
239 } // namespace forwarder2 | |
OLD | NEW |