Merge "init: Introduce class InterprocessFifo"
This commit is contained in:
commit
5e524e28ee
|
@ -39,6 +39,7 @@ init_common_sources = [
|
||||||
"epoll.cpp",
|
"epoll.cpp",
|
||||||
"import_parser.cpp",
|
"import_parser.cpp",
|
||||||
"interface_utils.cpp",
|
"interface_utils.cpp",
|
||||||
|
"interprocess_fifo.cpp",
|
||||||
"keychords.cpp",
|
"keychords.cpp",
|
||||||
"parser.cpp",
|
"parser.cpp",
|
||||||
"property_type.cpp",
|
"property_type.cpp",
|
||||||
|
@ -467,6 +468,7 @@ cc_test {
|
||||||
"epoll_test.cpp",
|
"epoll_test.cpp",
|
||||||
"firmware_handler_test.cpp",
|
"firmware_handler_test.cpp",
|
||||||
"init_test.cpp",
|
"init_test.cpp",
|
||||||
|
"interprocess_fifo_test.cpp",
|
||||||
"keychords_test.cpp",
|
"keychords_test.cpp",
|
||||||
"oneshot_on_test.cpp",
|
"oneshot_on_test.cpp",
|
||||||
"persistent_properties_test.cpp",
|
"persistent_properties_test.cpp",
|
||||||
|
@ -481,7 +483,10 @@ cc_test {
|
||||||
"ueventd_test.cpp",
|
"ueventd_test.cpp",
|
||||||
"util_test.cpp",
|
"util_test.cpp",
|
||||||
],
|
],
|
||||||
static_libs: ["libinit"],
|
static_libs: [
|
||||||
|
"libgmock",
|
||||||
|
"libinit",
|
||||||
|
],
|
||||||
|
|
||||||
test_suites: [
|
test_suites: [
|
||||||
"cts",
|
"cts",
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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 "interprocess_fifo.h"
|
||||||
|
|
||||||
|
#include <android-base/logging.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
using ::android::base::ErrnoError;
|
||||||
|
using ::android::base::Error;
|
||||||
|
using ::android::base::Result;
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
namespace init {
|
||||||
|
|
||||||
|
InterprocessFifo::InterprocessFifo() noexcept : fds_({-1, -1}) {}
|
||||||
|
|
||||||
|
InterprocessFifo::InterprocessFifo(InterprocessFifo&& orig) noexcept : fds_({-1, -1}) {
|
||||||
|
std::swap(fds_, orig.fds_);
|
||||||
|
}
|
||||||
|
|
||||||
|
InterprocessFifo::~InterprocessFifo() noexcept {
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterprocessFifo::CloseFd(int& fd) noexcept {
|
||||||
|
if (fd >= 0) {
|
||||||
|
close(fd);
|
||||||
|
fd = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterprocessFifo::CloseReadFd() noexcept {
|
||||||
|
CloseFd(fds_[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterprocessFifo::CloseWriteFd() noexcept {
|
||||||
|
CloseFd(fds_[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterprocessFifo::Close() noexcept {
|
||||||
|
CloseReadFd();
|
||||||
|
CloseWriteFd();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> InterprocessFifo::Initialize() noexcept {
|
||||||
|
if (fds_[0] >= 0) {
|
||||||
|
return Error() << "already initialized";
|
||||||
|
}
|
||||||
|
if (pipe(fds_.data()) < 0) { // NOLINT(android-cloexec-pipe)
|
||||||
|
return ErrnoError() << "pipe()";
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<uint8_t> InterprocessFifo::Read() noexcept {
|
||||||
|
uint8_t byte;
|
||||||
|
ssize_t count = read(fds_[0], &byte, 1);
|
||||||
|
if (count < 0) {
|
||||||
|
return ErrnoError() << "read()";
|
||||||
|
}
|
||||||
|
if (count == 0) {
|
||||||
|
return Error() << "read() EOF";
|
||||||
|
}
|
||||||
|
DCHECK_EQ(count, 1);
|
||||||
|
return byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> InterprocessFifo::Write(uint8_t byte) noexcept {
|
||||||
|
ssize_t written = write(fds_[1], &byte, 1);
|
||||||
|
if (written < 0) {
|
||||||
|
return ErrnoError() << "write()";
|
||||||
|
}
|
||||||
|
if (written == 0) {
|
||||||
|
return Error() << "write() EOF";
|
||||||
|
}
|
||||||
|
DCHECK_EQ(written, 1);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace init
|
||||||
|
} // namespace android
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#include <android-base/result.h>
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
namespace init {
|
||||||
|
|
||||||
|
// A FIFO for inter-process communication that uses a Unix pipe internally.
|
||||||
|
class InterprocessFifo {
|
||||||
|
public:
|
||||||
|
template <typename T>
|
||||||
|
using Result = ::android::base::Result<T>;
|
||||||
|
|
||||||
|
InterprocessFifo() noexcept;
|
||||||
|
InterprocessFifo(const InterprocessFifo& orig) noexcept = delete;
|
||||||
|
InterprocessFifo(InterprocessFifo&& orig) noexcept;
|
||||||
|
InterprocessFifo& operator=(const InterprocessFifo& orig) noexcept = delete;
|
||||||
|
InterprocessFifo& operator=(InterprocessFifo&& orig) noexcept = delete;
|
||||||
|
~InterprocessFifo() noexcept;
|
||||||
|
void CloseReadFd() noexcept;
|
||||||
|
void CloseWriteFd() noexcept;
|
||||||
|
void Close() noexcept;
|
||||||
|
Result<void> Initialize() noexcept;
|
||||||
|
Result<void> Write(uint8_t byte) noexcept;
|
||||||
|
Result<uint8_t> Read() noexcept;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void CloseFd(int& fd) noexcept;
|
||||||
|
|
||||||
|
std::array<int, 2> fds_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace init
|
||||||
|
} // namespace android
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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 "interprocess_fifo.h"
|
||||||
|
|
||||||
|
#include <android-base/result-gmock.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#define ASSERT_OK(e) ASSERT_THAT(e, Ok())
|
||||||
|
#define ASSERT_NOT_OK(e) ASSERT_THAT(e, Not(Ok()))
|
||||||
|
|
||||||
|
using ::android::base::Result;
|
||||||
|
using ::android::base::testing::Ok;
|
||||||
|
using ::testing::Not;
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
namespace init {
|
||||||
|
|
||||||
|
TEST(FifoTest, WriteAndRead) {
|
||||||
|
InterprocessFifo fifo;
|
||||||
|
ASSERT_OK(fifo.Initialize());
|
||||||
|
ASSERT_OK(fifo.Write('a'));
|
||||||
|
ASSERT_OK(fifo.Write('b'));
|
||||||
|
Result<uint8_t> result = fifo.Read();
|
||||||
|
ASSERT_OK(result);
|
||||||
|
EXPECT_EQ(*result, 'a');
|
||||||
|
result = fifo.Read();
|
||||||
|
ASSERT_OK(result);
|
||||||
|
EXPECT_EQ(*result, 'b');
|
||||||
|
InterprocessFifo fifo2 = std::move(fifo);
|
||||||
|
ASSERT_NOT_OK(fifo.Write('c'));
|
||||||
|
ASSERT_NOT_OK(fifo.Read());
|
||||||
|
ASSERT_OK(fifo2.Write('d'));
|
||||||
|
result = fifo2.Read();
|
||||||
|
ASSERT_OK(result);
|
||||||
|
EXPECT_EQ(*result, 'd');
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace init
|
||||||
|
} // namespace android
|
|
@ -38,6 +38,7 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "interprocess_fifo.h"
|
||||||
#include "lmkd_service.h"
|
#include "lmkd_service.h"
|
||||||
#include "service_list.h"
|
#include "service_list.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
@ -442,14 +443,6 @@ Result<void> Service::ExecStart() {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ClosePipe(const std::array<int, 2>* pipe) {
|
|
||||||
for (const auto fd : *pipe) {
|
|
||||||
if (fd >= 0) {
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void> Service::CheckConsole() {
|
Result<void> Service::CheckConsole() {
|
||||||
if (!(flags_ & SVC_CONSOLE)) {
|
if (!(flags_ & SVC_CONSOLE)) {
|
||||||
return {};
|
return {};
|
||||||
|
@ -514,7 +507,7 @@ void Service::ConfigureMemcg() {
|
||||||
|
|
||||||
// Enters namespaces, sets environment variables, writes PID files and runs the service executable.
|
// Enters namespaces, sets environment variables, writes PID files and runs the service executable.
|
||||||
void Service::RunService(const std::vector<Descriptor>& descriptors,
|
void Service::RunService(const std::vector<Descriptor>& descriptors,
|
||||||
std::unique_ptr<std::array<int, 2>, decltype(&ClosePipe)> pipefd) {
|
InterprocessFifo cgroups_activated) {
|
||||||
if (auto result = EnterNamespaces(namespaces_, name_, mount_namespace_); !result.ok()) {
|
if (auto result = EnterNamespaces(namespaces_, name_, mount_namespace_); !result.ok()) {
|
||||||
LOG(FATAL) << "Service '" << name_ << "' failed to set up namespaces: " << result.error();
|
LOG(FATAL) << "Service '" << name_ << "' failed to set up namespaces: " << result.error();
|
||||||
}
|
}
|
||||||
|
@ -536,12 +529,12 @@ void Service::RunService(const std::vector<Descriptor>& descriptors,
|
||||||
|
|
||||||
// Wait until the cgroups have been created and until the cgroup controllers have been
|
// Wait until the cgroups have been created and until the cgroup controllers have been
|
||||||
// activated.
|
// activated.
|
||||||
char byte = 0;
|
Result<uint8_t> byte = cgroups_activated.Read();
|
||||||
if (read((*pipefd)[0], &byte, 1) < 0) {
|
if (!byte.ok()) {
|
||||||
PLOG(ERROR) << "failed to read from notification channel";
|
LOG(ERROR) << name_ << ": failed to read from notification channel: " << byte.error();
|
||||||
}
|
}
|
||||||
pipefd.reset();
|
cgroups_activated.Close();
|
||||||
if (!byte) {
|
if (!*byte) {
|
||||||
LOG(FATAL) << "Service '" << name_ << "' failed to start due to a fatal error";
|
LOG(FATAL) << "Service '" << name_ << "' failed to start due to a fatal error";
|
||||||
_exit(EXIT_FAILURE);
|
_exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
@ -605,10 +598,10 @@ Result<void> Service::Start() {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<std::array<int, 2>, decltype(&ClosePipe)> pipefd(new std::array<int, 2>{-1, -1},
|
InterprocessFifo cgroups_activated;
|
||||||
ClosePipe);
|
|
||||||
if (pipe(pipefd->data()) < 0) {
|
if (Result<void> result = cgroups_activated.Initialize(); !result.ok()) {
|
||||||
return ErrnoError() << "pipe()";
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Result<void> result = CheckConsole(); !result.ok()) {
|
if (Result<void> result = CheckConsole(); !result.ok()) {
|
||||||
|
@ -667,8 +660,11 @@ Result<void> Service::Start() {
|
||||||
|
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
umask(077);
|
umask(077);
|
||||||
RunService(descriptors, std::move(pipefd));
|
cgroups_activated.CloseWriteFd();
|
||||||
|
RunService(descriptors, std::move(cgroups_activated));
|
||||||
_exit(127);
|
_exit(127);
|
||||||
|
} else {
|
||||||
|
cgroups_activated.CloseReadFd();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pid < 0) {
|
if (pid < 0) {
|
||||||
|
@ -697,8 +693,9 @@ Result<void> Service::Start() {
|
||||||
limit_percent_ != -1 || !limit_property_.empty();
|
limit_percent_ != -1 || !limit_property_.empty();
|
||||||
errno = -createProcessGroup(proc_attr_.uid, pid_, use_memcg);
|
errno = -createProcessGroup(proc_attr_.uid, pid_, use_memcg);
|
||||||
if (errno != 0) {
|
if (errno != 0) {
|
||||||
if (char byte = 0; write((*pipefd)[1], &byte, 1) < 0) {
|
Result<void> result = cgroups_activated.Write(0);
|
||||||
return ErrnoError() << "sending notification failed";
|
if (!result.ok()) {
|
||||||
|
return Error() << "Sending notification failed: " << result.error();
|
||||||
}
|
}
|
||||||
return Error() << "createProcessGroup(" << proc_attr_.uid << ", " << pid_
|
return Error() << "createProcessGroup(" << proc_attr_.uid << ", " << pid_
|
||||||
<< ") failed for service '" << name_ << "'";
|
<< ") failed for service '" << name_ << "'";
|
||||||
|
@ -720,8 +717,8 @@ Result<void> Service::Start() {
|
||||||
LmkdRegister(name_, proc_attr_.uid, pid_, oom_score_adjust_);
|
LmkdRegister(name_, proc_attr_.uid, pid_, oom_score_adjust_);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (char byte = 1; write((*pipefd)[1], &byte, 1) < 0) {
|
if (Result<void> result = cgroups_activated.Write(1); !result.ok()) {
|
||||||
return ErrnoError() << "sending notification failed";
|
return Error() << "Sending cgroups activated notification failed: " << result.error();
|
||||||
}
|
}
|
||||||
|
|
||||||
NotifyStateChange("running");
|
NotifyStateChange("running");
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
|
|
||||||
#include "action.h"
|
#include "action.h"
|
||||||
#include "capabilities.h"
|
#include "capabilities.h"
|
||||||
|
#include "interprocess_fifo.h"
|
||||||
#include "keyword_map.h"
|
#include "keyword_map.h"
|
||||||
#include "mount_namespace.h"
|
#include "mount_namespace.h"
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
|
@ -154,9 +155,7 @@ class Service {
|
||||||
void ResetFlagsForStart();
|
void ResetFlagsForStart();
|
||||||
Result<void> CheckConsole();
|
Result<void> CheckConsole();
|
||||||
void ConfigureMemcg();
|
void ConfigureMemcg();
|
||||||
void RunService(
|
void RunService(const std::vector<Descriptor>& descriptors, InterprocessFifo cgroups_activated);
|
||||||
const std::vector<Descriptor>& descriptors,
|
|
||||||
std::unique_ptr<std::array<int, 2>, void (*)(const std::array<int, 2>* pipe)> pipefd);
|
|
||||||
void SetMountNamespace();
|
void SetMountNamespace();
|
||||||
static unsigned long next_start_order_;
|
static unsigned long next_start_order_;
|
||||||
static bool is_exec_service_running_;
|
static bool is_exec_service_running_;
|
||||||
|
|
Loading…
Reference in New Issue