Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 28e980b5 authored by Jorge Lucangeli Obes's avatar Jorge Lucangeli Obes
Browse files

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/<pid>/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
parent 23f4e6b0
Loading
Loading
Loading
Loading

init/.clang-format

0 → 100644
+14 −0
Original line number Diff line number Diff line
---
Language:        Cpp
BasedOnStyle:  Google
BinPackArguments: true
BinPackParameters: true
ColumnLimit:     100
ConstructorInitializerAllOnOneLineOrOnePerLine: false
IndentWidth:     4
Standard:        Auto
TabWidth:        8
UseTab:          Never
DerivePointerAlignment: false
PointerAlignment: Left
...
+5 −0
Original line number Diff line number Diff line
@@ -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))
+27 −0
Original line number Diff line number Diff line
# 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)
+43 −0
Original line number Diff line number Diff line
# 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.
+78 −0
Original line number Diff line number Diff line
// 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;
}
Loading