blob: b8ea75e6f51615f1d38e1129b09f78319370af6e [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 <cstring>
#include <vector>
#include <arpa/inet.h> // for inet_ntop and inet_pton
#include <netdb.h> // for getaddrinfo
#include <base/files/file_util.h>
#include <base/files/scoped_file.h>
#include <base/files/scoped_temp_dir.h>
#include <base/logging.h>
#include <base/posix/eintr_wrapper.h>
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
using base::FilePath;
namespace vpn_manager {
ServiceManager::ServiceManager(const std::string& service_name,
const base::FilePath& temp_path)
: is_running_(false),
was_stopped_(false),
inner_service_(nullptr),
outer_service_(nullptr),
service_name_(service_name),
error_(kServiceErrorNoError),
temp_path_(temp_path) {}
void ServiceManager::OnStarted() {
CHECK(!is_running_ && !was_stopped_);
CHECK(outer_service_ == nullptr || outer_service_->is_running_);
is_running_ = true;
if (inner_service_ == nullptr)
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_ == nullptr || !inner_service_->is_running_);
is_running_ = false;
was_stopped_ = true;
if (outer_service_ != nullptr) {
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_ != nullptr) {
ServiceError inner_service_error = inner_service_->GetError();
if (inner_service_error != kServiceErrorNoError)
return inner_service_error;
}
return error_;
}
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", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
if (lines.empty()) {
partial_line->clear();
} else {
*partial_line = lines.back();
lines.pop_back();
}
for (const auto& line : lines) {
LOG(INFO) << prefix << line;
OnSyslogOutput(prefix, line);
}
}
bool ServiceManager::ResolveNameToSockAddr(const std::string& name,
sockaddr_storage* address) {
addrinfo* address_info;
int s = getaddrinfo(name.c_str(), nullptr, nullptr, &address_info);
if (s != 0) {
LOG(ERROR) << "getaddrinfo failed: " << gai_strerror(s);
return false;
}
memcpy(address, address_info->ai_addr, address_info->ai_addrlen);
freeaddrinfo(address_info);
return true;
}
bool ServiceManager::ConvertIPStringToSockAddr(const std::string& address_text,
sockaddr_storage* address) {
addrinfo hint_info = {};
addrinfo* address_info;
hint_info.ai_flags = AI_NUMERICHOST;
int s = getaddrinfo(address_text.c_str(), nullptr, &hint_info, &address_info);
if (s != 0) {
LOG(ERROR) << "getaddrinfo failed: " << gai_strerror(s);
return false;
}
memcpy(address, address_info->ai_addr, address_info->ai_addrlen);
freeaddrinfo(address_info);
return true;
}
bool ServiceManager::ConvertSockAddrToIPString(const sockaddr_storage& address,
std::string* address_text) {
char str[INET6_ADDRSTRLEN] = {0};
switch (address.ss_family) {
case AF_INET:
if (!inet_ntop(
AF_INET,
&(reinterpret_cast<const sockaddr_in*>(&address)->sin_addr), str,
sizeof(str))) {
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, sizeof(str))) {
PLOG(ERROR) << "inet_ntop failed";
return false;
}
break;
default:
LOG(ERROR) << "Unknown address family: " << address.ss_family;
return false;
}
*address_text = str;
return true;
}
bool ServiceManager::GetLocalAddressFromRemote(
const sockaddr_storage& remote_address, sockaddr_storage* local_address) {
base::ScopedFD socket_fd(socket(remote_address.ss_family, SOCK_DGRAM, 0));
if (!socket_fd.is_valid()) {
PLOG(ERROR) << "Unable to create socket";
return false;
}
socklen_t addr_len = sizeof(sockaddr_storage);
if (HANDLE_EINTR(connect(socket_fd.get(),
reinterpret_cast<const sockaddr*>(&remote_address),
addr_len)) != 0) {
PLOG(ERROR) << "Unable to connect";
return false;
}
if (getsockname(socket_fd.get(), reinterpret_cast<sockaddr*>(local_address),
&addr_len) != 0) {
PLOG(ERROR) << "getsockname failed on socket";
return false;
}
return true;
}
} // namespace vpn_manager