diff --git a/init/Android.bp b/init/Android.bp index ee339dd85..54442e340 100644 --- a/init/Android.bp +++ b/init/Android.bp @@ -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", diff --git a/init/README.md b/init/README.md index 8179bffb3..2de76a9ec 100644 --- a/init/README.md +++ b/init/README.md @@ -300,7 +300,8 @@ runs the service. `socket [ [ [ ] ] ]` > 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. diff --git a/init/descriptors.cpp b/init/descriptors.cpp deleted file mode 100644 index 62656872f..000000000 --- a/init/descriptors.cpp +++ /dev/null @@ -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 -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#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 diff --git a/init/descriptors.h b/init/descriptors.h deleted file mode 100644 index 3bdddfe5e..000000000 --- a/init/descriptors.h +++ /dev/null @@ -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 - -#include - -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 diff --git a/init/property_service.cpp b/init/property_service.cpp index 8623c3020..37617505d 100644 --- a/init/property_service.cpp +++ b/init/property_service.cpp @@ -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); diff --git a/init/service.cpp b/init/service.cpp index f95b67582..47f4db94e 100644 --- a/init/service.cpp +++ b/init/service.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -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 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(); diff --git a/init/service.h b/init/service.h index cc35a8d6d..cdf31bbf9 100644 --- a/init/service.h +++ b/init/service.h @@ -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> descriptors_; + std::vector sockets_; + std::vector files_; std::vector> environment_vars_; Action onrestart_; // Commands to execute on restart. diff --git a/init/service_parser.cpp b/init/service_parser.cpp index 88ce364c4..0fbbeb828 100644 --- a/init/service_parser.cpp +++ b/init/service_parser.cpp @@ -17,6 +17,8 @@ #include "service_parser.h" #include +#include +#include #include #include @@ -28,6 +30,7 @@ #include #include "rlimit_parser.h" +#include "service_utils.h" #include "util.h" #if defined(__ANDROID__) @@ -344,64 +347,98 @@ Result ServiceParser::ParseTimeoutPeriod(std::vector&& args) return {}; } -template -Result ServiceParser::AddDescriptor(std::vector&& args) { - int perm = args.size() > 3 ? std::strtoul(args[3].c_str(), 0, 8) : -1; - Result uid = 0; - Result gid = 0; - std::string context = args.size() > 6 ? args[6] : ""; +// name type perm [ uid gid context ] +Result ServiceParser::ParseSocket(std::vector&& 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(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 ServiceParser::ParseSocket(std::vector&& 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(std::move(args)); -} - -// name type perm [ uid gid context ] +// name type Result ServiceParser::ParseFile(std::vector&& 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(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 ServiceParser::ParseUser(std::vector&& args) { diff --git a/init/service_parser.h b/init/service_parser.h index 5ad26ef6c..bca07390b 100644 --- a/init/service_parser.h +++ b/init/service_parser.h @@ -81,9 +81,6 @@ class ServiceParser : public SectionParser { Result ParseWritepid(std::vector&& args); Result ParseUpdatable(std::vector&& args); - template - Result AddDescriptor(std::vector&& args); - bool IsValidName(const std::string& name) const; ServiceList* service_list_; diff --git a/init/service_utils.cpp b/init/service_utils.cpp index 34aa8370f..836145d62 100644 --- a/init/service_utils.cpp +++ b/init/service_utils.cpp @@ -27,9 +27,12 @@ #include #include #include +#include +#include #include #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 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 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 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) { diff --git a/init/service_utils.h b/init/service_utils.h index 365cb298b..befce253a 100644 --- a/init/service_utils.h +++ b/init/service_utils.h @@ -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 CreateAndPublish(const std::string& global_context) const; +}; + +struct FileDescriptor { + std::string name; + std::string type; + + Result CreateAndPublish() const; +}; + struct NamespaceInfo { int flags; // Pair of namespace type, path to name. diff --git a/init/util.cpp b/init/util.cpp index 058a1119f..8bfb75598 100644 --- a/init/util.cpp +++ b/init/util.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -77,32 +78,28 @@ Result DecodeUid(const std::string& name) { * daemon. We communicate the file descriptor's value via the environment * variable ANDROID_SOCKET_ENV_PREFIX ("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 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 ReadFile(const std::string& path) { diff --git a/init/util.h b/init/util.h index 1929cb5b1..6a12fb6f1 100644 --- a/init/util.h +++ b/init/util.h @@ -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 CreateSocket(const std::string& name, int type, bool passcred, mode_t perm, uid_t uid, + gid_t gid, const std::string& socketcon); Result ReadFile(const std::string& path); Result WriteFile(const std::string& path, const std::string& content);