Merge "init: clean up file / socket descriptor creation"

This commit is contained in:
Tom Cherry 2019-07-15 23:20:13 +00:00 committed by Gerrit Code Review
commit c8620ddafe
13 changed files with 184 additions and 292 deletions

View File

@ -107,7 +107,6 @@ cc_library_static {
"bootchart.cpp",
"builtins.cpp",
"capabilities.cpp",
"descriptors.cpp",
"devices.cpp",
"epoll.cpp",
"firmware_handler.cpp",
@ -254,7 +253,6 @@ cc_binary {
"action_manager.cpp",
"action_parser.cpp",
"capabilities.cpp",
"descriptors.cpp",
"epoll.cpp",
"keychords.cpp",
"import_parser.cpp",

View File

@ -300,7 +300,8 @@ runs the service.
`socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]`
> Create a UNIX domain socket named /dev/socket/_name_ and pass its fd to the
launched process. _type_ must be "dgram", "stream" or "seqpacket". User and
launched process. _type_ must be "dgram", "stream" or "seqpacket". _type_
may end with "+passcred" to enable SO_PASSCRED on the socket. User and
group default to 0. 'seclabel' is the SELinux security context for the
socket. It defaults to the service security context, as specified by
seclabel or computed based on the service executable file security context.

View File

@ -1,132 +0,0 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "descriptors.h"
#include <ctype.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <cutils/android_get_control_file.h>
#include <cutils/sockets.h>
#include "util.h"
namespace android {
namespace init {
DescriptorInfo::DescriptorInfo(const std::string& name, const std::string& type, uid_t uid,
gid_t gid, int perm, const std::string& context)
: name_(name), type_(type), uid_(uid), gid_(gid), perm_(perm), context_(context) {
}
DescriptorInfo::~DescriptorInfo() {
}
std::ostream& operator<<(std::ostream& os, const DescriptorInfo& info) {
return os << " descriptors " << info.name_ << " " << info.type_ << " " << std::oct << info.perm_;
}
bool DescriptorInfo::operator==(const DescriptorInfo& other) const {
return name_ == other.name_ && type_ == other.type_ && key() == other.key();
}
void DescriptorInfo::CreateAndPublish(const std::string& globalContext) const {
// Create
const std::string& contextStr = context_.empty() ? globalContext : context_;
int fd = Create(contextStr);
if (fd < 0) return;
// Publish
std::string publishedName = key() + name_;
std::for_each(publishedName.begin(), publishedName.end(),
[] (char& c) { c = isalnum(c) ? c : '_'; });
std::string val = std::to_string(fd);
setenv(publishedName.c_str(), val.c_str(), 1);
// make sure we don't close on exec
fcntl(fd, F_SETFD, 0);
}
void DescriptorInfo::Clean() const {
}
SocketInfo::SocketInfo(const std::string& name, const std::string& type, uid_t uid,
gid_t gid, int perm, const std::string& context)
: DescriptorInfo(name, type, uid, gid, perm, context) {
}
void SocketInfo::Clean() const {
std::string path = android::base::StringPrintf("%s/%s", ANDROID_SOCKET_DIR, name().c_str());
unlink(path.c_str());
}
int SocketInfo::Create(const std::string& context) const {
auto types = android::base::Split(type(), "+");
int flags =
((types[0] == "stream" ? SOCK_STREAM : (types[0] == "dgram" ? SOCK_DGRAM : SOCK_SEQPACKET)));
bool passcred = types.size() > 1 && types[1] == "passcred";
return CreateSocket(name().c_str(), flags, passcred, perm(), uid(), gid(), context.c_str());
}
const std::string SocketInfo::key() const {
return ANDROID_SOCKET_ENV_PREFIX;
}
FileInfo::FileInfo(const std::string& name, const std::string& type, uid_t uid,
gid_t gid, int perm, const std::string& context)
// defaults OK for uid,..., they are ignored for this class.
: DescriptorInfo(name, type, uid, gid, perm, context) {
}
int FileInfo::Create(const std::string&) const {
int flags = (type() == "r") ? O_RDONLY :
(type() == "w") ? O_WRONLY :
O_RDWR;
// Make sure we do not block on open (eg: devices can chose to block on
// carrier detect). Our intention is never to delay launch of a service
// for such a condition. The service can perform its own blocking on
// carrier detect.
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(name().c_str(),
flags | O_NONBLOCK)));
if (fd < 0) {
PLOG(ERROR) << "Failed to open file '" << name().c_str() << "'";
return -1;
}
// Fixup as we set O_NONBLOCK for open, the intent for fd is to block reads.
fcntl(fd, F_SETFL, flags);
LOG(INFO) << "Opened file '" << name().c_str() << "'"
<< ", flags " << std::oct << flags << std::dec;
return fd.release();
}
const std::string FileInfo::key() const {
return ANDROID_FILE_ENV_PREFIX;
}
} // namespace init
} // namespace android

View File

@ -1,84 +0,0 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _INIT_DESCRIPTORS_H
#define _INIT_DESCRIPTORS_H
#include <sys/types.h>
#include <string>
namespace android {
namespace init {
class DescriptorInfo {
public:
DescriptorInfo(const std::string& name, const std::string& type, uid_t uid,
gid_t gid, int perm, const std::string& context);
virtual ~DescriptorInfo();
friend std::ostream& operator<<(std::ostream& os, const class DescriptorInfo& info);
bool operator==(const DescriptorInfo& other) const;
void CreateAndPublish(const std::string& globalContext) const;
virtual void Clean() const;
protected:
const std::string& name() const { return name_; }
const std::string& type() const { return type_; }
uid_t uid() const { return uid_; }
gid_t gid() const { return gid_; }
int perm() const { return perm_; }
const std::string& context() const { return context_; }
private:
std::string name_;
std::string type_;
uid_t uid_;
gid_t gid_;
int perm_;
std::string context_;
virtual int Create(const std::string& globalContext) const = 0;
virtual const std::string key() const = 0;
};
std::ostream& operator<<(std::ostream& os, const DescriptorInfo& info);
class SocketInfo : public DescriptorInfo {
public:
SocketInfo(const std::string& name, const std::string& type, uid_t uid,
gid_t gid, int perm, const std::string& context);
void Clean() const override;
private:
virtual int Create(const std::string& context) const override;
virtual const std::string key() const override;
};
class FileInfo : public DescriptorInfo {
public:
FileInfo(const std::string& name, const std::string& type, uid_t uid,
gid_t gid, int perm, const std::string& context);
private:
virtual int Create(const std::string& context) const override;
virtual const std::string key() const override;
};
} // namespace init
} // namespace android
#endif

View File

@ -994,10 +994,11 @@ void CreateSerializedPropertyInfo() {
void StartPropertyService(Epoll* epoll) {
property_set("ro.property_service.version", "2");
property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
false, 0666, 0, 0, nullptr);
if (property_set_fd == -1) {
PLOG(FATAL) << "start_property_service socket creation failed";
if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
false, 0666, 0, 0, {})) {
property_set_fd = *result;
} else {
PLOG(FATAL) << "start_property_service socket creation failed: " << result.error();
}
listen(property_set_fd, 8);

View File

@ -31,6 +31,7 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <cutils/sockets.h>
#include <processgroup/processgroup.h>
#include <selinux/selinux.h>
@ -227,9 +228,11 @@ void Service::Reap(const siginfo_t& siginfo) {
KillProcessGroup(SIGKILL);
}
// Remove any descriptor resources we may have created.
std::for_each(descriptors_.begin(), descriptors_.end(),
std::bind(&DescriptorInfo::Clean, std::placeholders::_1));
// Remove any socket resources we may have created.
for (const auto& socket : sockets_) {
auto path = ANDROID_SOCKET_DIR "/" + socket.name;
unlink(path.c_str());
}
for (const auto& f : reap_callbacks_) {
f(siginfo);
@ -300,8 +303,12 @@ void Service::DumpState() const {
LOG(INFO) << "service " << name_;
LOG(INFO) << " class '" << Join(classnames_, " ") << "'";
LOG(INFO) << " exec " << Join(args_, " ");
std::for_each(descriptors_.begin(), descriptors_.end(),
[] (const auto& info) { LOG(INFO) << *info; });
for (const auto& socket : sockets_) {
LOG(INFO) << " socket " << socket.name;
}
for (const auto& file : files_) {
LOG(INFO) << " file " << file.name;
}
}
@ -419,8 +426,17 @@ Result<void> Service::Start() {
setenv(key.c_str(), value.c_str(), 1);
}
std::for_each(descriptors_.begin(), descriptors_.end(),
std::bind(&DescriptorInfo::CreateAndPublish, std::placeholders::_1, scon));
for (const auto& socket : sockets_) {
if (auto result = socket.CreateAndPublish(scon); !result) {
LOG(INFO) << "Could not create socket '" << socket.name << "': " << result.error();
}
}
for (const auto& file : files_) {
if (auto result = file.CreateAndPublish(); !result) {
LOG(INFO) << "Could not open file '" << file.name << "': " << result.error();
}
}
if (auto result = WritePidToFiles(&writepid_files_); !result) {
LOG(ERROR) << "failed to write pid to files: " << result.error();

View File

@ -31,7 +31,6 @@
#include "action.h"
#include "capabilities.h"
#include "descriptors.h"
#include "keyword_map.h"
#include "parser.h"
#include "service_utils.h"
@ -151,7 +150,8 @@ class Service {
std::string seclabel_;
std::vector<std::unique_ptr<DescriptorInfo>> descriptors_;
std::vector<SocketDescriptor> sockets_;
std::vector<FileDescriptor> files_;
std::vector<std::pair<std::string, std::string>> environment_vars_;
Action onrestart_; // Commands to execute on restart.

View File

@ -17,6 +17,8 @@
#include "service_parser.h"
#include <linux/input.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <algorithm>
#include <sstream>
@ -28,6 +30,7 @@
#include <system/thread_defs.h>
#include "rlimit_parser.h"
#include "service_utils.h"
#include "util.h"
#if defined(__ANDROID__)
@ -344,64 +347,98 @@ Result<void> ServiceParser::ParseTimeoutPeriod(std::vector<std::string>&& args)
return {};
}
template <typename T>
Result<void> ServiceParser::AddDescriptor(std::vector<std::string>&& args) {
int perm = args.size() > 3 ? std::strtoul(args[3].c_str(), 0, 8) : -1;
Result<uid_t> uid = 0;
Result<gid_t> gid = 0;
std::string context = args.size() > 6 ? args[6] : "";
// name type perm [ uid gid context ]
Result<void> ServiceParser::ParseSocket(std::vector<std::string>&& args) {
SocketDescriptor socket;
socket.name = std::move(args[1]);
auto types = Split(args[2], "+");
if (types[0] == "stream") {
socket.type = SOCK_STREAM;
} else if (types[0] == "dgram") {
socket.type = SOCK_DGRAM;
} else if (types[0] == "seqpacket") {
socket.type = SOCK_SEQPACKET;
} else {
return Error() << "socket type must be 'dgram', 'stream' or 'seqpacket', got '" << types[0]
<< "' instead.";
}
if (types.size() > 1) {
if (types.size() == 2 && types[1] == "passcred") {
socket.passcred = true;
} else {
return Error() << "Only 'passcred' may be used to modify the socket type";
}
}
errno = 0;
char* end = nullptr;
socket.perm = strtol(args[3].c_str(), &end, 8);
if (errno != 0) {
return ErrnoError() << "Unable to parse permissions '" << args[3] << "'";
}
if (end == args[3].c_str() || *end != '\0') {
errno = EINVAL;
return ErrnoError() << "Unable to parse permissions '" << args[3] << "'";
}
if (args.size() > 4) {
uid = DecodeUid(args[4]);
auto uid = DecodeUid(args[4]);
if (!uid) {
return Error() << "Unable to find UID for '" << args[4] << "': " << uid.error();
}
socket.uid = *uid;
}
if (args.size() > 5) {
gid = DecodeUid(args[5]);
auto gid = DecodeUid(args[5]);
if (!gid) {
return Error() << "Unable to find GID for '" << args[5] << "': " << gid.error();
}
socket.gid = *gid;
}
auto descriptor = std::make_unique<T>(args[1], args[2], *uid, *gid, perm, context);
socket.context = args.size() > 6 ? args[6] : "";
auto old = std::find_if(
service_->descriptors_.begin(), service_->descriptors_.end(),
[&descriptor](const auto& other) { return descriptor.get() == other.get(); });
auto old = std::find_if(service_->sockets_.begin(), service_->sockets_.end(),
[&socket](const auto& other) { return socket.name == other.name; });
if (old != service_->descriptors_.end()) {
return Error() << "duplicate descriptor " << args[1] << " " << args[2];
if (old != service_->sockets_.end()) {
return Error() << "duplicate socket descriptor '" << socket.name << "'";
}
service_->descriptors_.emplace_back(std::move(descriptor));
service_->sockets_.emplace_back(std::move(socket));
return {};
}
// name type perm [ uid gid context ]
Result<void> ServiceParser::ParseSocket(std::vector<std::string>&& args) {
if (!StartsWith(args[2], "dgram") && !StartsWith(args[2], "stream") &&
!StartsWith(args[2], "seqpacket")) {
return Error() << "socket type must be 'dgram', 'stream' or 'seqpacket'";
}
return AddDescriptor<SocketInfo>(std::move(args));
}
// name type perm [ uid gid context ]
// name type
Result<void> ServiceParser::ParseFile(std::vector<std::string>&& args) {
if (args[2] != "r" && args[2] != "w" && args[2] != "rw") {
return Error() << "file type must be 'r', 'w' or 'rw'";
}
std::string expanded;
if (!expand_props(args[1], &expanded)) {
FileDescriptor file;
file.type = args[2];
if (!expand_props(args[1], &file.name)) {
return Error() << "Could not expand property in file path '" << args[1] << "'";
}
args[1] = std::move(expanded);
if ((args[1][0] != '/') || (args[1].find("../") != std::string::npos)) {
if (file.name[0] != '/' || file.name.find("../") != std::string::npos) {
return Error() << "file name must not be relative";
}
return AddDescriptor<FileInfo>(std::move(args));
auto old = std::find_if(service_->files_.begin(), service_->files_.end(),
[&file](const auto& other) { return other.name == file.name; });
if (old != service_->files_.end()) {
return Error() << "duplicate file descriptor '" << file.name << "'";
}
service_->files_.emplace_back(std::move(file));
return {};
}
Result<void> ServiceParser::ParseUser(std::vector<std::string>&& args) {

View File

@ -81,9 +81,6 @@ class ServiceParser : public SectionParser {
Result<void> ParseWritepid(std::vector<std::string>&& args);
Result<void> ParseUpdatable(std::vector<std::string>&& args);
template <typename T>
Result<void> AddDescriptor(std::vector<std::string>&& args);
bool IsValidName(const std::string& name) const;
ServiceList* service_list_;

View File

@ -27,9 +27,12 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <cutils/android_get_control_file.h>
#include <cutils/sockets.h>
#include <processgroup/processgroup.h>
#include "mount_namespace.h"
#include "util.h"
using android::base::GetProperty;
using android::base::StartsWith;
@ -135,8 +138,52 @@ void OpenConsole(const std::string& console) {
dup2(fd, 2);
}
void PublishDescriptor(const std::string& key, const std::string& name, int fd) {
std::string published_name = key + name;
for (auto& c : published_name) {
c = isalnum(c) ? c : '_';
}
std::string val = std::to_string(fd);
setenv(published_name.c_str(), val.c_str(), 1);
}
} // namespace
Result<void> SocketDescriptor::CreateAndPublish(const std::string& global_context) const {
const auto& socket_context = context.empty() ? global_context : context;
auto result = CreateSocket(name, type, passcred, perm, uid, gid, socket_context);
if (!result) {
return result.error();
}
PublishDescriptor(ANDROID_SOCKET_ENV_PREFIX, name, *result);
return {};
}
Result<void> FileDescriptor::CreateAndPublish() const {
int flags = (type == "r") ? O_RDONLY : (type == "w") ? O_WRONLY : O_RDWR;
// Make sure we do not block on open (eg: devices can chose to block on carrier detect). Our
// intention is never to delay launch of a service for such a condition. The service can
// perform its own blocking on carrier detect.
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(name.c_str(), flags | O_NONBLOCK)));
if (fd < 0) {
return ErrnoError() << "Failed to open file '" << name << "'";
}
// Fixup as we set O_NONBLOCK for open, the intent for fd is to block reads.
fcntl(fd, F_SETFL, flags);
LOG(INFO) << "Opened file '" << name << "', flags " << flags;
PublishDescriptor(ANDROID_FILE_ENV_PREFIX, name, fd.release());
return {};
}
Result<void> EnterNamespaces(const NamespaceInfo& info, const std::string& name, bool pre_apexd) {
for (const auto& [nstype, path] : info.namespaces_to_enter) {
if (auto result = EnterNamespace(nstype, path.c_str()); !result) {

View File

@ -29,6 +29,25 @@
namespace android {
namespace init {
struct SocketDescriptor {
std::string name;
int type = 0;
uid_t uid = 0;
gid_t gid = 0;
int perm = 0;
std::string context;
bool passcred = false;
Result<void> CreateAndPublish(const std::string& global_context) const;
};
struct FileDescriptor {
std::string name;
std::string type;
Result<void> CreateAndPublish() const;
};
struct NamespaceInfo {
int flags;
// Pair of namespace type, path to name.

View File

@ -34,6 +34,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/scopeguard.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
@ -77,32 +78,28 @@ Result<uid_t> DecodeUid(const std::string& name) {
* daemon. We communicate the file descriptor's value via the environment
* variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo").
*/
int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
const char* socketcon) {
if (socketcon) {
if (setsockcreatecon(socketcon) == -1) {
PLOG(ERROR) << "setsockcreatecon(\"" << socketcon << "\") failed";
return -1;
Result<int> CreateSocket(const std::string& name, int type, bool passcred, mode_t perm, uid_t uid,
gid_t gid, const std::string& socketcon) {
if (!socketcon.empty()) {
if (setsockcreatecon(socketcon.c_str()) == -1) {
return ErrnoError() << "setsockcreatecon(\"" << socketcon << "\") failed";
}
}
android::base::unique_fd fd(socket(PF_UNIX, type, 0));
if (fd < 0) {
PLOG(ERROR) << "Failed to open socket '" << name << "'";
return -1;
return ErrnoError() << "Failed to open socket '" << name << "'";
}
if (socketcon) setsockcreatecon(NULL);
if (!socketcon.empty()) setsockcreatecon(nullptr);
struct sockaddr_un addr;
memset(&addr, 0 , sizeof(addr));
addr.sun_family = AF_UNIX;
snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s",
name);
snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR "/%s", name.c_str());
if ((unlink(addr.sun_path) != 0) && (errno != ENOENT)) {
PLOG(ERROR) << "Failed to unlink old socket '" << name << "'";
return -1;
return ErrnoError() << "Failed to unlink old socket '" << name << "'";
}
std::string secontext;
@ -113,8 +110,7 @@ int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t u
if (passcred) {
int on = 1;
if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) {
PLOG(ERROR) << "Failed to set SO_PASSCRED '" << name << "'";
return -1;
return ErrnoError() << "Failed to set SO_PASSCRED '" << name << "'";
}
}
@ -125,19 +121,18 @@ int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t u
setfscreatecon(nullptr);
}
auto guard = android::base::make_scope_guard([&addr] { unlink(addr.sun_path); });
if (ret) {
errno = savederrno;
PLOG(ERROR) << "Failed to bind socket '" << name << "'";
goto out_unlink;
return ErrnoError() << "Failed to bind socket '" << name << "'";
}
if (lchown(addr.sun_path, uid, gid)) {
PLOG(ERROR) << "Failed to lchown socket '" << addr.sun_path << "'";
goto out_unlink;
return ErrnoError() << "Failed to lchown socket '" << addr.sun_path << "'";
}
if (fchmodat(AT_FDCWD, addr.sun_path, perm, AT_SYMLINK_NOFOLLOW)) {
PLOG(ERROR) << "Failed to fchmodat socket '" << addr.sun_path << "'";
goto out_unlink;
return ErrnoError() << "Failed to fchmodat socket '" << addr.sun_path << "'";
}
LOG(INFO) << "Created socket '" << addr.sun_path << "'"
@ -145,11 +140,8 @@ int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t u
<< ", user " << uid
<< ", group " << gid;
guard.Disable();
return fd.release();
out_unlink:
unlink(addr.sun_path);
return -1;
}
Result<std::string> ReadFile(const std::string& path) {

View File

@ -38,8 +38,8 @@ namespace init {
static const char kColdBootDoneProp[] = "ro.cold_boot_done";
int CreateSocket(const char* name, int type, bool passcred, mode_t perm, uid_t uid, gid_t gid,
const char* socketcon);
Result<int> CreateSocket(const std::string& name, int type, bool passcred, mode_t perm, uid_t uid,
gid_t gid, const std::string& socketcon);
Result<std::string> ReadFile(const std::string& path);
Result<void> WriteFile(const std::string& path, const std::string& content);