| Index: tools/android/forwarder2/daemon.cc
|
| diff --git a/tools/android/forwarder2/daemon.cc b/tools/android/forwarder2/daemon.cc
|
| index 2928f8f627f7c778c08857a09def4e6442bf76f3..5bdf936aa9cd2085f62edf16b5fa6e0280297e9f 100644
|
| --- a/tools/android/forwarder2/daemon.cc
|
| +++ b/tools/android/forwarder2/daemon.cc
|
| @@ -7,29 +7,32 @@
|
| #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 <cstdlib>
|
| +#include <cstring>
|
| #include <string>
|
|
|
| #include "base/basictypes.h"
|
| #include "base/file_path.h"
|
| #include "base/file_util.h"
|
| #include "base/logging.h"
|
| +#include "base/memory/scoped_ptr.h"
|
| #include "base/posix/eintr_wrapper.h"
|
| #include "base/safe_strerror_posix.h"
|
| #include "base/string_number_conversions.h"
|
| #include "base/stringprintf.h"
|
| #include "tools/android/forwarder2/common.h"
|
| +#include "tools/android/forwarder2/socket.h"
|
|
|
| namespace forwarder2 {
|
| namespace {
|
|
|
| -const char kLogFilePath[] = "/tmp/host_forwarder_log";
|
| +const int kBufferSize = 256;
|
|
|
| class FileDescriptorAutoCloser {
|
| public:
|
| @@ -54,71 +57,44 @@ class FileDescriptorAutoCloser {
|
| 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);
|
| - struct flock lock_info = {};
|
| - lock_info.l_type = F_WRLCK;
|
| - lock_info.l_whence = SEEK_CUR;
|
| - if (HANDLE_EINTR(fcntl(pid_file_fd, F_SETLK, &lock_info)) < 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) {
|
| +void InitLoggingForDaemon(const std::string& log_file) {
|
| CHECK(
|
| logging::InitLogging(
|
| - log_file,
|
| - logging::LOG_ONLY_TO_FILE,
|
| - logging::DONT_LOCK_LOG_FILE,
|
| - logging::APPEND_TO_OLD_LOG_FILE,
|
| + log_file.c_str(),
|
| + log_file.empty() ?
|
| + logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG : logging::LOG_ONLY_TO_FILE,
|
| + logging::DONT_LOCK_LOG_FILE, logging::APPEND_TO_OLD_LOG_FILE,
|
| logging::ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS));
|
| }
|
|
|
| +bool RunServerAcceptLoop(const std::string& welcome_message,
|
| + Socket* server_socket,
|
| + Daemon::ServerDelegate* server_delegate) {
|
| + bool failed = false;
|
| + for (;;) {
|
| + scoped_ptr<Socket> client_socket(new Socket());
|
| + if (!server_socket->Accept(client_socket.get())) {
|
| + if (server_socket->exited())
|
| + break;
|
| + PError("Accept()");
|
| + failed = true;
|
| + break;
|
| + }
|
| + if (!client_socket->Write(welcome_message.c_str(),
|
| + welcome_message.length() + 1)) {
|
| + PError("Write()");
|
| + failed = true;
|
| + continue;
|
| + }
|
| + server_delegate->OnClientConnected(client_socket.Pass());
|
| + }
|
| + server_delegate->OnServerExited();
|
| + return !failed;
|
| +}
|
| +
|
| void SigChildHandler(int signal_number) {
|
| DCHECK_EQ(signal_number, SIGCHLD);
|
| + SIGNAL_SAFE_LOG(ERROR, "Caught unexpected SIGCHLD");
|
| // The daemon should not terminate while its parent is still running.
|
| int status;
|
| pid_t child_pid = waitpid(-1 /* any child */, &status, WNOHANG);
|
| @@ -166,39 +142,181 @@ bool GetFileLockOwnerPid(int fd, pid_t* lock_owner_pid) {
|
| return true;
|
| }
|
|
|
| +scoped_ptr<Socket> ConnectToUnixDomainSocket(
|
| + const std::string& socket_name,
|
| + int tries_count,
|
| + int idle_time_msec,
|
| + const std::string& expected_welcome_message) {
|
| + for (int i = 0; i < tries_count; ++i) {
|
| + scoped_ptr<Socket> socket(new Socket());
|
| + if (!socket->ConnectUnix(socket_name, true)) {
|
| + if (idle_time_msec)
|
| + usleep(idle_time_msec * 1000);
|
| + continue;
|
| + }
|
| + char buf[kBufferSize];
|
| + DCHECK(expected_welcome_message.length() + 1 <= sizeof(buf));
|
| + memset(buf, 0, sizeof(buf));
|
| + if (socket->Read(buf, sizeof(buf)) < 0) {
|
| + perror("read");
|
| + continue;
|
| + }
|
| + if (expected_welcome_message != buf) {
|
| + LOG(ERROR) << "Unexpected message read from daemon: " << buf;
|
| + break;
|
| + }
|
| + return socket.Pass();
|
| + }
|
| + return scoped_ptr<Socket>(NULL);
|
| +}
|
| +
|
| } // namespace
|
|
|
| -Daemon::Daemon(const std::string& pid_file_path)
|
| - : pid_file_path_(pid_file_path) {
|
| +// Handles creation and destruction of the PID file.
|
| +class Daemon::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);
|
| + struct flock lock_info = {};
|
| + lock_info.l_type = F_WRLCK;
|
| + lock_info.l_whence = SEEK_CUR;
|
| + if (HANDLE_EINTR(fcntl(pid_file_fd, F_SETLK, &lock_info)) < 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);
|
| +};
|
| +
|
| +Daemon::Daemon(const std::string& log_file_path,
|
| + const std::string& pid_file_path,
|
| + const std::string& identifier,
|
| + ClientDelegate* client_delegate,
|
| + ServerDelegate* server_delegate,
|
| + GetExitNotifierFDCallback get_exit_fd_callback)
|
| + : log_file_path_(log_file_path),
|
| + pid_file_path_(pid_file_path),
|
| + identifier_(identifier),
|
| + client_delegate_(client_delegate),
|
| + server_delegate_(server_delegate),
|
| + get_exit_fd_callback_(get_exit_fd_callback) {
|
| + DCHECK(client_delegate_);
|
| + DCHECK(server_delegate_);
|
| + DCHECK(get_exit_fd_callback_);
|
| }
|
|
|
| -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");
|
| +Daemon::~Daemon() {}
|
| +
|
| +bool Daemon::SpawnIfNeeded() {
|
| + const int kSingleTry = 1;
|
| + const int kNoIdleTime = 0;
|
| + scoped_ptr<Socket> client_socket = ConnectToUnixDomainSocket(
|
| + identifier_, kSingleTry, kNoIdleTime, identifier_);
|
| + if (!client_socket) {
|
| + switch (fork()) {
|
| + case -1:
|
| + PError("fork()");
|
| return false;
|
| + // Child.
|
| + case 0: {
|
| + DCHECK(!pid_file_);
|
| + pid_file_ = PIDFile::Create(pid_file_path_);
|
| + if (!pid_file_)
|
| + exit(1);
|
| + if (setsid() < 0) { // Detach the child process from its parent.
|
| + PError("setsid()");
|
| + exit(1);
|
| + }
|
| + InitLoggingForDaemon(log_file_path_);
|
| + CloseFD(STDIN_FILENO);
|
| + CloseFD(STDOUT_FILENO);
|
| + CloseFD(STDERR_FILENO);
|
| + const int null_fd = open("/dev/null", O_RDWR);
|
| + CHECK_EQ(null_fd, STDIN_FILENO);
|
| + CHECK_EQ(dup(null_fd), STDOUT_FILENO);
|
| + CHECK_EQ(dup(null_fd), STDERR_FILENO);
|
| + Socket command_socket;
|
| + if (!command_socket.BindUnix(identifier_, true)) {
|
| + PError("bind()");
|
| + exit(1);
|
| + }
|
| + server_delegate_->Init();
|
| + command_socket.set_exit_notifier_fd(get_exit_fd_callback_());
|
| + exit(!RunServerAcceptLoop(identifier_, &command_socket,
|
| + server_delegate_));
|
| }
|
| - CloseFD(STDOUT_FILENO);
|
| - CloseFD(STDERR_FILENO);
|
| - InitLogging(kLogFilePath);
|
| - break;
|
| + default:
|
| + break;
|
| }
|
| - default: // Parent.
|
| - *is_daemon = false;
|
| - signal(SIGCHLD, SigChildHandler);
|
| }
|
| - return true;
|
| + // Parent.
|
| + // Install the custom SIGCHLD handler.
|
| + sigset_t blocked_signals_set;
|
| + if (sigprocmask(0 /* first arg ignored */, NULL, &blocked_signals_set) < 0) {
|
| + PError("sigprocmask()");
|
| + return false;
|
| + }
|
| + struct sigaction old_action;
|
| + struct sigaction new_action;
|
| + memset(&new_action, 0, sizeof(new_action));
|
| + new_action.sa_handler = SigChildHandler;
|
| + new_action.sa_flags = SA_NOCLDSTOP;
|
| + sigemptyset(&new_action.sa_mask);
|
| + if (sigaction(SIGCHLD, &new_action, &old_action) < 0) {
|
| + PError("sigaction()");
|
| + return false;
|
| + }
|
| + // Connect to the daemon's Unix Domain Socket.
|
| + bool failed = false;
|
| + if (!client_socket) {
|
| + const int kConnectTries = 20;
|
| + const int kConnectIdleTimeMSec = 10;
|
| + client_socket = ConnectToUnixDomainSocket(
|
| + identifier_, kConnectTries, kConnectIdleTimeMSec, identifier_);
|
| + if (!client_socket) {
|
| + LOG(ERROR) << "Could not connect to daemon's Unix Daemon socket";
|
| + failed = true;
|
| + }
|
| + }
|
| + if (!failed)
|
| + client_delegate_->OnDaemonReady(client_socket.get());
|
| + // Restore the previous signal action for SIGCHLD.
|
| + if (sigaction(SIGCHLD, &old_action, NULL) < 0) {
|
| + PError("sigaction");
|
| + failed = true;
|
| + }
|
| + return !failed;
|
| }
|
|
|
| bool Daemon::Kill() {
|
|
|