Merge changes I316c13e3,I4d99744d,Id9614b72,I7c98a0b7

* changes:
  init: enable error reporting of builtin functions
  init: log Service failures via Result<T>
  init: pass errors from one Result<T> to another better
  init: cleanup environment handling
This commit is contained in:
Tom Cherry 2017-08-23 22:07:30 +00:00 committed by Gerrit Code Review
commit a78b5b300b
11 changed files with 237 additions and 145 deletions

View file

@ -91,14 +91,25 @@ void Action::ExecuteCommand(const Command& command) const {
auto result = command.InvokeFunc();
auto duration = t.duration();
// There are many legacy paths in rootdir/init.rc that will virtually never exist on a new
// device, such as '/sys/class/leds/jogball-backlight/brightness'. As of this writing, there
// are 198 such failures on bullhead. Instead of spamming the log reporting them, we do not
// report such failures unless we're running at the DEBUG log level.
bool report_failure = !result.has_value();
if (report_failure && android::base::GetMinimumLogSeverity() > android::base::DEBUG &&
result.error_errno() == ENOENT) {
report_failure = false;
}
// Any action longer than 50ms will be warned to user as slow operation
if (duration > 50ms || android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
if (report_failure || duration > 50ms ||
android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
std::string trigger_name = BuildTriggersString();
std::string cmd_str = command.BuildCommandString();
LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << " (" << filename_
<< ":" << command.line() << ") took " << duration.count() << "ms and "
<< (result ? "succeeded" : "failed: " + result.error());
<< (result ? "succeeded" : "failed: " + result.error_string());
}
}

View file

@ -127,7 +127,9 @@ static Result<Success> do_enable(const std::vector<std::string>& args) {
Service* svc = ServiceList::GetInstance().FindService(args[1]);
if (!svc) return Error() << "Could not find service";
if (!svc->Enable()) return Error() << "Could not enable service";
if (auto result = svc->Enable(); !result) {
return Error() << "Could not enable service: " << result.error();
}
return Success();
}
@ -137,8 +139,8 @@ static Result<Success> do_exec(const std::vector<std::string>& args) {
if (!service) {
return Error() << "Could not create exec service";
}
if (!service->ExecStart()) {
return Error() << "Could not start exec service";
if (auto result = service->ExecStart(); !result) {
return Error() << "Could not start exec service: " << result.error();
}
ServiceList::GetInstance().AddService(std::move(service));
@ -151,16 +153,16 @@ static Result<Success> do_exec_start(const std::vector<std::string>& args) {
return Error() << "Service not found";
}
if (!service->ExecStart()) {
return Error() << "Could not start Service";
if (auto result = service->ExecStart(); !result) {
return Error() << "Could not start exec service: " << result.error();
}
return Success();
}
static Result<Success> do_export(const std::vector<std::string>& args) {
if (!add_environment(args[1].c_str(), args[2].c_str())) {
return Error();
if (setenv(args[1].c_str(), args[2].c_str(), 1) == -1) {
return ErrnoError() << "setenv() failed";
}
return Success();
}
@ -583,7 +585,9 @@ static Result<Success> do_setrlimit(const std::vector<std::string>& args) {
static Result<Success> do_start(const std::vector<std::string>& args) {
Service* svc = ServiceList::GetInstance().FindService(args[1]);
if (!svc) return Error() << "service " << args[1] << " not found";
if (!svc->Start()) return Error() << "failed to start service";
if (auto result = svc->Start(); !result) {
return Error() << "Could not start service: " << result.error();
}
return Success();
}
@ -608,6 +612,11 @@ static Result<Success> do_trigger(const std::vector<std::string>& args) {
static Result<Success> do_symlink(const std::vector<std::string>& args) {
if (symlink(args[1].c_str(), args[2].c_str()) < 0) {
// The symlink builtin is often used to create symlinks for older devices to be backwards
// compatible with new paths, therefore we skip reporting this error.
if (errno == EEXIST && android::base::GetMinimumLogSeverity() > android::base::DEBUG) {
return Success();
}
return ErrnoError() << "symlink() failed";
}
return Success();

View file

@ -28,7 +28,6 @@
#include <cutils/android_get_control_file.h>
#include <cutils/sockets.h>
#include "init.h"
#include "util.h"
namespace android {
@ -62,7 +61,7 @@ void DescriptorInfo::CreateAndPublish(const std::string& globalContext) const {
[] (char& c) { c = isalnum(c) ? c : '_'; });
std::string val = std::to_string(fd);
add_environment(publishedName.c_str(), val.c_str());
setenv(publishedName.c_str(), val.c_str(), 1);
// make sure we don't close on exec
fcntl(fd, F_SETFD, 0);

View file

@ -71,8 +71,6 @@ static char qemu[32];
std::string default_console = "/dev/console";
const char *ENV[32];
static int epoll_fd = -1;
static std::unique_ptr<Timer> waiting_for_prop(nullptr);
@ -126,38 +124,6 @@ void register_epoll_handler(int fd, void (*fn)()) {
}
}
/* add_environment - add "key=value" to the current environment */
int add_environment(const char *key, const char *val)
{
size_t n;
size_t key_len = strlen(key);
/* The last environment entry is reserved to terminate the list */
for (n = 0; n < (arraysize(ENV) - 1); n++) {
/* Delete any existing entry for this key */
if (ENV[n] != NULL) {
size_t entry_key_len = strcspn(ENV[n], "=");
if ((entry_key_len == key_len) && (strncmp(ENV[n], key, entry_key_len) == 0)) {
free((char*)ENV[n]);
ENV[n] = NULL;
}
}
/* Add entry if a free slot is available */
if (ENV[n] == NULL) {
char* entry;
asprintf(&entry, "%s=%s", key, val);
ENV[n] = entry;
return 0;
}
}
LOG(ERROR) << "No env. room to store: '" << key << "':'" << val << "'";
return -1;
}
bool start_waiting_for_property(const char *name, const char *value)
{
if (waiting_for_prop) {
@ -429,8 +395,6 @@ int main(int argc, char** argv) {
install_reboot_signal_handlers();
}
add_environment("PATH", _PATH_DEFPATH);
bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
if (is_first_stage) {
@ -439,6 +403,8 @@ int main(int argc, char** argv) {
// Clear the umask.
umask(0);
clearenv();
setenv("PATH", _PATH_DEFPATH, 1);
// Get the basic filesystem setup we need put together in the initramdisk
// on / and then we'll let the rc file figure out the rest.
mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");

View file

@ -30,9 +30,7 @@ namespace init {
// Note: These globals are *only* valid in init, so they should not be used in ueventd,
// watchdogd, or any files that may be included in those, such as devices.cpp and util.cpp.
// TODO: Have an Init class and remove all globals.
extern const char *ENV[32];
extern std::string default_console;
extern std::vector<std::string> late_import_paths;
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list);
@ -43,8 +41,6 @@ void property_changed(const std::string& name, const std::string& value);
void register_epoll_handler(int fd, void (*fn)());
int add_environment(const char* key, const char* val);
bool start_waiting_for_property(const char *name, const char *value);
void DumpState();

View file

@ -21,9 +21,13 @@
// There are 3 classes that implement this functionality and one additional helper type.
//
// Result<T> either contains a member of type T that can be accessed using similar semantics as
// std::optional<T> or it contains a std::string describing an error, which can be accessed via
// std::optional<T> or it contains a ResultError describing an error, which can be accessed via
// Result<T>::error().
//
// ResultError is a type that contains both a std::string describing the error and a copy of errno
// from when the error occurred. ResultError can be used in an ostream directly to print its
// string value.
//
// Success is a typedef that aids in creating Result<T> that do not contain a return value.
// Result<Success> is the correct return type for a function that either returns successfully or
// returns an error value. Returning Success() from a function that returns Result<Success> is the
@ -33,10 +37,20 @@
// to T or from the constructor arguments for T. This allows you to return a type T directly from
// a function that returns Result<T>.
//
// Error and ErrnoError are used to construct a Result<T> that has failed. Each of these classes
// take an ostream as an input and are implicitly cast to a Result<T> containing that failure.
// ErrnoError() additionally appends ": " + strerror(errno) to the end of the failure string to aid
// in interacting with C APIs.
// Error and ErrnoError are used to construct a Result<T> that has failed. The Error class takes
// an ostream as an input and are implicitly cast to a Result<T> containing that failure.
// ErrnoError() is a helper function to create an Error class that appends ": " + strerror(errno)
// to the end of the failure string to aid in interacting with C APIs. Alternatively, an errno
// value can be directly specified via the Error() constructor.
//
// ResultError can be used in the ostream when using Error to construct a Result<T>. In this case,
// the string that the ResultError takes is passed through the stream normally, but the errno is
// passed to the Result<T>. This can be used to pass errno from a failing C function up multiple
// callers.
//
// ResultError can also directly construct a Result<T>. This is particularly useful if you have a
// function that return Result<T> but you have a Result<U> and want to return its error. In this
// case, you can return the .error() from the Result<U> to construct the Result<T>.
// An example of how to use these is below:
// Result<U> CalculateResult(const T& input) {
@ -66,9 +80,29 @@
namespace android {
namespace init {
struct ResultError {
template <typename T>
ResultError(T&& error_string, int error_errno)
: error_string(std::forward<T>(error_string)), error_errno(error_errno) {}
std::string error_string;
int error_errno;
};
inline std::ostream& operator<<(std::ostream& os, const ResultError& t) {
os << t.error_string;
return os;
}
inline std::ostream& operator<<(std::ostream& os, ResultError&& t) {
os << std::move(t.error_string);
return os;
}
class Error {
public:
Error() : append_errno_(0) {}
Error() : errno_(0), append_errno_(false) {}
Error(int errno_to_append) : errno_(errno_to_append), append_errno_(true) {}
template <typename T>
Error&& operator<<(T&& t) {
@ -76,30 +110,45 @@ class Error {
return std::move(*this);
}
const std::string str() const {
if (append_errno_) {
return ss_.str() + ": " + strerror(append_errno_);
}
return ss_.str();
Error&& operator<<(const ResultError& result_error) {
ss_ << result_error.error_string;
errno_ = result_error.error_errno;
return std::move(*this);
}
Error&& operator<<(ResultError&& result_error) {
ss_ << std::move(result_error.error_string);
errno_ = result_error.error_errno;
return std::move(*this);
}
const std::string str() const {
std::string str = ss_.str();
if (append_errno_) {
if (str.empty()) {
return strerror(errno_);
}
return str + ": " + strerror(errno_);
}
return str;
}
int get_errno() const { return errno_; }
Error(const Error&) = delete;
Error(Error&&) = delete;
Error& operator=(const Error&) = delete;
Error& operator=(Error&&) = delete;
protected:
Error(int append_errno) : append_errno_(append_errno) {}
private:
std::stringstream ss_;
int append_errno_;
int errno_;
bool append_errno_;
};
class ErrnoError : public Error {
public:
ErrnoError() : Error(errno) {}
};
inline Error ErrnoError() {
return Error(errno);
}
template <typename T>
class Result {
@ -107,7 +156,13 @@ class Result {
template <typename... U>
Result(U&&... result) : contents_(std::in_place_index_t<0>(), std::forward<U>(result)...) {}
Result(Error&& fb) : contents_(std::in_place_index_t<1>(), fb.str()) {}
Result(Error&& error) : contents_(std::in_place_index_t<1>(), error.str(), error.get_errno()) {}
Result(const ResultError& result_error)
: contents_(std::in_place_index_t<1>(), result_error.error_string,
result_error.error_errno) {}
Result(ResultError&& result_error)
: contents_(std::in_place_index_t<1>(), std::move(result_error.error_string),
result_error.error_errno) {}
bool has_value() const { return contents_.index() == 0; }
@ -116,9 +171,17 @@ class Result {
T&& value() && { return std::get<0>(std::move(contents_)); }
const T&& value() const && { return std::get<0>(std::move(contents_)); }
const std::string& error() const & { return std::get<1>(contents_); }
std::string&& error() && { return std::get<1>(std::move(contents_)); }
const std::string&& error() const && { return std::get<1>(std::move(contents_)); }
const ResultError& error() const & { return std::get<1>(contents_); }
ResultError&& error() && { return std::get<1>(std::move(contents_)); }
const ResultError&& error() const && { return std::get<1>(std::move(contents_)); }
const std::string& error_string() const & { return std::get<1>(contents_).error_string; }
std::string&& error_string() && { return std::get<1>(std::move(contents_)).error_string; }
const std::string&& error_string() const && {
return std::get<1>(std::move(contents_)).error_string;
}
int error_errno() const { return std::get<1>(contents_).error_errno; }
explicit operator bool() const { return has_value(); }
@ -131,7 +194,7 @@ class Result {
const T* operator->() const { return &value(); }
private:
std::variant<T, std::string> contents_;
std::variant<T, ResultError> contents_;
};
using Success = std::monostate;

View file

@ -74,7 +74,8 @@ TEST(result, result_error) {
ASSERT_FALSE(result);
ASSERT_FALSE(result.has_value());
EXPECT_EQ("failure1", result.error());
EXPECT_EQ(0, result.error_errno());
EXPECT_EQ("failure1", result.error_string());
}
TEST(result, result_error_empty) {
@ -82,7 +83,8 @@ TEST(result, result_error_empty) {
ASSERT_FALSE(result);
ASSERT_FALSE(result.has_value());
EXPECT_EQ("", result.error());
EXPECT_EQ(0, result.error_errno());
EXPECT_EQ("", result.error_string());
}
TEST(result, result_error_rvalue) {
@ -96,7 +98,8 @@ TEST(result, result_error_rvalue) {
ASSERT_FALSE(MakeRvalueErrorResult());
ASSERT_FALSE(MakeRvalueErrorResult().has_value());
EXPECT_EQ("failure1", MakeRvalueErrorResult().error());
EXPECT_EQ(0, MakeRvalueErrorResult().error_errno());
EXPECT_EQ("failure1", MakeRvalueErrorResult().error_string());
}
TEST(result, result_errno_error) {
@ -107,7 +110,72 @@ TEST(result, result_errno_error) {
ASSERT_FALSE(result);
ASSERT_FALSE(result.has_value());
EXPECT_EQ("failure1: "s + strerror(test_errno), result.error());
EXPECT_EQ(test_errno, result.error_errno());
EXPECT_EQ("failure1: "s + strerror(test_errno), result.error_string());
}
TEST(result, result_errno_error_no_text) {
constexpr int test_errno = 6;
errno = test_errno;
Result<Success> result = ErrnoError();
ASSERT_FALSE(result);
ASSERT_FALSE(result.has_value());
EXPECT_EQ(test_errno, result.error_errno());
EXPECT_EQ(strerror(test_errno), result.error_string());
}
TEST(result, result_error_from_other_result) {
auto error_text = "test error"s;
Result<Success> result = Error() << error_text;
ASSERT_FALSE(result);
ASSERT_FALSE(result.has_value());
Result<std::string> result2 = result.error();
ASSERT_FALSE(result2);
ASSERT_FALSE(result2.has_value());
EXPECT_EQ(0, result.error_errno());
EXPECT_EQ(error_text, result.error_string());
}
TEST(result, result_error_through_ostream) {
auto error_text = "test error"s;
Result<Success> result = Error() << error_text;
ASSERT_FALSE(result);
ASSERT_FALSE(result.has_value());
Result<std::string> result2 = Error() << result.error();
ASSERT_FALSE(result2);
ASSERT_FALSE(result2.has_value());
EXPECT_EQ(0, result.error_errno());
EXPECT_EQ(error_text, result.error_string());
}
TEST(result, result_errno_error_through_ostream) {
auto error_text = "test error"s;
constexpr int test_errno = 6;
errno = 6;
Result<Success> result = ErrnoError() << error_text;
errno = 0;
ASSERT_FALSE(result);
ASSERT_FALSE(result.has_value());
Result<std::string> result2 = Error() << result.error();
ASSERT_FALSE(result2);
ASSERT_FALSE(result2.has_value());
EXPECT_EQ(test_errno, result.error_errno());
EXPECT_EQ(error_text + ": " + strerror(test_errno), result.error_string());
}
TEST(result, constructor_forwarding) {
@ -215,7 +283,7 @@ TEST(result, die_on_access_failed_result) {
TEST(result, die_on_get_error_succesful_result) {
Result<std::string> result = "success";
ASSERT_DEATH(result.error(), "");
ASSERT_DEATH(result.error_string(), "");
}
} // namespace init

View file

@ -48,7 +48,6 @@
#include "selinux.h"
#include <fcntl.h>
#include <paths.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
@ -126,8 +125,7 @@ bool ForkExecveAndWaitForCompletion(const char* filename, char* const argv[]) {
}
TEMP_FAILURE_RETRY(close(pipe_fds[1]));
const char* envp[] = {_PATH_DEFPATH, nullptr};
if (execve(filename, argv, (char**)envp) == -1) {
if (execv(filename, argv) == -1) {
PLOG(ERROR) << "Failed to execve " << filename;
return false;
}

View file

@ -57,22 +57,20 @@ using android::base::WriteStringToFile;
namespace android {
namespace init {
static std::string ComputeContextFromExecutable(std::string& service_name,
const std::string& service_path) {
static Result<std::string> ComputeContextFromExecutable(std::string& service_name,
const std::string& service_path) {
std::string computed_context;
char* raw_con = nullptr;
char* raw_filecon = nullptr;
if (getcon(&raw_con) == -1) {
LOG(ERROR) << "could not get context while starting '" << service_name << "'";
return "";
return Error() << "Could not get security context";
}
std::unique_ptr<char> mycon(raw_con);
if (getfilecon(service_path.c_str(), &raw_filecon) == -1) {
LOG(ERROR) << "could not get file context while starting '" << service_name << "'";
return "";
return Error() << "Could not get file context";
}
std::unique_ptr<char> filecon(raw_filecon);
@ -84,12 +82,10 @@ static std::string ComputeContextFromExecutable(std::string& service_name,
free(new_con);
}
if (rc == 0 && computed_context == mycon.get()) {
LOG(ERROR) << "service " << service_name << " does not have a SELinux domain defined";
return "";
return Error() << "Service does not have an SELinux domain defined";
}
if (rc < 0) {
LOG(ERROR) << "could not get context while starting '" << service_name << "'";
return "";
return Error() << "Could not get process context";
}
return computed_context;
}
@ -147,14 +143,6 @@ static void ExpandArgs(const std::vector<std::string>& args, std::vector<char*>*
strs->push_back(nullptr);
}
ServiceEnvironmentInfo::ServiceEnvironmentInfo() {
}
ServiceEnvironmentInfo::ServiceEnvironmentInfo(const std::string& name,
const std::string& value)
: name(name), value(value) {
}
unsigned long Service::next_start_order_ = 1;
bool Service::is_exec_service_running_ = false;
@ -507,7 +495,7 @@ Result<Success> Service::ParseSeclabel(const std::vector<std::string>& args) {
}
Result<Success> Service::ParseSetenv(const std::vector<std::string>& args) {
envvars_.emplace_back(args[1], args[2]);
environment_vars_.emplace_back(args[1], args[2]);
return Success();
}
@ -637,16 +625,16 @@ Result<Success> Service::ParseLine(const std::vector<std::string>& args) {
static const OptionParserMap parser_map;
auto parser = parser_map.FindFunction(args);
if (!parser) return Error() << parser.error();
if (!parser) return parser.error();
return std::invoke(*parser, this, args);
}
bool Service::ExecStart() {
Result<Success> Service::ExecStart() {
flags_ |= SVC_ONESHOT;
if (!Start()) {
return false;
if (auto result = Start(); !result) {
return result;
}
flags_ |= SVC_EXEC;
@ -656,10 +644,10 @@ bool Service::ExecStart() {
<< supp_gids_.size() << " context " << (!seclabel_.empty() ? seclabel_ : "default")
<< ") started; waiting...";
return true;
return Success();
}
bool Service::Start() {
Result<Success> Service::Start() {
// Starting a service removes it from the disabled or reset state and
// immediately takes it out of the restarting state if it was in there.
flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
@ -668,7 +656,8 @@ bool Service::Start() {
// process of exiting, we've ensured that they will immediately restart
// on exit, unless they are ONESHOT.
if (flags_ & SVC_RUNNING) {
return false;
// It is not an error to try to start a service that is already running.
return Success();
}
bool needs_console = (flags_ & SVC_CONSOLE);
@ -681,28 +670,27 @@ bool Service::Start() {
// properly registered for the device node
int console_fd = open(console_.c_str(), O_RDWR | O_CLOEXEC);
if (console_fd < 0) {
PLOG(ERROR) << "service '" << name_ << "' couldn't open console '" << console_ << "'";
flags_ |= SVC_DISABLED;
return false;
return ErrnoError() << "Couldn't open console '" << console_ << "'";
}
close(console_fd);
}
struct stat sb;
if (stat(args_[0].c_str(), &sb) == -1) {
PLOG(ERROR) << "cannot find '" << args_[0] << "', disabling '" << name_ << "'";
flags_ |= SVC_DISABLED;
return false;
return ErrnoError() << "Cannot find '" << args_[0] << "'";
}
std::string scon;
if (!seclabel_.empty()) {
scon = seclabel_;
} else {
scon = ComputeContextFromExecutable(name_, args_[0]);
if (scon == "") {
return false;
auto result = ComputeContextFromExecutable(name_, args_[0]);
if (!result) {
return result.error();
}
scon = *result;
}
LOG(INFO) << "starting service '" << name_ << "'...";
@ -723,8 +711,8 @@ bool Service::Start() {
SetUpPidNamespace(name_);
}
for (const auto& ei : envvars_) {
add_environment(ei.name.c_str(), ei.value.c_str());
for (const auto& [key, value] : environment_vars_) {
setenv(key.c_str(), value.c_str(), 1);
}
std::for_each(descriptors_.begin(), descriptors_.end(),
@ -779,7 +767,7 @@ bool Service::Start() {
std::vector<char*> strs;
ExpandArgs(args_, &strs);
if (execve(strs[0], (char**) &strs[0], (char**) ENV) < 0) {
if (execv(strs[0], (char**)&strs[0]) < 0) {
PLOG(ERROR) << "cannot execve('" << strs[0] << "')";
}
@ -787,9 +775,8 @@ bool Service::Start() {
}
if (pid < 0) {
PLOG(ERROR) << "failed to fork for '" << name_ << "'";
pid_ = 0;
return false;
return ErrnoError() << "Failed to fork";
}
if (oom_score_adjust_ != -1000) {
@ -831,24 +818,24 @@ bool Service::Start() {
}
NotifyStateChange("running");
return true;
return Success();
}
bool Service::StartIfNotDisabled() {
Result<Success> Service::StartIfNotDisabled() {
if (!(flags_ & SVC_DISABLED)) {
return Start();
} else {
flags_ |= SVC_DISABLED_START;
}
return true;
return Success();
}
bool Service::Enable() {
Result<Success> Service::Enable() {
flags_ &= ~(SVC_DISABLED | SVC_RC_DISABLED);
if (flags_ & SVC_DISABLED_START) {
return Start();
}
return true;
return Success();
}
void Service::Reset() {
@ -874,7 +861,9 @@ void Service::Restart() {
StopOrReset(SVC_RESTART);
} else if (!(flags_ & SVC_RESTARTING)) {
/* Just start the service since it's not running. */
Start();
if (auto result = Start(); !result) {
LOG(ERROR) << "Could not restart '" << name_ << "': " << result.error();
}
} /* else: Service is restarting anyways. */
}

View file

@ -57,13 +57,6 @@
namespace android {
namespace init {
struct ServiceEnvironmentInfo {
ServiceEnvironmentInfo();
ServiceEnvironmentInfo(const std::string& name, const std::string& value);
std::string name;
std::string value;
};
class Service {
public:
Service(const std::string& name, const std::vector<std::string>& args);
@ -77,10 +70,10 @@ class Service {
bool IsRunning() { return (flags_ & SVC_RUNNING) != 0; }
Result<Success> ParseLine(const std::vector<std::string>& args);
bool ExecStart();
bool Start();
bool StartIfNotDisabled();
bool Enable();
Result<Success> ExecStart();
Result<Success> Start();
Result<Success> StartIfNotDisabled();
Result<Success> Enable();
void Reset();
void Stop();
void Terminate();
@ -178,7 +171,7 @@ class Service {
std::string seclabel_;
std::vector<std::unique_ptr<DescriptorInfo>> descriptors_;
std::vector<ServiceEnvironmentInfo> envvars_;
std::vector<std::pair<std::string, std::string>> environment_vars_;
Action onrestart_; // Commands to execute on restart.

View file

@ -34,7 +34,7 @@ TEST(util, ReadFile_ENOENT) {
auto file_contents = ReadFile("/proc/does-not-exist");
EXPECT_EQ(ENOENT, errno);
ASSERT_FALSE(file_contents);
EXPECT_EQ("open() failed: No such file or directory", file_contents.error());
EXPECT_EQ("open() failed: No such file or directory", file_contents.error_string());
}
TEST(util, ReadFileGroupWriteable) {
@ -45,7 +45,7 @@ TEST(util, ReadFileGroupWriteable) {
EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0620, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
auto file_contents = ReadFile(tf.path);
ASSERT_FALSE(file_contents) << strerror(errno);
EXPECT_EQ("Skipping insecure file", file_contents.error());
EXPECT_EQ("Skipping insecure file", file_contents.error_string());
}
TEST(util, ReadFileWorldWiteable) {
@ -56,7 +56,7 @@ TEST(util, ReadFileWorldWiteable) {
EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0602, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
auto file_contents = ReadFile(tf.path);
ASSERT_FALSE(file_contents) << strerror(errno);
EXPECT_EQ("Skipping insecure file", file_contents.error());
EXPECT_EQ("Skipping insecure file", file_contents.error_string());
}
TEST(util, ReadFileSymbolicLink) {
@ -65,7 +65,7 @@ TEST(util, ReadFileSymbolicLink) {
auto file_contents = ReadFile("/charger");
EXPECT_EQ(ELOOP, errno);
ASSERT_FALSE(file_contents);
EXPECT_EQ("open() failed: Too many symbolic links encountered", file_contents.error());
EXPECT_EQ("open() failed: Too many symbolic links encountered", file_contents.error_string());
}
TEST(util, ReadFileSuccess) {
@ -130,7 +130,7 @@ TEST(util, DecodeUid) {
decoded_uid = DecodeUid("toot");
EXPECT_FALSE(decoded_uid);
EXPECT_EQ("getpwnam failed: No such file or directory", decoded_uid.error());
EXPECT_EQ("getpwnam failed: No such file or directory", decoded_uid.error_string());
decoded_uid = DecodeUid("123");
EXPECT_TRUE(decoded_uid);