“This is the first day of my participation in the First Challenge 2022. For details: First Challenge 2022.”

Analytical tool analysis of system startup process analysis

In Android 11.0 system start source, before we have analyzed, in the system start, call SecondStageMain function to start Zygote and system services, and in this process, there is a very important link is to parse the configuration file, and to parse the configuration file, the first thing to build parsing tools.

static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
    // 1. Create a parsing tool
    Parser parser = CreateParser(action_manager, service_list);
    // 2. Obtain the corresponding parsing file
    std::string bootscript = GetProperty("ro.boot.init_rc"."");
    // If ro.boot.init_rc is undefined, the specified parse file is directly parsed
    if (bootscript.empty()) {
        parser.ParseConfig("/system/etc/init/hw/init.rc");
        if(! parser.ParseConfig("/system/etc/init")) {
            late_import_paths.emplace_back("/system/etc/init");
        }
        // late_import is available only in Q and earlier release. As we don't
        // have system_ext in those versions, skip late_import for system_ext.
        parser.ParseConfig("/system_ext/etc/init");
        if(! parser.ParseConfig("/vendor/etc/init")) {
            late_import_paths.emplace_back("/vendor/etc/init");
        }
        if(! parser.ParseConfig("/odm/etc/init")) {
            late_import_paths.emplace_back("/odm/etc/init");
        }
        if(! parser.ParseConfig("/product/etc/init")) {
            late_import_paths.emplace_back("/product/etc/init"); }}else {
         // If the ro.boot.init_rc property is defined, the configuration file it points to is parsed directlyparser.ParseConfig(bootscript); }}Copy the code

The LoadBootScripts function is used to parse the configuration file. The CreateParser function is used to create three different types of parsers.

Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
    Parser parser;
    parser.AddSectionParser("service".std::make_unique<ServiceParser>(
                                               &service_list, GetSubcontext(), std::nullopt));
    parser.AddSectionParser("on".std::make_unique<ActionParser>(&action_manager, GetSubcontext()));
    parser.AddSectionParser("import".std::make_unique<ImportParser>(&parser));
    return parser;
}
Copy the code

The CreateParser function has two parameters, the ActionManager object and the ServiceList object. The initialization of these two objects is described in the previous section. This function returns a Parser object, which we’ll examine later.

First, three parsers are added to the Parser object using the AddSectionParser function.

std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
void Parser::AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser) {
    section_parsers_[name] = std::move(parser);
}
Copy the code

The Parser object defines a map named section_parsers_, and then adds three parsers to the map. The corresponding keys are “service”, “on”, and “import”. Both inherit from the SectionParser class, which is very simple to define.

system/core/init/parser.h
class SectionParser {
  public:
    virtual ~SectionParser() {}
    virtual Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
                                      int line) = 0;
    virtual Result<void> ParseLineSection(std::vector<std::string>&&, int) { return {}; };
    virtual Result<void> EndSection() { return {}; };
    virtual void EndFile(){};
};
Copy the code

Next, let’s take a look at each of the three parsing tools

Entry to the Parser: the Parser

Initialize the

Parser::Parser() {}

Start parsing

Parser.parseconfig (bootscript) parser.parseconfig (bootscript) parser.parseconfig (bootscript) parser.parseconfig (bootscript) parser.parseconfig (bootscript) parser.parseconfig (bootscript) parser.parseconfig (bootscript) parser.parseconfig (bootscript) Here, because we need to parse/system/core/rootdir/init zygote32. Rc file, so the bootscript to/system/core/rootdir/init zygote32. Rc string

bool Parser::ParseConfig(const std::string& path) {
    if (is_dir(path.c_str())) {
        return ParseConfigDir(path);
    }
    return ParseConfigFile(path);
}
Copy the code

Check whether the current passed path is a folder. If it is a folder, then call ParseConfigDir to parse each configuration file in the folder. Here we passed a file, so ParseConfigFile

bool Parser::ParseConfigFile(const std::string& path) {
    LOG(INFO) << "Parsing file " << path << "...";
    android::base::Timer t;
    // 1. Read all the content in the configuration file from the file and save it as a string
    auto config_contents = ReadFile(path);
    if(! config_contents.ok()) { LOG(INFO) <<"Unable to read config file '" << path << "'." << config_contents.error();
        return false;
    }
    // 2. Call ParseData to parse all the configuration contents in the above configuration file
    ParseData(path, &config_contents.value());
    LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".) ";
    return true;
}
Copy the code

From the code above, there are two steps

Read all the content in the configuration file from the file and save it as a string. This part is not analyzed here

Second, the ParseData function is called to parse the contents of the corresponding configuration file

void Parser::ParseData(const std::string& filename, std::string* data) {
    // 1. Add the terminator to the end of the parsed data
    data->push_back('\n');
    data->push_back('\ 0');
    // Parse the status data
    parse_state state;
    state.line = 0;
    state.ptr = data->data();
    state.nexttoken = 0;
    SectionParser* section_parser = nullptr;
    int section_start_line = -1;
    std::vector<std::string> args;
 
    // If we encounter a bad section start, there is no valid parser object to parse the subsequent
    // sections, so we must suppress errors until the next valid section is found.
    bool bad_section_found = false;
    2. Define an end_section function
    // The function closes the configuration file after it has been parsed
    auto end_section = [&] {
        bad_section_found = false;
        if (section_parser == nullptr) return;
 
        if(auto result = section_parser->EndSection(); ! result.ok()) { parse_error_count_++; LOG(ERROR) << filename <<":" << section_start_line << ":" << result.error();
        }

        section_parser = nullptr;
        section_start_line = -1;
    };

    3.Infinite loop, parsing configuration datafor (;;) {
        switch (next_token(&state)) {
             // The configuration file is parsed
            case T_EOF:
                end_section();
                for (const auto& [section_name, section_parser] : section_parsers_) {
                    section_parser->EndFile();
                }
                return;
             // The configuration file parses a new row of data
            case T_NEWLINE: {
                // Verify the parsed line
                state.line++;
                if (args.empty()) break;
                // If we have a line matching a prefix we recognize, call its callback and unset any
                // current section parsers. This is meant for /sys/ and /dev/ line entries for
                // uevent.
                // Find the corresponding substring starting with args[0] from the string
                auto line_callback = std::find_if(
                    line_callbacks_.begin(), line_callbacks_.end(),
                    [&args](const auto& c) { return android::base::StartsWith(args[0], c.first); });
                if(line_callback ! = line_callbacks_.end()) {// end_section
                    end_section();
                    if(auto result = line_callback->second(std::move(args)); ! result.ok()) { parse_error_count_++; LOG(ERROR) << filename <<":" << state.line << ":"<< result.error(); }}else if (section_parsers_.count(args[0])) {
                    end_section();
                    // Get the parsing tool
                    section_parser = section_parsers_[args[0]].get();
                    section_start_line = state.line;
                    / / Section
                    if(auto result = section_parser->ParseSection(std::move(args), filename, state.line); ! result.ok()) { parse_error_count_++; LOG(ERROR) << filename <<":" << state.line << ":" << result.error();
                        section_parser = nullptr;
                        bad_section_found = true;
                    }
                // The Section Parser tool is found in the library
                } else if (section_parser) {
                    // Call the ParseLineSection of the parsing tool to parse the corresponding row
                    if(auto result = section_parser->ParseLineSection(std::move(args), state.line); ! result.ok()) { parse_error_count_++; LOG(ERROR) << filename <<":" << state.line << ":" << result.error();
                    }
                // else
                } else if(! bad_section_found) { parse_error_count_++; LOG(ERROR) << filename <<":" << state.line
                               << ": Invalid section keyword found";
                }
                args.clear();
                break;
            }
            // If it is TEXT, add it as an argument
            case T_TEXT:
                args.emplace_back(state.text);
                break; }}}Copy the code

As you can see from the above code, here

First, add an end character to the corresponding configuration data to prevent access to other data in memory

Second, the configuration file data is manipulated using the parse_state structure

struct parse_state
{
    char *ptr; // The PTR of state here points to the first character of configuration file data
    char *text; // Default data
    int line;   // Default is 0
    int nexttoken; // Default is 0
};
Copy the code

Third, we define a reference to the end_section function. The main purpose of this function is to call the EndSection function if section_parser is not empty and do some closing parsing.

Finally, the configuration file data is parsed bit by bit through a loop

Next, let’s start parsing the corresponding configuration file data

Inside the loop, is a switch statement whose body is the next_token function and whose argument is referenced by the parse_state object defined above

int next_token(struct parse_state *state)
{
    char *x = state->ptr;
    char *s;
 
    if (state->nexttoken) {
        int t = state->nexttoken;
        state->nexttoken = 0;
        return t;
    }
 
    for (;;) {
        switch (*x) {
        case 0:
            state->ptr = x;
            return T_EOF;
        case '\n':
            x++;
            state->ptr = x;
            return T_NEWLINE;
        case ' ':
        case '\t':
        case '\r':
            x++;
            continue;
        case The '#':
            while(*x && (*x ! ='\n')) x++;
            if (*x == '\n') {
                state->ptr = x+1;
                return T_NEWLINE;
            } else {
                state->ptr = x;
                return T_EOF;
            }
        default: goto text; }}textdone:
    state->ptr = x;
    *s = 0;
    return T_TEXT;
text:
    state->text = s = x;
textresume:
    for (;;) {
        switch (*x) {
        case 0:
            goto textdone;
        case ' ':
        case '\t':
        case '\r':
            x++;
            goto textdone;
        case '\n':
            state->nexttoken = T_NEWLINE;
            x++;
            goto textdone;
        case '"':
            x++;
            for (;;) {
                switch (*x) {
                case 0:
                    /* unterminated quoted thing */
                    state->ptr = x;
                    return T_EOF;
                case '"':
                    x++;
                    goto textresume;
                default: *s++ = *x++; }}break;
        case '\ \':
            x++;
            switch (*x) {
            case 0:
                goto textdone;
            case 'n':
                *s++ = '\n';
                x++;
                break;
            case 'r':
                *s++ = '\r';
                x++;
                break;
            case 't':
                *s++ = '\t';
                x++;
                break;
            case '\ \':
                *s++ = '\ \';
                x++;
                break;
            case '\r':
                /* \ <cr> <lf> -> line continuation */
                if (x[1] != '\n') {
                    x++;
                    continue;
                }
                x++;
                FALLTHROUGH_INTENDED;
            case '\n':
                /* \ <lf> -> line continuation */
                state->line++;
                x++;
                /* eat any extra whitespace */
                while((*x == ' ') || (*x == '\t')) x++;
                continue;
            default:
                /* unknown escape -- just copy */
                *s++ = *x++;
            }
            continue;
        default: *s++ = *x++; }}return T_EOF;
}
Copy the code

As you can see, the next_token function does the following

If the current state-> PTR refers to an uppercase or lowercase character, the character is saved to point to the next character and parsed until a non-alphabetic character is parsed. Finally, all parsed letters are referred to the head with state->text, and T_TEXT is returned

If it is currently newline ‘\n’, it indicates that the previous line has been parsed, points state-> PTR to the current position, and returns T_NEWLINE

If the current is a space, ‘\t’, ‘\r’, etc., parse the next character

If the current character is’ # ‘, the line is filtered. Of course, if the last character parsed is’ \n ‘, T_NEWLINE is returned. If not, the configuration file data has been parsed, T_EOF is returned

So, back to ParseData’s for loop

When the next_token function returns T_EOF, the configuration file has been parsed, and the endsections of all parsing tools (here are the three added by AddSectionParser) are called to end parsing

When the next_token function returns T_NEWLINE, it fetches all the data in the corresponding ARGS. Where does the data in the ARGS come from? Look at the following parsing

When the next_token function returns T_TEXT, the corresponding returned string is added to the ARGS string array

If ParseData is parsed to a string, it is added to the args string array. When parsed to a string, it is parsed to T_NEWLINE. If parsed to a configuration data, it is parsed to T_EOF

Next, let’s take a look at the specific parsing work of the following three parsing tools.

ServiceParser Parsing tool

Initialize the

From the above code, the initialization statement for ServiceParser is as follows

std::make_unique<ServiceParser>(&service_list, GetSubcontext(), std::nullopt)

The ServiceParser constructor is as follows

ServiceParser(ServiceList* service_list, Subcontext* subcontext,
            const std::optional<InterfaceInheritanceHierarchyMap>& interface_inheritance_hierarchy,
            bool from_apex = false)
        : service_list_(service_list),
          subcontext_(subcontext),
          interface_inheritance_hierarchy_(interface_inheritance_hierarchy),
          service_(nullptr),
          from_apex_(from_apex) {}
Copy the code

That is, the ServiceParser object

The reference to a ServiceList object named Service_list_ points to the ServiceList object you just created,

The Subcontext object named subContext_ refers to the previous Subcontext object,

The name is interface_inheritance_hierarchy_ nullopt,

The Service object named Service_ points to nullPtr

From_apex_ is set to false

Analytical process

If you need a configuration file to work with the ServiceParser, we’ll use the classic Zygote configuration file. Here we use init.zygote32.rc

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    priority- 20user root
    group root readproc reserved_disk
    socket zygote stream 660 root system
    socket usap_pool_primary stream 660 root system
    onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
    onrestart write /sys/power/state on
    onrestart restart audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart netd
    onrestart restart wificond
    task_profiles ProcessCapacityHigh
    critical window=${zygote.critical_window.minute:-off} target=zygote-fatal
Copy the code

The ParseData function of Parser shows that the configuration file is parsed line by line, so all the contents of the first line are read first, then the string is added to args when it is encountered, then the line is parsed and processed in T_NEWLINE, so we parsed line by line

The first line

Let’s look at the first line

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
Copy the code

After the above analysis, all strings will be added to the ARGS string array because they are initially composed of letters (including ‘/’ characters and ‘-‘)

That is, when the last ‘\n’ is encountered, the data in args is as follows

{" service ", "zygote", "/ system/bin/app_process", "- Xzygote", "/ system/bin", "zygote", "- start - system - server"}Copy the code

Next comes the newline character, which returns T_NEWLINE

case T_NEWLINE: {
    // Verify the number of parsed rows
    state.line++;
    // If args is empty, no parsing is required
    if (args.empty()) break;
    // If we have a line matching a prefix we recognize, call its callback and unset any
    // current section parsers. This is meant for /sys/ and /dev/ line entries for
    // uevent.
    // Find the corresponding substring starting with args[0] from the string
    // Line_callbacks are defined as line_callbacks
    auto line_callback = std::find_if(
        line_callbacks_.begin(), line_callbacks_.end(),
        [&args](const auto& c) { return android::base::StartsWith(args[0], c.first); });
    if(line_callback ! = line_callbacks_.end()) {// Close the section parsing tool
        end_section();
       
        if(auto result = line_callback->second(std::move(args)); ! result.ok()) { parse_error_count_++; LOG(ERROR) << filename <<":" << state.line << ":" << result.error();
        }
    // Use the args[0] string to determine whether the map contains the parsing tool for args[0]
    } else if (section_parsers_.count(args[0])) {
        end_section();
        // Get the parsing tool
        section_parser = section_parsers_[args[0]].get();
        section_start_line = state.line;
        / / Section
        if(auto result = section_parser->ParseSection(std::move(args), filename, state.line); ! result.ok()) { parse_error_count_++; LOG(ERROR) << filename <<":" << state.line << ":" << result.error();
            section_parser = nullptr;
            bad_section_found = true;
        }
    // If the current args[0] does not find the corresponding parsing tool, then check whether the parsing tool already exists
    } else if (section_parser) {
         // Call the ParseLineSection of the parsing tool to parse the corresponding row
        if(auto result = section_parser->ParseLineSection(std::move(args), state.line); ! result.ok()) { parse_error_count_++; LOG(ERROR) << filename <<":" << state.line << ":" << result.error();
        }
    // If none of the preceding conditions is met, logs cannot be parsed for the current row
    } else if(! bad_section_found) { parse_error_count_++; LOG(ERROR) << filename <<":" << state.line
                   << ": Invalid section keyword found";
}
// Finally empty the args for the next line to parse
    args.clear();
    break;
}
Copy the code

If args[0] is a string of “service”, section_parser_ map corresponds to ServiceParser, so section_parser is set to ServiceParser. The tool’s ParseSection function is then called

Result<void> ServiceParser::ParseSection(std::vector<std::string>&& args,
                                         const std::string& filename, int line) {
    // The number of arGs is 7, which does not meet the condition
    if (args.size() < 3) {
        return Error() < <"services must have a name and a program";
    }
    Zygote = zygote
    const std::string& name = args[1];
    // Check whether the name is valid
    if(! IsValidName(name)) {return Error() < <"invalid service name '" << name << "'";
    }
    // Set the file path
    filename_ = filename;
 
    Subcontext* restart_action_subcontext = nullptr;
    // Check whether the configuration file is resolved under /vendor or/ODM
    if (subcontext_ && subcontext_->PathMatchesSubcontext(filename)) {
        restart_action_subcontext = subcontext_;
    }
 
    // Get the last five parameters of args
    std::vector<std::string> str_args(args.begin() + 2, args.end()); .// Initialize a Service object named service_
    service_ = std::make_unique<Service>(name, restart_action_subcontext, str_args, from_apex_);
    return {};
}
Copy the code

As you can see from the above code, this code basically creates a Service object and initializes service_ to point to the object reference

Service::Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
                 const std::vector<std::string>& args, bool from_apex)
    : Service(name, 0.0.0, {}, 0."", subcontext_for_restart_commands, args, from_apex) {}

Service::Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
                 const std::vector<gid_t>& supp_gids, int namespace_flags,
                 const std::string& seclabel, Subcontext* subcontext_for_restart_commands,
                 const std::vector<std::string>& args, bool from_apex)
    : name_(name),
      classnames_({"default"}),
      flags_(flags),
      pid_(0),
      crash_count_(0),
      proc_attr_{.ioprio_class = IoSchedClass_NONE,
                 .ioprio_pri = 0,
                 .uid = uid,
                 .gid = gid,
                 .supp_gids = supp_gids,
                 .priority = 0},
      namespaces_{.flags = namespace_flags},
      seclabel_(seclabel),
      subcontext_(subcontext_for_restart_commands),
      onrestart_(false, subcontext_for_restart_commands, "<Service '" + name + "' onrestart>".0."onrestart", {}),
      oom_score_adjust_(DEFAULT_OOM_SCORE_ADJUST),
      start_order_(0),
      args_(args),
      from_apex_(from_apex) {}
Copy the code

According to its constructor, the main set here is its name_ to zygote and args_ to the last five parameter arrays read above, i.e

{"/system/bin/app_process ", "- Xzygote", "/ system/bin", "zygote", "- start - system - server"}Copy the code

The second line

Next, parse the second line

class main
Copy the code

From the above analysis, you can see that args contains two string arguments, “class” and “main”

T_NEWLINE is displayed. Since there is no parser for the class and Section_Parser is set to ServiceParser, call ParseLineSection of ServiceParser

Result<void> ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line) {
    // The previous line was created when it was parsed
    if(! service_) {return {};
    }

    // Get the parsing tool from GetParserMap (corresponding parsing method)
    auto parser = GetParserMap().Find(args);
    if(! parser.ok())return parser.error();
    // When the corresponding parsing method is obtained, call it
    return std::invoke(*parser, this.std::move(args));
}
Copy the code

Analyze the code above, get the corresponding parsing method and call it

const KeywordMap<ServiceParser::OptionParser>& ServiceParser::GetParserMap() const {
    constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
    // clang-format off
    static const KeywordMap<ServiceParser::OptionParser> parser_map = {
        {"capabilities",            {0,     kMax, &ServiceParser::ParseCapabilities}},
        {"class",                   {1,     kMax, &ServiceParser::ParseClass}},
        {"console",                 {0.1,    &ServiceParser::ParseConsole}},
        {"critical",                {0.2,    &ServiceParser::ParseCritical}},
        {"disabled",                {0.0,    &ServiceParser::ParseDisabled}},
        {"enter_namespace",         {2.2,    &ServiceParser::ParseEnterNamespace}},
        {"file",                    {2.2,    &ServiceParser::ParseFile}},
        {"group",                   {1,     NR_SVC_SUPP_GIDS + 1, &ServiceParser::ParseGroup}},
        {"interface",               {2.2,    &ServiceParser::ParseInterface}},
        {"ioprio",                  {2.2,    &ServiceParser::ParseIoprio}},
        {"keycodes",                {1,     kMax, &ServiceParser::ParseKeycodes}},
        {"memcg.limit_in_bytes",    {1.1,    &ServiceParser::ParseMemcgLimitInBytes}},
        {"memcg.limit_percent",     {1.1,    &ServiceParser::ParseMemcgLimitPercent}},
        {"memcg.limit_property",    {1.1,    &ServiceParser::ParseMemcgLimitProperty}},
        {"memcg.soft_limit_in_bytes",
                                    {1.1,    &ServiceParser::ParseMemcgSoftLimitInBytes}},
        {"memcg.swappiness",        {1.1,    &ServiceParser::ParseMemcgSwappiness}},
        {"namespace",               {1.2,    &ServiceParser::ParseNamespace}},
        {"oneshot",                 {0.0,    &ServiceParser::ParseOneshot}},
        {"onrestart",               {1,     kMax, &ServiceParser::ParseOnrestart}},
        {"oom_score_adjust",        {1.1,    &ServiceParser::ParseOomScoreAdjust}},
        {"override",                {0.0,    &ServiceParser::ParseOverride}},
        {"priority",                {1.1,    &ServiceParser::ParsePriority}},
        {"reboot_on_failure",       {1.1,    &ServiceParser::ParseRebootOnFailure}},
        {"restart_period",          {1.1,    &ServiceParser::ParseRestartPeriod}},
        {"rlimit",                  {3.3,    &ServiceParser::ParseProcessRlimit}},
        {"seclabel",                {1.1,    &ServiceParser::ParseSeclabel}},
        {"setenv",                  {2.2,    &ServiceParser::ParseSetenv}},
        {"shutdown",                {1.1,    &ServiceParser::ParseShutdown}},
        {"sigstop",                 {0.0,    &ServiceParser::ParseSigstop}},
        {"socket",                  {3.6,    &ServiceParser::ParseSocket}},
        {"stdio_to_kmsg",           {0.0,    &ServiceParser::ParseStdioToKmsg}},
        {"task_profiles",           {1,     kMax, &ServiceParser::ParseTaskProfiles}},
        {"timeout_period",          {1.1,    &ServiceParser::ParseTimeoutPeriod}},
        {"updatable",               {0.0,    &ServiceParser::ParseUpdatable}},
        {"user",                    {1.1,    &ServiceParser::ParseUser}},
        {"writepid",                {1,     kMax, &ServiceParser::ParseWritepid}},
    };
    // clang-format on
    return parser_map;
}
Copy the code

As you can see, the class string corresponding analytic function for ServiceParser: : ParseClass

Result<void> ServiceParser::ParseClass(std::vector<std::string>&& args) {
    service_->classnames_ = std::set<std::string>(args.begin() + 1, args.end());
    return {};
}
Copy the code

Set the classNames_ field referenced by Service_ to the second args parameter, main

The third line

And then the third row

priority -20
Copy the code

Based on the above analysis, the call ServiceParser: : ParsePriority function reference

Result<void> ServiceParser::ParsePriority(std::vector<std::string>&& args) {
    service_->proc_attr_.priority = 0;
    if(! ParseInt(args[1], &service_->proc_attr_.priority,
                  static_cast<int>(ANDROID_PRIORITY_HIGHEST),  // highest is negative
                  static_cast<int>(ANDROID_PRIORITY_LOWEST))) {
        return Errorf("process priority value must be range {} - {}", ANDROID_PRIORITY_HIGHEST,
                      ANDROID_PRIORITY_LOWEST);
    }
    return {};
}
Copy the code

Set service_->proc_attr_. Priority to -20, indicating the highest priority

In the fourth row

The following

user root
Copy the code

Based on the above analysis, call the &ServiceParser::ParseUser function

Result<void> ServiceParser::ParseUser(std::vector<std::string>&& args) {
    auto uid = DecodeUid(args[1]);
    if(! uid.ok()) {return Error() < <"Unable to find UID for '" << args[1] < <"'." << uid.error();
    }
    service_->proc_attr_.uid = *uid;
    return {};
}
Copy the code

Set service_->proc_attr_. Uid

The fifth row

The following

group root readproc reserved_disk
Copy the code

According to the previous analysis, this call ServiceParser: : ParseGroup

Result<void> ServiceParser::ParseGroup(std::vector<std::string>&& args) {
    / / gid
    auto gid = DecodeUid(args[1]);
    if(! gid.ok()) {return Error() < <"Unable to decode GID for '" << args[1] < <"'." << gid.error();
    }
    / / Settings service_ - > proc_attr_ gid
    service_->proc_attr_.gid = *gid;

    for (std::size_t n = 2; n < args.size(); n++) {
        gid = DecodeUid(args[n]);
        if(! gid.ok()) {return Error() < <"Unable to decode GID for '" << args[n] << "'." << gid.error();
        }
        // Parse the corresponding service_->proc_attr_. Supp_gids
        service_->proc_attr_.supp_gids.emplace_back(*gid);
    }
    return {};
}
Copy the code

As shown above, resolve the corresponding service_->proc_attr_. Gid and service_->proc_attr_. Supp_gids

Lines six through seven

The following

socket zygote stream 660 root system
socket usap_pool_primary stream 660 root system
Copy the code

The same socket at the beginning of fields, call ServiceParser: : ParseSocket

Result<void> ServiceParser::ParseSocket(std::vector<std::string>&& args) {
    SocketDescriptor socket;
    // Set the socket name
    socket.name = std::move(args[1]);

    // Set the socket type
    auto types = Split(args[2]."+");
    if (types[0] = ="stream") {
        socket.type = SOCK_STREAM;
    } else if (types[0] = ="dgram") {
        socket.type = SOCK_DGRAM;
    } else if (types[0] = ="seqpacket") {
        socket.type = SOCK_SEQPACKET;
    } else {
        return Error() < <"socket type must be 'dgram', 'stream' or 'seqpacket', got '" << types[0] < <"' instead.";
    }

    if (types.size() > 1) {
        if (types.size() == 2 && types[1] = ="passcred") {
            socket.passcred = true;
        } else {
            return Error() < <"Only 'passcred' may be used to modify the socket type";
        }
    }
    errno = 0;
    
    char* end = nullptr;
    // socket.perm
    socket.perm = strtol(args[3].c_str(), &end, 8);
    if(errno ! =0) {
        return ErrnoError() << "Unable to parse permissions '" << args[3] < <"'";
    }
    if (end == args[3].c_str() || *end ! ='\ 0') {
        errno = EINVAL;
        return ErrnoError() << "Unable to parse permissions '" << args[3] < <"'";
    }

    if (args.size() > 4) {
         / / parsing socket. The uid
        auto uid = DecodeUid(args[4]);
        if(! uid.ok()) {return Error() < <"Unable to find UID for '" << args[4] < <"'." << uid.error();
        }
        socket.uid = *uid;
    }

    if (args.size() > 5) {
         / / parsing socket. Gid
        auto gid = DecodeUid(args[5]);
        if(! gid.ok()) {return Error() < <"Unable to find GID for '" << args[5] < <"'." << gid.error();
        }
        socket.gid = *gid;
    }

    // There is no seventh parameter
    socket.context = args.size() > 6 ? args[6] : "";
    auto old = std::find_if(service_->sockets_.begin(), service_->sockets_.end(),
                            [&socket](const auto& other) { return socket.name == other.name; });
    if(old ! = service_->sockets_.end()) {return Error() < <"duplicate socket descriptor '" << socket.name << "'";
    }
    service_->sockets_.emplace_back(std::move(socket));
    return {};
}
Copy the code

Parsing service_ – > sockets_

Lines eight to fourteen

onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
onrestart restart wificond
Copy the code

By the foregoing can cause, call ServiceParser: : ParseOnrestart

Result<void> ServiceParser::ParseOnrestart(std::vector<std::string>&& args) {
    args.erase(args.begin());
    int line = service_->onrestart_.NumCommands() + 1;
        if(auto result = service_->onrestart_.AddCommand(std::move(args), line); ! result.ok()) {return Error() < <"cannot add Onrestart command: " << result.error();
    }
    return {};
}
Copy the code

Add the command line to service_-> onRestart_, where onrestart_ is an Action object

Result<void> Action::AddCommand(std::vector<std::string>&& args, int line) {
    if(! function_map_) {return Error() < <"no function map available";
    }

    auto map_result = function_map_->Find(args);
    if(! map_result.ok()) {return Error() << map_result.error();
    }
    commands_.emplace_back(map_result->function.map_result->run_in_subcontext.std: :move(args),
                           line);
    return {};
}
Copy the code

That is, add the corresponding action function to service_->onrestart_->commands_, which will be examined later

Line 15

task_profiles ProcessCapacityHigh
Copy the code

Similarly, call ServiceParser: : ParseTaskProfiles

Result<void> ServiceParser::ParseTaskProfiles(std::vector<std::string>&& args) {
    args.erase(args.begin());
    service_->task_profiles_ = std::move(args);
    return {};
}
Copy the code

Set the service_->task_profiles_ parameter

Line 16

critical window=${zygote.critical_window.minute:-off} target=zygote-fatal
Copy the code

Call ServiceParser: : ParseCritical

Result<void> ServiceParser::ParseCritical(std::vector<std::string>&& args) {
    std::optional<std::string> fatal_reboot_target;
    std::optional<std::chrono::minutes> fatal_crash_window;
 
    for (auto it = args.begin() + 1; it ! = args.end(); ++it) { auto arg = android::base::Split(*it,"=");
        if(arg.size() ! =2) {
            return Error() < <"critical: Argument '" << *it << "' is not supported";
         // The second argument is target
        } else if (arg[0] = ="target") {
           // Set fatal_reboot_target to zygote-fatal
            fatal_reboot_target = arg[1];
        Zygote. Critical_window. minute = off and set the property value
        } else if (arg[0] = ="window") {
            int minutes;
            auto window = ExpandProps(arg[1]);
            if (!window.ok()) {
                return Error() < <"critical: Could not expand argument ': " << arg[1];
            }
           // Return directly here
            if (*window= ="off") {
                return {};
            }
            if(! ParseInt(*window, &minutes, 0)) {
                return Error() < <"critical: 'fatal_crash_window' must be an integer > 0";
            }
            fatal_crash_window = std::chrono::minutes(minutes);
        } else {
            return Error() < <"critical: Argument '" << *it << "' is not supported"; }}if (fatal_reboot_target) {
        service_->fatal_reboot_target_ = *fatal_reboot_target;
    }
    if (fatal_crash_window) {
        service_->fatal_crash_window_ = *fatal_crash_window;
    }
    service_->flags_ |= SVC_CRITICAL;
    return {};
}
Copy the code

Set service_->fatal_reboot_target_ to “zygote-fatal”

The end of the

The call will return a T_EOF, which will call ServiceParser’s EndSection function (since ServiceParser doesn’t define EndFile, no need to analyze).

Result<void> ServiceParser::EndSection(){...// Check whether the corresponding Service object is already included in the list
    Service* old_service = service_list_->FindService(service_->name());
    if (old_service) {
        ......
    }
    // Save the newly created Service object into the ServiceManager with AddService
    service_list_->AddService(std::move(service_));
    return {};
}
Copy the code

As you can see, the Service object you just created will eventually be saved to the ServiceManager by AddService

void ServiceList::AddService(std::unique_ptr<Service> service) {
    services_.emplace_back(std::move(service));
}
Copy the code

It is stored in the Service_ array of the Service

conclusion

ServiceParser parses the configuration file into a Service object and adds it to the ServiceManager for management

ActionParser tool

Initialize the

Based on the foregoing, ActionParser is initialized as follows

Note that the corresponding ActionParser field in the map is “on”.

std::make_unique<ActionParser>(&action_manager, GetSubcontext())
Copy the code

Corresponding ActionParser constructor

ActionParser(ActionManager* action_manager, Subcontext* subcontext)
        : action_manager_(action_manager), subcontext_(subcontext), action_(nullptr) {}
Copy the code

As you can see, this is mainly about assigning values to parameters in the ActionParser object

Analytical process

Rc configuration file. Check the init.usb.rc configuration file. Here we only look at the previous data parsing

on post-fs-data
    chown system system /sys/class/android_usb/android0/f_mass_storage/lun/file
    chmod 0660 /sys/class/android_usb/android0/f_mass_storage/lun/file
    chown system system /sys/class/android_usb/android0/f_rndis/ethaddr
    chmod 0660 /sys/class/android_usb/android0/f_rndis/ethaddr
    mkdir /data/misc/adb 02750 system shell
    mkdir /data/adb 0700 root root encryption=Require
 
# adbd is controlled via property triggers in init.<platform>.usb.rc
service adbd /system/bin/adbd --root_seclabel=u:r:su:s0
    class core
    socket adbd seqpacket 660 system system
    disabled
    updatable
    seclabel u:r:adbd:s0

on property:vendor.sys.usb.adb.disabled=*
    setprop sys.usb.adb.disabled ${vendor.sys.usb.adb.disabled}

# Set default value on sys.usb.configfs early in boot sequence. It will be
# overridden in `on boot` action of init.hardware.rc.
on init
setprop sys.usb.configfs 0.Copy the code

According to previous analysis, Parser parses line by line in the process of parsing, and stores the corresponding string into args string array when encountering letters and characters

The first line

on post-fs-data
Copy the code

According to the previous analysis, when the Parser is parsed to this line, the above two strings are put into the args string array respectively, and then the first string “on” is used to find the Parser object here and call its ParseSection function

Result<void> ActionParser::ParseSection(std::vector<std::string>&& args,
                                        const std::string& filename, int line) {
    // Place the ARgs data into the triggers string array. There is only one element in triggers, post-fs-data, that is resolved here
    std::vector<std::string> triggers(args.begin() + 1, args.end());
    if (triggers.size() < 1) {
        return Error() < <"Actions must have a trigger";
    }
 
    Subcontext* action_subcontext = nullptr;
    // Check whether the current file address exists in /vendor or /odm, and action_subcontext is nullptr
    if (subcontext_ && subcontext_->PathMatchesSubcontext(filename)) {
        action_subcontext = subcontext_;
    }
    std::string event_trigger;
    std::map<std::string, std::string> property_triggers;
    // Invoke the ParseTriggers function
    if(auto result = ParseTriggers(triggers, action_subcontext, &event_trigger, &property_triggers); ! result.ok()) {return Error() < <"ParseTriggers() failed: " << result.error();
    }
    // Initialize an Action object
    auto action = std::make_unique<Action>(false, action_subcontext, filename, line, event_trigger,
                                           property_triggers);
    // Finally set action_ to the Action you just created
    action_ = std::move(action);
    return {};
}
Copy the code

As you can see from the code above, over here

The ParseTriggers function of ActionParser is called first

Result<void> ParseTriggers(const std::vector<std::string>& args, Subcontext* subcontext,
                           std::string* event_trigger,
                           std::map<std::string, std::string>* property_triggers) {
    const static std::string prop_str("property:");
    // The for loop looks at each passed argument
    for (std::size_t i = 0; i < args.size(); ++i) {
        // Check whether it is null
        if (args[i].empty()) {
            return Error() < <"empty trigger is not valid";
        }
        // Check whether there are two arguments
        if (i % 2) {
            if(args[i] ! ="&") {
                return Error() < <"&& is the only symbol allowed to concatenate actions";
            } else {
                continue; }}// Check whether the parameter is property: the data at the beginning
        if(! args[i].compare(0, prop_str.length(), prop_str)) {
            if(auto result = ParsePropertyTrigger(args[i], subcontext, property_triggers); ! result.ok()) {returnresult; }}else {
            if(! event_trigger->empty()) {return Error() < <"multiple event triggers are not allowed";
            }
            // Determine if the passed parameter is a valid event trigger
            if(auto result = ValidateEventTrigger(args[i]); ! result.ok()) {return result;
            }
            // Save the event trigger*event_trigger = args[i]; }}return {};
}
Copy the code

In this case, the main thing is to make event_trigger point to the data represented by args[0], i.e., post-fs-data

Next, we initialize an Action object

Action::Action(bool oneshot, Subcontext* subcontext, const std::string& filename, int line,
               const std::string& event_trigger,
               const std::map<std::string, std::string>& property_triggers)
    : property_triggers_(property_triggers),
      event_trigger_(event_trigger),
      oneshot_(oneshot),
      subcontext_(subcontext),
      filename_(filename),
      line_(line) {}
Copy the code

From the perspective of the parameters passed in, we assign values to the Action parameters

Oneshot_ is assigned to false

Event_trigger_ assigns the address of the string just passed in, and its value is post-Fs-data

Subcontext_ assigns to the Subcontext object initialized in the previous article

Filename_ is the address of the configuration file

Line_ indicates what line this is

The second line

chown system system /sys/class/android_usb/android0/f_mass_storage/lun/file
Copy the code

The section_parser object is an ActionParser object, so call the ParseLineSection function of ActionParser. Its arguments are the strings described above

Result<void> ActionParser::ParseLineSection(std::vector<std::string>&& args, int line) {
    return action_ ? action_->AddCommand(std::move(args), line) : Result<void> {}; }Copy the code

Action_ we just created, so call it AddCommand

Result<void> Action::AddCommand(std::vector<std::string>&& args, int line) {
    if(! function_map_) {return Error() < <"no function map available";
    }
    // Find the function corresponding to the parameter in function_map_
    auto map_result = function_map_->Find(args);
    // If no corresponding parsing function is found, exit
    if(! map_result.ok()) {return Error() << map_result.error();
    }
    // Store the corresponding functions and parameters in the corresponding command line array
    commands_.emplace_back(map_result->function.map_result->run_in_subcontext.std: :move(args),
                           line);
    return {};
}
Copy the code

What is function_map? This is set during system startup via Action::set_function_map, which we’ll revisit

// Builtin-function-map start
const BuiltinFunctionMap& GetBuiltinFunctionMap() {
    constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
    // clang-format off
    static const BuiltinFunctionMap builtin_functions = {
        {"bootchart",               {1.1,    {false,  do_bootchart}}},
        {"chmod",                   {2.2,    {true,   do_chmod}}},
        {"chown",                   {2.3,    {true,   do_chown}}},
        {"class_reset",             {1.1,    {false,  do_class_reset}}},
        {"class_reset_post_data",   {1.1,    {false,  do_class_reset_post_data}}},
        {"class_restart",           {1.1,    {false,  do_class_restart}}},
        {"class_start",             {1.1,    {false,  do_class_start}}},
        {"class_start_post_data",   {1.1,    {false,  do_class_start_post_data}}},
        {"class_stop",              {1.1,    {false,  do_class_stop}}},
        {"copy",                    {2.2,    {true,   do_copy}}},
        {"copy_per_line",           {2.2,    {true,   do_copy_per_line}}},
        {"domainname",              {1.1,    {true,   do_domainname}}},
        {"enable",                  {1.1,    {false,  do_enable}}},
        {"exec",                    {1,     kMax, {false,  do_exec}}},
        {"exec_background",         {1,     kMax, {false,  do_exec_background}}},
        {"exec_start",              {1.1,    {false,  do_exec_start}}},
        {"export",                  {2.2,    {false,  do_export}}},
        {"hostname",                {1.1,    {true,   do_hostname}}},
        {"ifup",                    {1.1,    {true,   do_ifup}}},
        {"init_user0",              {0.0,    {false,  do_init_user0}}},
        {"insmod",                  {1,     kMax, {true,   do_insmod}}},
        {"installkey",              {1.1,    {false,  do_installkey}}},
        {"interface_restart",       {1.1,    {false,  do_interface_restart}}},
        {"interface_start",         {1.1,    {false,  do_interface_start}}},
        {"interface_stop",          {1.1,    {false,  do_interface_stop}}},
        {"load_exports",            {1.1,    {false,  do_load_exports}}},
        {"load_persist_props",      {0.0,    {false,  do_load_persist_props}}},
        {"load_system_props",       {0.0,    {false,  do_load_system_props}}},
        {"loglevel",                {1.1,    {false,  do_loglevel}}},
        {"mark_post_data",          {0.0,    {false,  do_mark_post_data}}},
        {"mkdir",                   {1.6,    {true,   do_mkdir}}},
        // TODO: Do mount operations in vendor_init.
        // mount_all is currently too complex to run in vendor_init as it queues action triggers,
        // imports rc scripts, etc. It should be simplified and run in vendor_init context.
        // mount and umount are run in the same context as mount_all for symmetry.
        {"mount_all",               {0,     kMax, {false,  do_mount_all}}},
        {"mount",                   {3,     kMax, {false,  do_mount}}},
        {"perform_apex_config",     {0.0,    {false,  do_perform_apex_config}}},
        {"umount",                  {1.1,    {false,  do_umount}}},
        {"umount_all",              {0.1,    {false,  do_umount_all}}},
        {"update_linker_config",    {0.0,    {false,  do_update_linker_config}}},
        {"readahead",               {1.2,    {true,   do_readahead}}},
        {"remount_userdata",        {0.0,    {false,  do_remount_userdata}}},
        {"restart",                 {1.1,    {false,  do_restart}}},
        {"restorecon",              {1,     kMax, {true,   do_restorecon}}},
        {"restorecon_recursive",    {1,     kMax, {true,   do_restorecon_recursive}}},
        {"rm",                      {1.1,    {true,   do_rm}}},
        {"rmdir",                   {1.1,    {true,   do_rmdir}}},
        {"setprop",                 {2.2,    {true,   do_setprop}}},
        {"setrlimit",               {3.3,    {false,  do_setrlimit}}},
        {"start",                   {1.1,    {false,  do_start}}},
        {"stop",                    {1.1,    {false,  do_stop}}},
        {"swapon_all",              {0.1,    {false,  do_swapon_all}}},
        {"enter_default_mount_ns",  {0.0,    {false,  do_enter_default_mount_ns}}},
        {"symlink",                 {2.2,    {true,   do_symlink}}},
        {"sysclktz",                {1.1,    {false,  do_sysclktz}}},
        {"trigger",                 {1.1,    {false,  do_trigger}}},
        {"verity_update_state",     {0.0,    {false,  do_verity_update_state}}},
        {"wait",                    {1.2,    {true,   do_wait}}},
        {"wait_for_prop",           {2.2,    {false,  do_wait_for_prop}}},
        {"write",                   {2.2,    {true,   do_write}}},
    };
    // clang-format on
    return builtin_functions;
}
Copy the code

From this map, we can find the corresponding chown function is do_chown function, we do not do too much parsing here, according to the name, this is to change the file permissions

Lines three to seven

As you can see from the parsing of the second line above, commands are added to the Commands_ command line array of Aciton objects

Lines 10 to 15

Based on ServiceParser parsing, a Service object is created and added to the ServiceManager for management

However, since ActionParser has not encountered T_EOF before, how is the corresponding Action object saved?

So this is using the end_section function that we’ve looked at before, which we’ve looked at before, mainly when we run into T_EOF or when we run into the next SectionParser, and in this function, This is done by calling the corresponding section_parser EndSection function. For init.usb.rc, ActionParser::EndSection will be called until the next ServiceParser is encountered

Result<void> ActionParser::EndSection() {
    if (action_ && action_->NumCommands() > 0) {
        action_manager_->AddAction(std::move(action_));
    }
    return {};
}
Copy the code

That is, the Action object created in the first line is added to the ActionManager for management

void ActionManager::AddAction(std::unique_ptr<Action> action) {
    actions_.emplace_back(std::move(action));
}
Copy the code

As you can see, the Action object is added to the Action_ parameter array of the ActionManager

Lines 17 through 18

on property:vendor.sys.usb.adb.disabled=*
    setprop sys.usb.adb.disabled ${vendor.sys.usb.adb.disabled}
Copy the code

The difference here from the Action parsing on the first line is in ParserTriggers, where the ParsePropertyTrigger function is called because the args being parsed starts with property:

Result<void> ParsePropertyTrigger(const std::string& trigger, Subcontext* subcontext,
                                  std::map<std::string, std::string>* property_triggers) {
    const static std::string prop_str("property:");
    // Get proterty: the following string
    std::string prop_name(trigger.substr(prop_str.length()));
    // Check whether the string contains the '=' character
    size_t equal_pos = prop_name.find('=');
    if (equal_pos == std::string::npos) {
        return Error() < <"property trigger found without matching '='";
    }
    / / name resolution attribute value for the vendor. The sys. Usb. The adb. The disabled, the attribute value is *
    std::string prop_value(prop_name.substr(equal_pos + 1));
    prop_name.erase(equal_pos);
    // Since the attribute value name begins with vendor, the function returns true here
    if(! IsActionableProperty(subcontext, prop_name)) {return Error() < <"unexported property trigger found: " << prop_name;
    }
    // Save the paired properties into property_triggers
        if(auto [it, inserted] = property_triggers->emplace(prop_name, prop_value); ! inserted) {return Error() < <"multiple property triggers found for same property";
    }
    return {};
}
Copy the code

The Action object is then created to indicate that the Action item is triggered as a property, and the next line is parsed to add the command line for the Action item, and finally, as above, to the Action_action item array of the ActionManager

conclusion

Firing the ActionParser tool parses an Action object that contains multiple commands and is eventually added to the ActionManager

ImportParser Parsing tool

Initialize the

std::make_unique<ImportParser>(&parser)
Copy the code

Initialize an ImportParser object directly

ImportParser(Parser* parser) : parser_(parser) {}
Copy the code

Analytical process

Let’s look at this using init.rc

import /init.environ.rc
import /system/etc/init/hw/init.usb.rc
import /init.${ro.hardware}.rc
Copy the code

In the first line of parsing, the ImportParser ParserSection function is triggered

Result<void> ImportParser::ParseSection(std::vector<std::string>&& args,
                                        const std::string& filename, int line) {
    if(args.size() ! =2) {
        return Error() < <"single argument needed for import\n";
    }
    // Parse the second argument
    auto conf_file = ExpandProps(args[1]);
    if(! conf_file.ok()) {return Error() < <"Could not expand import: " << conf_file.error();
    }
    LOG(INFO) << "Added '" << *conf_file << "' to import list";
    if (filename_.empty()) filename_ = filename;
    // Add to imports_
    imports_.emplace_back(std::move(*conf_file), line);
    return {};
}
Copy the code

The first parameter is parsed

Result<std::string> ExpandProps(const std::string& src) {
    const char* src_ptr = src.c_str();
    std::string dst;
    /* - variables can either be $x.y or ${x.y}, in case they are only part * of the string. * - will accept $$ as a literal $. * - no nested property expansion, i.e. ${foo.${bar}} is not supported, * bad things will happen * - ${x.y:-default} will return default value if property empty. */
    while (*src_ptr) {
        const char* c;
        c = strchr(src_ptr, '$');
        if(! c) { dst.append(src_ptr);return dst;
        }
        dst.append(src_ptr, c);
        c++;
        if (*c == '$') {
            dst.push_back(*(c++));
            src_ptr = c;
            continue;
        } else if (*c == '\ 0') {
            return dst;
        }
        std::string prop_name;
        std::string def_val;
        if (*c == '{') {
            c++;
            const char* end = strchr(c, '} ');
            if(! end) {// failed to find closing brace, abort.
                return Error() < <"unexpected end of string in '" << src << "', looking for }";
            }
            prop_name = std::string(c, end);
            c = end + 1;
            size_t def = prop_name.find(: "-");
            if (def < prop_name.size()) {
                def_val = prop_name.substr(def + 2);
                prop_name = prop_name.substr(0, def); }}else {
            prop_name = c;
            if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__) {
                return Error() < <"using deprecated syntax for specifying property '" << c
                               << "', use ${name} instead";
            } else {
                LOG(ERROR) << "using deprecated syntax for specifying property '" << c
                           << "', use ${name} instead";
            }
            c += prop_name.size();
        }
        if (prop_name.empty()) {
            return Error() < <"invalid zero-length property name in '" << src << "'";
        }
        std::string prop_val = android::base::GetProperty(prop_name, "");
        if (prop_val.empty()) {
            if (def_val.empty()) {
                return Error() < <"property '" << prop_name << "' doesn't exist while expanding '"
                               << src << "'";
            }
            prop_val = def_val;
        }
        dst.append(prop_val);
        src_ptr = c;
    }
    return dst;
}
Copy the code

The value of the property name contained in the address of the second parameter file is parsed and replaced into the string. After that, all configuration data starting with import is saved into the import_ array

So where does the parsing take place? Remember when the configuration file is parsed, T_EOF is reported to the Parser? The EndFile function of all configuration file parsing tools is called

void ImportParser::EndFile() {
    auto current_imports = std::move(imports_);
    imports_.clear();
    for (const auto& [import, line_num] : current_imports) {
        parser_->ParseConfig(import); }}Copy the code

Then parses all imports_ containing configuration file addresses using ParseConfig of Parser

In init.rc, the import line is added at the beginning of the file, but it is not parsed until the configuration file is parsed

conclusion

ImportParser is a parsing tool that adds and imports other parsing files and parses them together

Summary of parsing tools

From the analysis of the three parsing tools above, we know that at the end of parsing the configuration files, the configuration files are changed to

  1. The Service object is added to the Service_ Service array of the ServiceList for unified management
  2. The Action object, added to the ActionManager actions_ Action items array for unified management, also contains command lines stored in the Commands_ array
  3. The ImportParser utility introduces a new configuration file