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

Unified 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: Remove duplicate include Created 8 years, 2 months 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/android/forwarder2/daemon.h ('k') | tools/android/forwarder2/device_controller.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/android/forwarder2/daemon.cc
diff --git a/tools/android/forwarder2/daemon.cc b/tools/android/forwarder2/daemon.cc
new file mode 100644
index 0000000000000000000000000000000000000000..7c04d689a0c14bc85f205ce419e2ff05a7e00bf1
--- /dev/null
+++ b/tools/android/forwarder2/daemon.cc
@@ -0,0 +1,239 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/android/forwarder2/daemon.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/eintr_wrapper.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/safe_strerror_posix.h"
+#include "base/string_number_conversions.h"
+#include "base/stringprintf.h"
+
+namespace {
+
+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
+
+void PError(const char* msg) {
+ LOG(ERROR) << msg << ": " << safe_strerror(errno);
+}
+
+void CloseFD(int fd) {
+ 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.
+ PError("close");
+}
+
+class FileDescriptorAutoCloser {
+ public:
+ explicit FileDescriptorAutoCloser(int fd) : fd_(fd) {
+ DCHECK(fd_ >= 0);
+ }
+
+ ~FileDescriptorAutoCloser() {
+ if (fd_ > -1)
+ CloseFD(fd_);
+ }
+
+ int Release() {
+ const int fd = fd_;
+ fd_ = -1;
+ return fd;
+ }
+
+ private:
+ int fd_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileDescriptorAutoCloser);
+};
+
+// Handles creation and destruction of the PID file.
+class PIDFile {
+ public:
+ static scoped_ptr<PIDFile> Create(const std::string& path) {
+ scoped_ptr<PIDFile> pid_file;
+ const int pid_file_fd = HANDLE_EINTR(
+ open(path.c_str(), O_CREAT | O_WRONLY, 0600));
+ if (pid_file_fd < 0) {
+ PError("open()");
+ return pid_file.Pass();
+ }
+ FileDescriptorAutoCloser fd_closer(pid_file_fd);
+ if (HANDLE_EINTR(flock(pid_file_fd, LOCK_EX | LOCK_NB)) < 0) {
+ if (errno == EAGAIN || errno == EACCES) {
+ LOG(ERROR) << "Daemon already running (PID file already locked)";
+ return pid_file.Pass();
+ }
+ PError("lockf()");
+ return pid_file.Pass();
+ }
+ const std::string pid_string = base::StringPrintf("%d\n", getpid());
+ CHECK(HANDLE_EINTR(write(pid_file_fd, pid_string.c_str(),
+ pid_string.length())));
+ pid_file.reset(new PIDFile(fd_closer.Release(), path));
+ return pid_file.Pass();
+ }
+
+ ~PIDFile() {
+ CloseFD(fd_); // This also releases the lock.
+ if (remove(path_.c_str()) < 0)
+ PError("remove");
+ }
+
+ private:
+ PIDFile(int fd, const std::string& path) : fd_(fd), path_(path) {
+ DCHECK(fd_ >= 0);
+ }
+
+ const int fd_;
+ const std::string path_;
+
+ DISALLOW_COPY_AND_ASSIGN(PIDFile);
+};
+
+// Takes ownership of |data|.
+void ReleaseDaemonResourcesAtExit(void* data) {
+ DCHECK(data);
+ delete reinterpret_cast<PIDFile*>(data);
+}
+
+void InitLogging(const char* log_file) {
+ CHECK(
+ logging::InitLogging(
+ log_file,
+ logging::LOG_ONLY_TO_FILE,
+ logging::DONT_LOCK_LOG_FILE,
+ logging::APPEND_TO_OLD_LOG_FILE,
+ logging::ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS));
+}
+
+void SigChildHandler(int signal_number) {
+ DCHECK_EQ(signal_number, SIGCHLD);
+ // The daemon should not terminate while its parent is still running.
+ int status;
+ pid_t child_pid = waitpid(-1 /* any child */, &status, WNOHANG);
+ if (child_pid < 0) {
+ PError("waitpid");
+ return;
+ }
+ if (child_pid == 0)
+ return;
+ 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
+ if (WIFEXITED(status))
+ LOG(ERROR) << "status " << WEXITSTATUS(status);
+ else if (WIFSIGNALED(status))
+ LOG(ERROR) << "signal " << WTERMSIG(status);
+ else
+ LOG(ERROR) << "unknown reason";
+}
+
+} // namespace
+
+namespace forwarder2 {
+
+Daemon::Daemon(const std::string& pid_file_path)
+ : pid_file_path_(pid_file_path) {
+}
+
+bool Daemon::Spawn(bool* is_daemon) {
+ switch (fork()) {
+ case -1:
+ *is_daemon = false;
+ PError("fork()");
+ return false;
+ case 0: { // Child.
+ *is_daemon = true;
+ scoped_ptr<PIDFile> pid_file = PIDFile::Create(pid_file_path_);
+ if (!pid_file)
+ return false;
+ base::AtExitManager::RegisterCallback(
+ &ReleaseDaemonResourcesAtExit, pid_file.release());
+ if (setsid() < 0) { // Detach the child process from its parent.
+ PError("setsid");
+ return false;
+ }
+ CloseFD(STDOUT_FILENO);
+ CloseFD(STDERR_FILENO);
+ InitLogging(kLogFilePath);
+ break;
+ }
+ default: // Parent.
+ *is_daemon = false;
+ signal(SIGCHLD, SigChildHandler);
+ }
+ return true;
+}
+
+bool Daemon::Kill() {
+ std::string pid_string;
+ const FilePath pid_file_path(pid_file_path_);
+ if (!file_util::ReadFileToString(pid_file_path, &pid_string)) {
+ int error = errno;
+ if (file_util::PathExists(pid_file_path)) {
+ LOG(ERROR) << "Could not read file " << pid_file_path_ << ": "
+ << safe_strerror(error);
+ return false;
+ }
+ // Reasonably assume that the daemon is not running.
+ return true;
+ }
+ CHECK(pid_string.length() > 1);
+ // Remove the trailing \n.
+ pid_string.resize(pid_string.length() - 1);
+ pid_t pid;
+ CHECK(base::StringToInt(pid_string, &pid));
+ CHECK_NE(pid, getpid());
+ CHECK_EQ(0, kill(pid, SIGTERM));
+ int pid_file_fd = HANDLE_EINTR(open(pid_file_path_.c_str(), O_WRONLY));
+ if (pid_file_fd < 0) {
+ LOG(ERROR) << "Could not open " << pid_file_path_ << " in write mode: "
+ << safe_strerror(errno);
+ return false;
+ }
+ const FileDescriptorAutoCloser fd_closer(pid_file_fd);
+ // Wait until the daemon exits. Rely on the fact that the daemon releases the
+ // lock on the PID file when it exits.
+ // TODO(pliard): Consider using a mutex + condition in shared memory to avoid
+ // polling.
+ const int kTries = 20;
+ const int kIdleTimeMS = 100;
+ for (int i = 0; i < kTries; ++i) {
+ struct flock lock_info = {};
+ lock_info.l_type = F_WRLCK;
+ lock_info.l_whence = SEEK_CUR;
+ const int ret = HANDLE_EINTR(fcntl(pid_file_fd, F_GETLK, &lock_info));
+ if (ret < 0)
+ PError("fcntl");
+ else if (lock_info.l_type == F_UNLCK)
+ return true;
+ else {
+ CHECK_EQ(F_WRLCK /* exclusive lock */, lock_info.l_type);
+ if (lock_info.l_pid != pid) {
+ LOG(WARNING) << "Daemon (pid=" << pid
+ << ") was successfully killed but a new daemon (pid="
+ << lock_info.l_pid << ") seems to be running now.";
+ return true;
+ }
+ }
+ usleep(kIdleTimeMS * 1000);
+ }
+ LOG(ERROR) << "Timed out while killing daemon. "
+ "It might still be tearing down.";
+ return false;
+}
+
+} // namespace forwarder2
« no previous file with comments | « tools/android/forwarder2/daemon.h ('k') | tools/android/forwarder2/device_controller.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698