diff options
author | Treehugger Robot <treehugger-gerrit@google.com> | 2017-08-28 19:27:08 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2017-08-28 19:27:08 +0000 |
commit | 459aa1cac669b3bac6232e5906ceeacd33671b62 (patch) | |
tree | 41c38cd57a6fbfb27195d10cc89309ae8a867e93 /init | |
parent | 9bde0dc769b8085536faded5da5ebf0e1077dd92 (diff) | |
parent | 7ac013de7edc7ec6570ff6a5b6bd3bdce68b769c (diff) | |
download | core-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.bp | 2 | ||||
-rw-r--r-- | init/README.md | 12 | ||||
-rw-r--r-- | init/builtins.cpp | 17 | ||||
-rw-r--r-- | init/rlimit_parser.cpp | 78 | ||||
-rw-r--r-- | init/rlimit_parser.h | 35 | ||||
-rw-r--r-- | init/rlimit_parser_test.cpp | 126 | ||||
-rw-r--r-- | init/service.cpp | 16 | ||||
-rw-r--r-- | init/service.h | 4 |
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_; }; |