blob: 5c3fc3beff89df80898f6eac496ef202ffbc4441 [file] [log] [blame]
// Copyright (c) 2012 The Chromium OS 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 "vpn-manager/service_manager.h"
#include <arpa/inet.h> // for inet_ntop and inet_pton
#include <netdb.h> // for getaddrinfo
#include "base/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
#include "base/string_split.h"
#include "base/string_util.h"
namespace vpn_manager {
const base::FilePath *ServiceManager::temp_path_ = NULL;
const char ServiceManager::kDefaultTempBasePath[] = "/var/run/l2tpipsec_vpn";
const char ServiceManager::kPersistentSubdir[] = "current";
const char *ServiceManager::temp_base_path_ =
ServiceManager::kDefaultTempBasePath;
ServiceManager::ServiceManager(const std::string& service_name)
: is_running_(false),
was_stopped_(false),
debug_(false),
inner_service_(NULL),
outer_service_(NULL),
service_name_(service_name),
error_(kServiceErrorNoError) {
}
ServiceManager::~ServiceManager() {
}
void ServiceManager::OnStarted() {
CHECK(!is_running_ && !was_stopped_);
CHECK(outer_service_ == NULL || outer_service_->is_running_);
is_running_ = true;
if (inner_service_ == NULL)
return;
DLOG(INFO) << "Starting inner " << inner_service_->service_name();
if (!inner_service_->Start()) {
// Inner service could not be started, stop this layer.
LOG(ERROR) << "Inner service " << inner_service_->service_name()
<< " failed. Stopping " << service_name();
Stop();
}
}
void ServiceManager::OnStopped(bool was_error) {
CHECK(inner_service_ == NULL || !inner_service_->is_running_);
is_running_ = false;
was_stopped_ = true;
if (outer_service_ != NULL) {
outer_service_->Stop();
}
}
void ServiceManager::OnSyslogOutput(const std::string& prefix,
const std::string& line) {
}
void ServiceManager::RegisterError(ServiceError error) {
// Register a given error only if it has a higher value than the one
// currently registered as error identifiers are ordered from less
// specific to more specific.
if (error_ < error)
error_ = error;
}
ServiceError ServiceManager::GetError() const {
if (inner_service_ != NULL) {
ServiceError inner_service_error = inner_service_->GetError();
if (inner_service_error != kServiceErrorNoError)
return inner_service_error;
}
return error_;
}
void ServiceManager::InitializeDirectories(
base::ScopedTempDir* scoped_temp_path) {
bool success =
scoped_temp_path->CreateUniqueTempDirUnderPath(FilePath(temp_base_path_));
temp_path_ = &scoped_temp_path->path();
PLOG_IF(INFO, !success) << "Could not create temporary directory. ";
LOG_IF(INFO, success) << "Using temporary directory " << temp_path_->value();
}
void ServiceManager::WriteFdToSyslog(int fd,
const std::string& prefix,
std::string* partial_line) {
char buffer[256];
ssize_t written = HANDLE_EINTR(read(fd, &buffer, sizeof(buffer) - 1));
if (written < 0) {
PLOG(WARNING) << "Error condition on " << prefix << " pipe";
return;
}
buffer[written] = '\0';
partial_line->append(buffer);
std::vector<std::string> lines;
base::SplitString(*partial_line, '\n', &lines);
if (lines.empty()) {
partial_line->clear();
} else {
*partial_line = lines.back();
lines.pop_back();
}
for (size_t i = 0; i < lines.size(); ++i) {
LOG(INFO) << prefix << lines[i];
OnSyslogOutput(prefix, lines[i]);
}
}
bool ServiceManager::ResolveNameToSockAddr(const std::string& name,
struct sockaddr* address) {
struct addrinfo *address_info;
int s = getaddrinfo(name.c_str(), NULL, NULL, &address_info);
if (s != 0) {
LOG(ERROR) << "getaddrinfo failed: " << gai_strerror(s);
return false;
}
*address = *address_info->ai_addr;
freeaddrinfo(address_info);
return true;
}
bool ServiceManager::ConvertIPStringToSockAddr(
const std::string& address_text,
struct sockaddr* address) {
struct addrinfo hint_info = {};
struct addrinfo *address_info;
hint_info.ai_flags = AI_NUMERICHOST;
int s = getaddrinfo(address_text.c_str(), NULL, &hint_info, &address_info);
if (s != 0) {
LOG(ERROR) << "getaddrinfo failed: " << gai_strerror(s);
return false;
}
*address = *address_info->ai_addr;
freeaddrinfo(address_info);
return true;
}
bool ServiceManager::ConvertSockAddrToIPString(
const struct sockaddr& address,
std::string* address_text) {
char str[INET6_ADDRSTRLEN] = { 0 };
switch (address.sa_family) {
case AF_INET:
if (!inet_ntop(AF_INET, &reinterpret_cast<const sockaddr_in*>(
&address)->sin_addr, str, INET6_ADDRSTRLEN)) {
PLOG(ERROR) << "inet_ntop failed";
return false;
}
break;
case AF_INET6:
if (!inet_ntop(AF_INET6, &reinterpret_cast<const sockaddr_in6*>(
&address)->sin6_addr, str, INET6_ADDRSTRLEN)) {
PLOG(ERROR) << "inet_ntop failed";
return false;
}
break;
default:
LOG(ERROR) << "Unknown address family";
return false;
}
*address_text = str;
return true;
}
bool ServiceManager::GetLocalAddressFromRemote(
const struct sockaddr& remote_address,
struct sockaddr* local_address) {
int sock = HANDLE_EINTR(socket(AF_INET, SOCK_DGRAM, 0));
if (sock < 0) {
LOG(ERROR) << "Unable to create socket";
return false;
}
if (HANDLE_EINTR(connect(sock, &remote_address,
sizeof(struct sockaddr))) != 0) {
LOG(ERROR) << "Unable to connect";
HANDLE_EINTR(close(sock));
return false;
}
bool result = false;
socklen_t addr_len = sizeof(*local_address);
if (getsockname(sock, local_address, &addr_len) != 0) {
PLOG(ERROR) << "getsockname failed on socket";
goto error_label;
}
result = true;
error_label:
HANDLE_EINTR(close(sock));
return result;
}
// static
FilePath ServiceManager::GetRootPersistentPath() {
return FilePath(temp_base_path_).Append(kPersistentSubdir);
}
} // namespace vpn_manager