summaryrefslogtreecommitdiffstats
path: root/init
diff options
context:
space:
mode:
authorTreehugger Robot <treehugger-gerrit@google.com>2017-08-28 19:27:08 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2017-08-28 19:27:08 +0000
commit459aa1cac669b3bac6232e5906ceeacd33671b62 (patch)
tree41c38cd57a6fbfb27195d10cc89309ae8a867e93 /init
parent9bde0dc769b8085536faded5da5ebf0e1077dd92 (diff)
parent7ac013de7edc7ec6570ff6a5b6bd3bdce68b769c (diff)
downloadcore-459aa1cac669b3bac6232e5906ceeacd33671b62.tar.gz
core-459aa1cac669b3bac6232e5906ceeacd33671b62.tar.bz2
core-459aa1cac669b3bac6232e5906ceeacd33671b62.zip
Merge "init: support setting rlimits per service"
Diffstat (limited to 'init')
-rw-r--r--init/Android.bp2
-rw-r--r--init/README.md12
-rw-r--r--init/builtins.cpp17
-rw-r--r--init/rlimit_parser.cpp78
-rw-r--r--init/rlimit_parser.h35
-rw-r--r--init/rlimit_parser_test.cpp126
-rw-r--r--init/service.cpp16
-rw-r--r--init/service.h4
8 files changed, 276 insertions, 14 deletions
diff --git a/init/Android.bp b/init/Android.bp
index efa5a022c..33dfe566c 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -76,6 +76,7 @@ cc_library_static {
"security.cpp",
"selinux.cpp",
"service.cpp",
+ "rlimit_parser.cpp",
"tokenizer.cpp",
"uevent_listener.cpp",
"ueventd_parser.cpp",
@@ -163,6 +164,7 @@ cc_test {
"init_test.cpp",
"property_service_test.cpp",
"result_test.cpp",
+ "rlimit_parser_test.cpp",
"service_test.cpp",
"ueventd_test.cpp",
"util_test.cpp",
diff --git a/init/README.md b/init/README.md
index 0ea00fba9..b681f2115 100644
--- a/init/README.md
+++ b/init/README.md
@@ -216,6 +216,12 @@ runs the service.
http://man7.org/linux/man-pages/man7/capabilities.7.html for a list of Linux
capabilities.
+`setrlimit <resource> <cur> <max>`
+> This applies the given rlimit to the service. rlimits are inherited by child
+ processes, so this effectively applies the given rlimit to the process tree
+ started by this service.
+ It is parsed similarly to the setrlimit command specified below.
+
`seclabel <seclabel>`
> Change to 'seclabel' before exec'ing this service.
Primarily for use by services run from the rootfs, e.g. ueventd, adbd.
@@ -455,7 +461,11 @@ Commands
within _value_.
`setrlimit <resource> <cur> <max>`
-> Set the rlimit for a resource.
+> Set the rlimit for a resource. This applies to all processes launched after
+ the limit is set. It is intended to be set early in init and applied globally.
+ _resource_ is best specified using its text representation ('cpu', 'rtio', etc
+ or 'RLIM_CPU', 'RLIM_RTIO', etc). It also may be specified as the int value
+ that the resource enum corresponds to.
`start <service>`
> Start a service running if it is not already running.
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 54ccf091e..e2e3d933b 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -63,6 +63,7 @@
#include "parser.h"
#include "property_service.h"
#include "reboot.h"
+#include "rlimit_parser.h"
#include "service.h"
#include "signal_handler.h"
#include "util.h"
@@ -563,20 +564,10 @@ static Result<Success> do_setprop(const std::vector<std::string>& args) {
}
static Result<Success> do_setrlimit(const std::vector<std::string>& args) {
- int resource;
- if (!android::base::ParseInt(args[1], &resource)) {
- return Error() << "unable to parse resource, " << args[1];
- }
-
- struct rlimit limit;
- if (!android::base::ParseUint(args[2], &limit.rlim_cur)) {
- return Error() << "unable to parse rlim_cur, " << args[2];
- }
- if (!android::base::ParseUint(args[3], &limit.rlim_max)) {
- return Error() << "unable to parse rlim_max, " << args[3];
- }
+ auto rlimit = ParseRlimit(args);
+ if (!rlimit) return rlimit.error();
- if (setrlimit(resource, &limit) == -1) {
+ if (setrlimit(rlimit->first, &rlimit->second) == -1) {
return ErrnoError() << "setrlimit failed";
}
return Success();
diff --git a/init/rlimit_parser.cpp b/init/rlimit_parser.cpp
new file mode 100644
index 000000000..fe1d6a724
--- /dev/null
+++ b/init/rlimit_parser.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2017 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 "rlimit_parser.h"
+
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+
+using android::base::EqualsIgnoreCase;
+using android::base::ParseInt;
+using android::base::ParseUint;
+using android::base::StartsWith;
+
+namespace android {
+namespace init {
+
+// Builtins and service definitions both have their arguments start at 1 and finish at 3.
+Result<std::pair<int, rlimit>> ParseRlimit(const std::vector<std::string>& args) {
+ static const std::vector<std::pair<const char*, int>> text_to_resources = {
+ {"cpu", 0}, {"fsize", 1}, {"data", 2}, {"stack", 3},
+ {"core", 4}, {"rss", 5}, {"nproc", 6}, {"nofile", 7},
+ {"memlock", 8}, {"as", 9}, {"locks", 10}, {"sigpending", 11},
+ {"msgqueue", 12}, {"nice", 13}, {"rtprio", 14}, {"rttime", 15},
+ };
+
+ int resource;
+
+ if (ParseInt(args[1], &resource)) {
+ if (resource >= RLIM_NLIMITS) {
+ return Error() << "Resource '" << args[1] << "' over the maximum resource value '"
+ << RLIM_NLIMITS << "'";
+ } else if (resource < 0) {
+ return Error() << "Resource '" << args[1] << "' below the minimum resource value '0'";
+ }
+ } else {
+ std::string resource_string;
+ if (StartsWith(args[1], "RLIM_")) {
+ resource_string = args[1].substr(5);
+ } else {
+ resource_string = args[1];
+ }
+
+ auto it = std::find_if(text_to_resources.begin(), text_to_resources.end(),
+ [&resource_string](const auto& entry) {
+ return EqualsIgnoreCase(resource_string, entry.first);
+ });
+ if (it == text_to_resources.end()) {
+ return Error() << "Could not parse resource '" << args[1] << "'";
+ }
+
+ resource = it->second;
+ }
+
+ rlimit limit;
+ if (!ParseUint(args[2], &limit.rlim_cur)) {
+ return Error() << "Could not parse soft limit '" << args[2] << "'";
+ }
+ if (!ParseUint(args[3], &limit.rlim_max)) {
+ return Error() << "Could not parse hard limit '" << args[3] << "'";
+ }
+ return {resource, limit};
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/rlimit_parser.h b/init/rlimit_parser.h
new file mode 100644
index 000000000..0396463b5
--- /dev/null
+++ b/init/rlimit_parser.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 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_RLIMIT_PARSER_H
+#define _INIT_RLIMIT_PARSER_H
+
+#include <sys/resource.h>
+
+#include <string>
+#include <vector>
+
+#include "result.h"
+
+namespace android {
+namespace init {
+
+Result<std::pair<int, rlimit>> ParseRlimit(const std::vector<std::string>& args);
+
+} // namespace init
+} // namespace android
+
+#endif
diff --git a/init/rlimit_parser_test.cpp b/init/rlimit_parser_test.cpp
new file mode 100644
index 000000000..f3f9eb4e3
--- /dev/null
+++ b/init/rlimit_parser_test.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2017 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 "rlimit_parser.h"
+
+#include <iostream>
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace init {
+
+void TestRlimitSuccess(std::vector<std::string> input,
+ const std::pair<int, rlimit>& expected_result) {
+ input.emplace(input.begin(), "");
+ ASSERT_EQ(4U, input.size());
+ auto result = ParseRlimit(input);
+
+ ASSERT_TRUE(result) << "input: " << input[1];
+ const auto& [resource, rlimit] = *result;
+ const auto& [expected_resource, expected_rlimit] = expected_result;
+ EXPECT_EQ(expected_resource, resource);
+ EXPECT_EQ(expected_rlimit.rlim_cur, rlimit.rlim_cur);
+ EXPECT_EQ(expected_rlimit.rlim_max, rlimit.rlim_max);
+}
+
+void TestRlimitFailure(std::vector<std::string> input, const std::string& expected_result) {
+ input.emplace(input.begin(), "");
+ ASSERT_EQ(4U, input.size());
+ auto result = ParseRlimit(input);
+
+ ASSERT_FALSE(result) << "input: " << input[1];
+ EXPECT_EQ(expected_result, result.error_string());
+ EXPECT_EQ(0, result.error_errno());
+}
+
+TEST(rlimit, RlimitSuccess) {
+ const std::vector<std::pair<std::vector<std::string>, std::pair<int, rlimit>>>
+ inputs_and_results = {
+ {{"cpu", "10", "10"}, {0, {10, 10}}},
+ {{"fsize", "10", "10"}, {1, {10, 10}}},
+ {{"data", "10", "10"}, {2, {10, 10}}},
+ {{"stack", "10", "10"}, {3, {10, 10}}},
+ {{"core", "10", "10"}, {4, {10, 10}}},
+ {{"rss", "10", "10"}, {5, {10, 10}}},
+ {{"nproc", "10", "10"}, {6, {10, 10}}},
+ {{"nofile", "10", "10"}, {7, {10, 10}}},
+ {{"memlock", "10", "10"}, {8, {10, 10}}},
+ {{"as", "10", "10"}, {9, {10, 10}}},
+ {{"locks", "10", "10"}, {10, {10, 10}}},
+ {{"sigpending", "10", "10"}, {11, {10, 10}}},
+ {{"msgqueue", "10", "10"}, {12, {10, 10}}},
+ {{"nice", "10", "10"}, {13, {10, 10}}},
+ {{"rtprio", "10", "10"}, {14, {10, 10}}},
+ {{"rttime", "10", "10"}, {15, {10, 10}}},
+
+ {{"RLIM_CPU", "10", "10"}, {0, {10, 10}}},
+ {{"RLIM_FSIZE", "10", "10"}, {1, {10, 10}}},
+ {{"RLIM_DATA", "10", "10"}, {2, {10, 10}}},
+ {{"RLIM_STACK", "10", "10"}, {3, {10, 10}}},
+ {{"RLIM_CORE", "10", "10"}, {4, {10, 10}}},
+ {{"RLIM_RSS", "10", "10"}, {5, {10, 10}}},
+ {{"RLIM_NPROC", "10", "10"}, {6, {10, 10}}},
+ {{"RLIM_NOFILE", "10", "10"}, {7, {10, 10}}},
+ {{"RLIM_MEMLOCK", "10", "10"}, {8, {10, 10}}},
+ {{"RLIM_AS", "10", "10"}, {9, {10, 10}}},
+ {{"RLIM_LOCKS", "10", "10"}, {10, {10, 10}}},
+ {{"RLIM_SIGPENDING", "10", "10"}, {11, {10, 10}}},
+ {{"RLIM_MSGQUEUE", "10", "10"}, {12, {10, 10}}},
+ {{"RLIM_NICE", "10", "10"}, {13, {10, 10}}},
+ {{"RLIM_RTPRIO", "10", "10"}, {14, {10, 10}}},
+ {{"RLIM_RTTIME", "10", "10"}, {15, {10, 10}}},
+
+ {{"0", "10", "10"}, {0, {10, 10}}},
+ {{"1", "10", "10"}, {1, {10, 10}}},
+ {{"2", "10", "10"}, {2, {10, 10}}},
+ {{"3", "10", "10"}, {3, {10, 10}}},
+ {{"4", "10", "10"}, {4, {10, 10}}},
+ {{"5", "10", "10"}, {5, {10, 10}}},
+ {{"6", "10", "10"}, {6, {10, 10}}},
+ {{"7", "10", "10"}, {7, {10, 10}}},
+ {{"8", "10", "10"}, {8, {10, 10}}},
+ {{"9", "10", "10"}, {9, {10, 10}}},
+ {{"10", "10", "10"}, {10, {10, 10}}},
+ {{"11", "10", "10"}, {11, {10, 10}}},
+ {{"12", "10", "10"}, {12, {10, 10}}},
+ {{"13", "10", "10"}, {13, {10, 10}}},
+ {{"14", "10", "10"}, {14, {10, 10}}},
+ {{"15", "10", "10"}, {15, {10, 10}}},
+ };
+
+ for (const auto& [input, expected_result] : inputs_and_results) {
+ TestRlimitSuccess(input, expected_result);
+ }
+}
+
+TEST(rlimit, RlimitFailure) {
+ const std::vector<std::pair<std::vector<std::string>, std::string>> inputs_and_results = {
+ {{"-4", "10", "10"}, "Resource '-4' below the minimum resource value '0'"},
+ {{"100", "10", "10"}, "Resource '100' over the maximum resource value '16'"},
+ {{"bad_string", "10", "10"}, "Could not parse resource 'bad_string'"},
+ {{"RLIM_", "10", "10"}, "Could not parse resource 'RLIM_'"},
+ {{"cpu", "abc", "10"}, "Could not parse soft limit 'abc'"},
+ {{"cpu", "10", "abc"}, "Could not parse hard limit 'abc'"},
+ };
+
+ for (const auto& [input, expected_result] : inputs_and_results) {
+ TestRlimitFailure(input, expected_result);
+ }
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/service.cpp b/init/service.cpp
index dee0c3d23..6f27a4bb6 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -43,6 +43,7 @@
#include "init.h"
#include "property_service.h"
+#include "rlimit_parser.h"
#include "util.h"
using android::base::boot_clock;
@@ -220,6 +221,12 @@ void Service::KillProcessGroup(int signal) {
}
void Service::SetProcessAttributes() {
+ for (const auto& rlimit : rlimits_) {
+ if (setrlimit(rlimit.first, &rlimit.second) == -1) {
+ LOG(FATAL) << StringPrintf("setrlimit(%d, {rlim_cur=%ld, rlim_max=%ld}) failed",
+ rlimit.first, rlimit.second.rlim_cur, rlimit.second.rlim_max);
+ }
+ }
// Keep capabilites on uid change.
if (capabilities_.any() && uid_) {
// If Android is running in a container, some securebits might already
@@ -493,6 +500,14 @@ Result<Success> Service::ParseMemcgSoftLimitInBytes(const std::vector<std::strin
return Success();
}
+Result<Success> Service::ParseProcessRlimit(const std::vector<std::string>& args) {
+ auto rlimit = ParseRlimit(args);
+ if (!rlimit) return rlimit.error();
+
+ rlimits_.emplace_back(*rlimit);
+ return Success();
+}
+
Result<Success> Service::ParseSeclabel(const std::vector<std::string>& args) {
seclabel_ = args[1];
return Success();
@@ -613,6 +628,7 @@ const Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
{"memcg.limit_in_bytes",
{1, 1, &Service::ParseMemcgLimitInBytes}},
{"namespace", {1, 2, &Service::ParseNamespace}},
+ {"rlimit", {3, 3, &Service::ParseProcessRlimit}},
{"seclabel", {1, 1, &Service::ParseSeclabel}},
{"setenv", {2, 2, &Service::ParseSetenv}},
{"shutdown", {1, 1, &Service::ParseShutdown}},
diff --git a/init/service.h b/init/service.h
index 1f2c44f93..67542ca92 100644
--- a/init/service.h
+++ b/init/service.h
@@ -17,6 +17,7 @@
#ifndef _INIT_SERVICE_H
#define _INIT_SERVICE_H
+#include <sys/resource.h>
#include <sys/types.h>
#include <memory>
@@ -138,6 +139,7 @@ class Service {
Result<Success> ParseMemcgSoftLimitInBytes(const std::vector<std::string>& args);
Result<Success> ParseMemcgSwappiness(const std::vector<std::string>& args);
Result<Success> ParseNamespace(const std::vector<std::string>& args);
+ Result<Success> ParseProcessRlimit(const std::vector<std::string>& args);
Result<Success> ParseSeclabel(const std::vector<std::string>& args);
Result<Success> ParseSetenv(const std::vector<std::string>& args);
Result<Success> ParseShutdown(const std::vector<std::string>& args);
@@ -195,6 +197,8 @@ class Service {
unsigned long start_order_;
+ std::vector<std::pair<int, rlimit>> rlimits_;
+
std::vector<std::string> args_;
};