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

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: Refine error handling in Daemon::Kill() 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));
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698