Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(82)

Side by Side Diff: tools/android/forwarder2/daemon.cc

Issue 11269036: Support HTTP test-server based net unit tests on Android. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix Clang build + sync Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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));
64 if (pid_file_fd < 0) {
65 PError("open()");
66 return pid_file.Pass();
67 }
68 FileDescriptorAutoCloser fd_closer(pid_file_fd);
69 struct flock lock_info = {};
70 lock_info.l_type = F_WRLCK;
71 lock_info.l_whence = SEEK_CUR;
72 if (HANDLE_EINTR(fcntl(pid_file_fd, F_SETLK, &lock_info)) < 0) {
73 if (errno == EAGAIN || errno == EACCES) {
74 LOG(ERROR) << "Daemon already running (PID file already locked)";
75 return pid_file.Pass();
76 }
77 PError("lockf()");
78 return pid_file.Pass();
79 }
80 const std::string pid_string = base::StringPrintf("%d\n", getpid());
81 CHECK(HANDLE_EINTR(write(pid_file_fd, pid_string.c_str(),
82 pid_string.length())));
83 pid_file.reset(new PIDFile(fd_closer.Release(), path));
84 return pid_file.Pass();
85 }
86
87 ~PIDFile() {
88 CloseFD(fd_); // This also releases the lock.
89 if (remove(path_.c_str()) < 0)
90 PError("remove");
91 }
92
93 private:
94 PIDFile(int fd, const std::string& path) : fd_(fd), path_(path) {
95 DCHECK(fd_ >= 0);
96 }
97
98 const int fd_;
99 const std::string path_;
100
101 DISALLOW_COPY_AND_ASSIGN(PIDFile);
102 };
103
104 // Takes ownership of |data|.
105 void ReleaseDaemonResourcesAtExit(void* data) {
106 DCHECK(data);
107 delete reinterpret_cast<PIDFile*>(data);
108 }
109
110 void InitLogging(const char* log_file) {
111 CHECK(
112 logging::InitLogging(
113 log_file,
114 logging::LOG_ONLY_TO_FILE,
115 logging::DONT_LOCK_LOG_FILE,
116 logging::APPEND_TO_OLD_LOG_FILE,
117 logging::ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS));
118 }
119
120 void SigChildHandler(int signal_number) {
121 DCHECK_EQ(signal_number, SIGCHLD);
122 // The daemon should not terminate while its parent is still running.
123 int status;
124 pid_t child_pid = waitpid(-1 /* any child */, &status, WNOHANG);
125 if (child_pid < 0) {
126 PError("waitpid");
127 return;
128 }
129 if (child_pid == 0)
130 return;
131 // Avoid using StringAppendF() since it's unsafe in a signal handler due to
132 // its use of LOG().
133 FixedSizeStringBuilder<256> string_builder;
134 string_builder.Append("Daemon (pid=%d) died unexpectedly with ", child_pid);
135 if (WIFEXITED(status))
136 string_builder.Append("status %d.", WEXITSTATUS(status));
137 else if (WIFSIGNALED(status))
138 string_builder.Append("signal %d.", WTERMSIG(status));
139 else
140 string_builder.Append("unknown reason.");
141 SIGNAL_SAFE_LOG(ERROR, string_builder.buffer());
142 }
143
144 // Note that 0 is written to |lock_owner_pid| in case the file is not locked.
145 bool GetFileLockOwnerPid(int fd, pid_t* lock_owner_pid) {
146 struct flock lock_info = {};
147 lock_info.l_type = F_WRLCK;
148 lock_info.l_whence = SEEK_CUR;
149 const int ret = HANDLE_EINTR(fcntl(fd, F_GETLK, &lock_info));
150 if (ret < 0) {
151 if (errno == EBADF) {
152 // Assume that the provided file descriptor corresponding to the PID file
153 // was valid until the daemon removed this file.
154 *lock_owner_pid = 0;
155 return true;
156 }
157 PError("fcntl");
158 return false;
159 }
160 if (lock_info.l_type == F_UNLCK) {
161 *lock_owner_pid = 0;
162 return true;
163 }
164 CHECK_EQ(F_WRLCK /* exclusive lock */, lock_info.l_type);
165 *lock_owner_pid = lock_info.l_pid;
166 return true;
167 }
168
169 } // namespace
170
171 Daemon::Daemon(const std::string& pid_file_path)
172 : pid_file_path_(pid_file_path) {
173 }
174
175 bool Daemon::Spawn(bool* is_daemon) {
176 switch (fork()) {
177 case -1:
178 *is_daemon = false;
179 PError("fork()");
180 return false;
181 case 0: { // Child.
182 *is_daemon = true;
183 scoped_ptr<PIDFile> pid_file = PIDFile::Create(pid_file_path_);
184 if (!pid_file)
185 return false;
186 base::AtExitManager::RegisterCallback(
187 &ReleaseDaemonResourcesAtExit, pid_file.release());
188 if (setsid() < 0) { // Detach the child process from its parent.
189 PError("setsid");
190 return false;
191 }
192 CloseFD(STDOUT_FILENO);
193 CloseFD(STDERR_FILENO);
194 InitLogging(kLogFilePath);
195 break;
196 }
197 default: // Parent.
198 *is_daemon = false;
199 signal(SIGCHLD, SigChildHandler);
200 }
201 return true;
202 }
203
204 bool Daemon::Kill() {
205 int pid_file_fd = HANDLE_EINTR(open(pid_file_path_.c_str(), O_WRONLY));
206 if (pid_file_fd < 0) {
207 if (errno == ENOENT)
208 return true;
209 LOG(ERROR) << "Could not open " << pid_file_path_ << " in write mode: "
210 << safe_strerror(errno);
211 return false;
212 }
213 const FileDescriptorAutoCloser fd_closer(pid_file_fd);
214 pid_t lock_owner_pid;
215 if (!GetFileLockOwnerPid(pid_file_fd, &lock_owner_pid))
216 return false;
217 if (lock_owner_pid == 0)
218 // No daemon running.
219 return true;
220 if (kill(lock_owner_pid, SIGTERM) < 0) {
221 if (errno == ESRCH /* invalid PID */)
222 // The daemon exited for some reason (e.g. kill by a process other than
223 // us) right before the call to kill() above.
224 return true;
225 PError("kill");
226 return false;
227 }
228 // Wait until the daemon exits. Rely on the fact that the daemon releases the
229 // lock on the PID file when it exits.
230 // TODO(pliard): Consider using a mutex + condition in shared memory to avoid
231 // polling.
232 const int kTries = 20;
233 const int kIdleTimeMS = 50;
234 for (int i = 0; i < kTries; ++i) {
235 pid_t current_lock_owner_pid;
236 if (!GetFileLockOwnerPid(pid_file_fd, &current_lock_owner_pid))
237 return false;
238 if (current_lock_owner_pid == 0)
239 // The daemon released the PID file's lock.
240 return true;
241 // Since we are polling we might not see the 'daemon exited' event if
242 // another daemon was spawned during our idle period.
243 if (current_lock_owner_pid != lock_owner_pid) {
244 LOG(WARNING) << "Daemon (pid=" << lock_owner_pid
245 << ") was successfully killed but a new daemon (pid="
246 << current_lock_owner_pid << ") seems to be running now.";
247 return true;
248 }
249 usleep(kIdleTimeMS * 1000);
250 }
251 LOG(ERROR) << "Timed out while killing daemon. "
252 "It might still be tearing down.";
253 return false;
254 }
255
256 } // namespace forwarder2
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698