From 28e980b59c8e309d248b361829ed34408632720f Mon Sep 17 00:00:00 2001 From: Jorge Lucangeli Obes Date: Tue, 20 Dec 2016 16:54:04 -0500 Subject: [PATCH] Add a sample service for testing init. I find myself using something like this every time I add functionality to init. I cannot possibly be the only one doing this. On the other hand, if this hasn't been added for so long, maybe there's a reason for that. The advantage of using a test service versus modifying an existing service is that the test service doesn't *require* any permissions or privileges, so you can add and/or remove whatever you need to test without breaking the service. I found it useful to have the service check its own /proc//status from command-line arguments, so that's what the service does. This CL also adds a .clang-format file for init. Bug: None Test: Service runs and exits successfully. Change-Id: I3e7841a7283158e10c0bf55e0103c03902afb1f0 --- init/.clang-format | 14 ++++++ init/Android.mk | 5 ++ init/test_service/Android.mk | 27 +++++++++++ init/test_service/README.md | 43 ++++++++++++++++ init/test_service/test_service.cpp | 78 ++++++++++++++++++++++++++++++ init/test_service/test_service.rc | 7 +++ 6 files changed, 174 insertions(+) create mode 100644 init/.clang-format create mode 100644 init/test_service/Android.mk create mode 100644 init/test_service/README.md create mode 100644 init/test_service/test_service.cpp create mode 100644 init/test_service/test_service.rc diff --git a/init/.clang-format b/init/.clang-format new file mode 100644 index 000000000..48d423f71 --- /dev/null +++ b/init/.clang-format @@ -0,0 +1,14 @@ +--- +Language: Cpp +BasedOnStyle: Google +BinPackArguments: true +BinPackParameters: true +ColumnLimit: 100 +ConstructorInitializerAllOnOneLineOrOnePerLine: false +IndentWidth: 4 +Standard: Auto +TabWidth: 8 +UseTab: Never +DerivePointerAlignment: false +PointerAlignment: Left +... diff --git a/init/Android.mk b/init/Android.mk index 9e61fb2f6..a10a714a1 100644 --- a/init/Android.mk +++ b/init/Android.mk @@ -134,3 +134,8 @@ LOCAL_SANITIZE := integer LOCAL_CLANG := true LOCAL_CPPFLAGS := -Wall -Wextra -Werror include $(BUILD_NATIVE_TEST) + + +# Include targets in subdirs. +# ========================================================= +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/init/test_service/Android.mk b/init/test_service/Android.mk new file mode 100644 index 000000000..30c9e9d04 --- /dev/null +++ b/init/test_service/Android.mk @@ -0,0 +1,27 @@ +# Copyright (C) 2016 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. + +LOCAL_PATH := $(call my-dir) + +# Sample service for testing. +# ========================================================= +include $(CLEAR_VARS) +LOCAL_MODULE := test_service +LOCAL_SRC_FILES := test_service.cpp + +LOCAL_SHARED_LIBRARIES += libbase + +LOCAL_INIT_RC := test_service.rc + +include $(BUILD_EXECUTABLE) diff --git a/init/test_service/README.md b/init/test_service/README.md new file mode 100644 index 000000000..67732354c --- /dev/null +++ b/init/test_service/README.md @@ -0,0 +1,43 @@ +# Sample service for testing +This is a sample service that can be used for testing init. + +## Design +The service includes a `.rc` file that allows starting it from init. + + service test_service /system/bin/test_service CapAmb 0000000000003000 + class main + user system + group system + capabilities NET_ADMIN NET_RAW + disabled + oneshot + +The service accepts any even number of arguments on the command line +(i.e. any number of pairs of arguments.) +It will attempt to find the first element of each pair of arguments in +`/proc/self/status`, and attempt to exactly match the second element of the pair +to the relevant line of `proc/self/status`. + +### Example +In the above case, the service will look for lines containing `CapAmb`: + + cat /proc/self/status + ... + CapAmb: 0000000000003000 + +And then attempt to exactly match the token after `:`, `0000000000003000`, +with the command-line argument. +If they match, the service exits successfully. If not, the service will exit +with an error. + +## Usage + mmma -j system/core/init/testservice + adb root + adb remount + adb sync + adb reboot + adb root + adb shell start test_service + adb logcat -b all -d | grep test_service + +Look for an exit status of 0. diff --git a/init/test_service/test_service.cpp b/init/test_service/test_service.cpp new file mode 100644 index 000000000..e7206f89e --- /dev/null +++ b/init/test_service/test_service.cpp @@ -0,0 +1,78 @@ +// Copyright (C) 2016 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 + +#include +#include +#include + +#include +#include +#include + +void Usage(char* argv[]) { + printf("Usage: %s [ ]*\n", argv[0]); + printf("E.g.: $ %s Uid \"1000 1000 1000 1000\"\n", argv[0]); +} + +int main(int argc, char* argv[]) { + if (argc < 3) { + Usage(argv); + LOG(FATAL) << "no status field requested"; + } + if (argc % 2 == 0) { + // Since |argc| counts argv[0], if |argc| is odd, then the number of + // command-line arguments is even. + Usage(argv); + LOG(FATAL) << "need even number of command-line arguments"; + } + + std::string status; + bool res = android::base::ReadFileToString("/proc/self/status", &status, true); + if (!res) { + PLOG(FATAL) << "could not read /proc/self/status"; + } + + std::map fields; + std::vector lines = android::base::Split(status, "\n"); + for (const auto& line : lines) { + std::vector tokens = android::base::Split(line, ":"); + if (tokens.size() >= 2) { + std::string field = tokens[0]; + std::string value = android::base::Trim(tokens[1]); + if (field.length() > 0) { + fields[field] = value; + } + } + } + + bool test_fails = false; + size_t uargc = static_cast(argc); // |argc| >= 3. + for (size_t i = 1; i < static_cast(argc); i = i + 2) { + std::string expected_value = argv[i + 1]; + auto f = fields.find(argv[i]); + if (f != fields.end()) { + if (f->second != expected_value) { + LOG(ERROR) << "field '" << argv[i] << "' expected '" << expected_value + << "', actual '" << f->second << "'"; + test_fails = true; + } + } else { + LOG(WARNING) << "could not find field '" << argv[i] << "'"; + } + } + + return test_fails ? 1 : 0; +} diff --git a/init/test_service/test_service.rc b/init/test_service/test_service.rc new file mode 100644 index 000000000..91e1a0f4a --- /dev/null +++ b/init/test_service/test_service.rc @@ -0,0 +1,7 @@ +service test_service /system/bin/test_service CapAmb 0000000000003000 + class main + user system + group system + capabilities NET_ADMIN NET_RAW + disabled + oneshot