Merge "Add a sample service for testing init." am: d7b5c7b2da am: 41aa8f07b2
am: 7fec45ee32

Change-Id: Id8255444079be334d8a82a75532def19fffb3b17
diff --git a/init/.clang-format b/init/.clang-format
new file mode 100644
index 0000000..48d423f
--- /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 9e61fb2..a10a714 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -134,3 +134,8 @@
 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 0000000..30c9e9d
--- /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 0000000..6773235
--- /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 <jobs> 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 0000000..e7206f8
--- /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 <unistd.h>
+
+#include <map>
+#include <sstream>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+void Usage(char* argv[]) {
+    printf("Usage: %s <status field> <value> [<status field> <value>]*\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<std::string, std::string> fields;
+    std::vector<std::string> lines = android::base::Split(status, "\n");
+    for (const auto& line : lines) {
+        std::vector<std::string> 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<size_t>(argc);  // |argc| >= 3.
+    for (size_t i = 1; i < static_cast<size_t>(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 0000000..91e1a0f
--- /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