Versioning for apex init.rc files

Support an "init.#rc" versioning scheme for apex init files.
chooses highest # <= current system sdk.
".rc" (aka the old init.rc) is treated as sdk 0
Document these semantics in README.md

Bug: 198186200
Test: booting, lots of logcat output
Change-Id: I2d0405be73dae9bafa6f22535b29ed6b053ddbc4
This commit is contained in:
Ray Essick 2021-10-07 15:48:11 -07:00
parent 132f2341f9
commit 35ffd69db5
2 changed files with 93 additions and 5 deletions

View file

@ -77,6 +77,43 @@ monolithic init .rc files. This additionally will aid in merge
conflict resolution when multiple services are added to the system, as
each one will go into a separate file.
Versioned RC files within APEXs
-------------------------------
With the arrival of mainline on Android Q, the individual mainline
modules carry their own init.rc files within their boundaries. Init
processes these files according to the naming pattern `/apex/*/etc/*rc`.
Because APEX modules must run on more than one release of Android,
they may require different parameters as part of the services they
define. This is achieved, starting in Android T, by incorporating
the SDK version information in the name of the init file. The suffix
is changed from `.rc` to `.#rc` where # is the first SDK where that
RC file is accepted. An init file specific to SDK=31 might be named
`init.31rc`. With this scheme, an APEX may include multiple init files. An
example is appropriate.
For an APEX module with the following files in /apex/sample-module/apex/etc/:
1. init.rc
2. init.32rc
4. init.35rc
The selection rule chooses the highest `.#rc` value that does not
exceed the SDK of the currently running system. The unadorned `.rc`
is interpreted as sdk=0.
When this APEX is installed on a device with SDK <=31, the system will
process init.rc. When installed on a device running SDK 32, 33, or 34,
it will use init.32rc. When installed on a device running SDKs >= 35,
it will choose init.35rc
This versioning scheme is used only for the init files within APEX
modules; it does not apply to the init files stored in /system/etc/init,
/vendor/etc/init, or other directories.
This naming scheme is available after Android S.
Actions
-------
Actions are named sequences of commands. Actions have a trigger which

View file

@ -28,6 +28,7 @@
#include <net/if.h>
#include <sched.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -42,6 +43,7 @@
#include <sys/wait.h>
#include <unistd.h>
#include <map>
#include <memory>
#include <ApexProperties.sysprop.h>
@ -1313,7 +1315,7 @@ static Result<void> do_update_linker_config(const BuiltinArguments&) {
static Result<void> parse_apex_configs() {
glob_t glob_result;
static constexpr char glob_pattern[] = "/apex/*/etc/*.rc";
static constexpr char glob_pattern[] = "/apex/*/etc/*rc";
const int ret = glob(glob_pattern, GLOB_MARK, nullptr, &glob_result);
if (ret != 0 && ret != GLOB_NOMATCH) {
globfree(&glob_result);
@ -1330,17 +1332,66 @@ static Result<void> parse_apex_configs() {
if (paths.size() >= 3 && paths[2].find('@') != std::string::npos) {
continue;
}
// Filter directories
if (path.back() == '/') {
continue;
}
configs.push_back(path);
}
globfree(&glob_result);
bool success = true;
// Compare all files /apex/path.#rc and /apex/path.rc with the same "/apex/path" prefix,
// choosing the one with the highest # that doesn't exceed the system's SDK.
// (.rc == .0rc for ranking purposes)
//
int active_sdk = android::base::GetIntProperty("ro.build.version.sdk", INT_MAX);
std::map<std::string, std::pair<std::string, int>> script_map;
for (const auto& c : configs) {
if (c.back() == '/') {
// skip if directory
int sdk = 0;
const std::vector<std::string> parts = android::base::Split(c, ".");
std::string base;
if (parts.size() < 2) {
continue;
}
success &= parser.ParseConfigFile(c);
// parts[size()-1], aka the suffix, should be "rc" or "#rc"
// any other pattern gets discarded
const auto& suffix = parts[parts.size() - 1];
if (suffix == "rc") {
sdk = 0;
} else {
char trailer[9] = {0};
int r = sscanf(suffix.c_str(), "%d%8s", &sdk, trailer);
if (r != 2) {
continue;
}
if (strlen(trailer) > 2 || strcmp(trailer, "rc") != 0) {
continue;
}
}
if (sdk < 0 || sdk > active_sdk) {
continue;
}
base = parts[0];
for (unsigned int i = 1; i < parts.size() - 1; i++) {
base = base + "." + parts[i];
}
// is this preferred over what we already have
auto it = script_map.find(base);
if (it == script_map.end() || it->second.second < sdk) {
script_map[base] = std::make_pair(c, sdk);
}
}
bool success = true;
for (const auto& m : script_map) {
success &= parser.ParseConfigFile(m.second.first);
}
ServiceList::GetInstance().MarkServicesUpdate();
if (success) {