init: add stdio_to_kmsg option

Some services are not native android services and therefore don't log
via the normal mechanisms.  This gives developers an option to have
their stdout/stderr logs sent directly to kmsg.

Test: see test prints to kernel log
Change-Id: I7973ea74d5cab3a90c2cd9a3d5de2266439d0c01
This commit is contained in:
Tom Cherry 2019-09-23 16:16:54 -07:00
parent 03642ad8b8
commit f74b7f5756
5 changed files with 34 additions and 5 deletions

View file

@ -170,6 +170,8 @@ runs the service.
be changed by setting the "androidboot.console" kernel parameter. In
all cases the leading "/dev/" should be omitted, so "/dev/tty0" would be
specified as just "console tty0".
This option connects stdin, stdout, and stderr to the console. It is mutually exclusive with the
stdio_to_kmsg option, which only connects stdout and stderr to kmsg.
`critical`
> This is a device-critical service. If it exits more than four times in
@ -313,6 +315,13 @@ runs the service.
seclabel or computed based on the service executable file security context.
For native executables see libcutils android\_get\_control\_socket().
`stdio_to_kmsg`
> Redirect stdout and stderr to /dev/kmsg_debug. This is useful for services that do not use native
Android logging during early boot and whose logs messages we want to capture. This is only enabled
when /dev/kmsg_debug is enabled, which is only enabled on userdebug and eng builds.
This is mutually exclusive with the console option, which additionally connects stdin to the
given console.
`timeout_period <seconds>`
> Provide a timeout after which point the service will be killed. The oneshot keyword is respected
here, so oneshot services do not automatically restart, however all other services will.

View file

@ -83,6 +83,9 @@ Result<void> ServiceParser::ParseClass(std::vector<std::string>&& args) {
}
Result<void> ServiceParser::ParseConsole(std::vector<std::string>&& args) {
if (service_->proc_attr_.stdio_to_kmsg) {
return Error() << "'console' and 'stdio_to_kmsg' are mutually exclusive";
}
service_->flags_ |= SVC_CONSOLE;
service_->proc_attr_.console = args.size() > 1 ? "/dev/" + args[1] : "";
return {};
@ -429,6 +432,14 @@ Result<void> ServiceParser::ParseSocket(std::vector<std::string>&& args) {
return {};
}
Result<void> ServiceParser::ParseStdioToKmsg(std::vector<std::string>&& args) {
if (service_->flags_ & SVC_CONSOLE) {
return Error() << "'stdio_to_kmsg' and 'console' are mutually exclusive";
}
service_->proc_attr_.stdio_to_kmsg = true;
return {};
}
// name type
Result<void> ServiceParser::ParseFile(std::vector<std::string>&& args) {
if (args[2] != "r" && args[2] != "w" && args[2] != "rw") {
@ -514,6 +525,7 @@ const KeywordMap<ServiceParser::OptionParser>& ServiceParser::GetParserMap() con
{"shutdown", {1, 1, &ServiceParser::ParseShutdown}},
{"sigstop", {0, 0, &ServiceParser::ParseSigstop}},
{"socket", {3, 6, &ServiceParser::ParseSocket}},
{"stdio_to_kmsg", {0, 0, &ServiceParser::ParseStdioToKmsg}},
{"timeout_period", {1, 1, &ServiceParser::ParseTimeoutPeriod}},
{"updatable", {0, 0, &ServiceParser::ParseUpdatable}},
{"user", {1, 1, &ServiceParser::ParseUser}},

View file

@ -75,6 +75,7 @@ class ServiceParser : public SectionParser {
Result<void> ParseShutdown(std::vector<std::string>&& args);
Result<void> ParseSigstop(std::vector<std::string>&& args);
Result<void> ParseSocket(std::vector<std::string>&& args);
Result<void> ParseStdioToKmsg(std::vector<std::string>&& args);
Result<void> ParseTimeoutPeriod(std::vector<std::string>&& args);
Result<void> ParseFile(std::vector<std::string>&& args);
Result<void> ParseUser(std::vector<std::string>&& args);

View file

@ -16,10 +16,12 @@
#include "service_utils.h"
#include <fcntl.h>
#include <grp.h>
#include <sys/mount.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <unistd.h>
#include <android-base/file.h>
#include <android-base/logging.h>
@ -121,11 +123,15 @@ Result<void> SetUpPidNamespace(const char* name) {
return {};
}
void ZapStdio() {
void SetupStdio(bool stdio_to_kmsg) {
auto fd = unique_fd{open("/dev/null", O_RDWR | O_CLOEXEC)};
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
dup2(fd, STDIN_FILENO);
if (stdio_to_kmsg) {
fd.reset(open("/dev/kmsg_debug", O_WRONLY | O_CLOEXEC));
if (fd == -1) fd.reset(open("/dev/null", O_WRONLY | O_CLOEXEC));
}
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
}
void OpenConsole(const std::string& console) {
@ -238,7 +244,7 @@ Result<void> SetProcessAttributes(const ProcessAttributes& attr) {
if (setpgid(0, getpid()) == -1) {
return ErrnoError() << "setpgid failed";
}
ZapStdio();
SetupStdio(attr.stdio_to_kmsg);
}
for (const auto& rlimit : attr.rlimits) {

View file

@ -77,6 +77,7 @@ struct ProcessAttributes {
gid_t gid;
std::vector<gid_t> supp_gids;
int priority;
bool stdio_to_kmsg;
};
Result<void> SetProcessAttributes(const ProcessAttributes& attr);