action.cpp revision 331cf2fb7c16b5b25064f8d2f00284105a9b413f
1fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry/*
2fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry * Copyright (C) 2015 The Android Open Source Project
3fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry *
4fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry * Licensed under the Apache License, Version 2.0 (the "License");
5fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry * you may not use this file except in compliance with the License.
6fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry * You may obtain a copy of the License at
7fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry *
8fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry *      http://www.apache.org/licenses/LICENSE-2.0
9fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry *
10fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry * Unless required by applicable law or agreed to in writing, software
11fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry * distributed under the License is distributed on an "AS IS" BASIS,
12fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry * See the License for the specific language governing permissions and
14fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry * limitations under the License.
15fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry */
16fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
17fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry#include "action.h"
18fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
19fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry#include <errno.h>
20fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
214f71319df011d796a60a43fc1bc68e16fbf7d321Elliott Hughes#include <android-base/strings.h>
224f71319df011d796a60a43fc1bc68e16fbf7d321Elliott Hughes#include <android-base/stringprintf.h>
23fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
24b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry#include "builtins.h"
25fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry#include "error.h"
26fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry#include "init_parser.h"
27fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry#include "log.h"
28fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry#include "property_service.h"
29fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry#include "util.h"
30fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
31b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryusing android::base::Join;
32b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryusing android::base::StringPrintf;
33fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
34b7349902a945903f9e36a569051f5131beb0bc24Tom CherryCommand::Command(BuiltinFunction f, const std::vector<std::string>& args,
35b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                 const std::string& filename, int line)
36b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    : func_(f), args_(args), filename_(filename), line_(line) {
37fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
38fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
39b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryint Command::InvokeFunc() const {
4096f67316a22bc9236aed70b198e91a5406389e5bTom Cherry    std::vector<std::string> expanded_args;
4196f67316a22bc9236aed70b198e91a5406389e5bTom Cherry    expanded_args.resize(args_.size());
4296f67316a22bc9236aed70b198e91a5406389e5bTom Cherry    expanded_args[0] = args_[0];
43fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    for (std::size_t i = 1; i < args_.size(); ++i) {
44b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        if (!expand_props(args_[i], &expanded_args[i])) {
45f86b5a6b90619e02d1d034ef7b0adc3b439f4abbElliott Hughes            LOG(ERROR) << args_[0] << ": cannot expand '" << args_[i] << "'";
46fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            return -EINVAL;
47fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        }
48fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
49fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
5096f67316a22bc9236aed70b198e91a5406389e5bTom Cherry    return func_(expanded_args);
51fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
52fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
53b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrystd::string Command::BuildCommandString() const {
54b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    return Join(args_, ' ');
55fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
56fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
57b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrystd::string Command::BuildSourceString() const {
58fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    if (!filename_.empty()) {
59b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        return StringPrintf(" (%s:%d)", filename_.c_str(), line_);
60fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    } else {
61fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        return std::string();
62fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
63fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
64fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
65b7349902a945903f9e36a569051f5131beb0bc24Tom CherryAction::Action(bool oneshot) : oneshot_(oneshot) {
66fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
67fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
68b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryconst KeywordMap<BuiltinFunction>* Action::function_map_ = nullptr;
69b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry
70b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool Action::AddCommand(const std::vector<std::string>& args,
71b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                        const std::string& filename, int line, std::string* err) {
72b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    if (!function_map_) {
73b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        *err = "no function map available";
74b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        return false;
75b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    }
76b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry
77b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    if (args.empty()) {
78b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        *err = "command needed, but not provided";
79b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        return false;
80b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    }
81b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry
82b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    auto function = function_map_->FindFunction(args[0], args.size() - 1, err);
83b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    if (!function) {
84b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        return false;
85b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    }
86b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry
87b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    AddCommand(function, args, filename, line);
88b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    return true;
89b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry}
90b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry
91b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid Action::AddCommand(BuiltinFunction f,
92fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                        const std::vector<std::string>& args,
93b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                        const std::string& filename, int line) {
94b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    commands_.emplace_back(f, args, filename, line);
95b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry}
96b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry
97b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid Action::CombineAction(const Action& action) {
98b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    for (const auto& c : action.commands_) {
99b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        commands_.emplace_back(c);
100b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    }
101fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
102fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
103b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrystd::size_t Action::NumCommands() const {
104fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    return commands_.size();
105fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
106fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
107b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid Action::ExecuteOneCommand(std::size_t command) const {
108d67a4abc647d5ed7235ff7ee1695b31340e63a1cWei Wang    // We need a copy here since some Command execution may result in
109d67a4abc647d5ed7235ff7ee1695b31340e63a1cWei Wang    // changing commands_ vector by importing .rc files through parser
110d67a4abc647d5ed7235ff7ee1695b31340e63a1cWei Wang    Command cmd = commands_[command];
111d67a4abc647d5ed7235ff7ee1695b31340e63a1cWei Wang    ExecuteCommand(cmd);
112fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
113fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
114b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid Action::ExecuteAllCommands() const {
115fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    for (const auto& c : commands_) {
116b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        ExecuteCommand(c);
117fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
118fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
119fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
120b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid Action::ExecuteCommand(const Command& command) const {
121fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    Timer t;
122fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    int result = command.InvokeFunc();
123fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
124331cf2fb7c16b5b25064f8d2f00284105a9b413fElliott Hughes    double duration_ms = t.duration_s() * 1000;
1258b1d526a72c1e6705b03f4b3267ee02fe84ce765Wei Wang    // Any action longer than 50ms will be warned to user as slow operation
1268b1d526a72c1e6705b03f4b3267ee02fe84ce765Wei Wang    if (duration_ms > 50.0 ||
1278b1d526a72c1e6705b03f4b3267ee02fe84ce765Wei Wang        android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
128fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        std::string trigger_name = BuildTriggersString();
129fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        std::string cmd_str = command.BuildCommandString();
130fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        std::string source = command.BuildSourceString();
131fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
132f86b5a6b90619e02d1d034ef7b0adc3b439f4abbElliott Hughes        LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << source
1338b1d526a72c1e6705b03f4b3267ee02fe84ce765Wei Wang                  << " returned " << result << " took " << duration_ms << "ms.";
134fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
135fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
136fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
137b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool Action::ParsePropertyTrigger(const std::string& trigger, std::string* err) {
138fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    const static std::string prop_str("property:");
139fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    std::string prop_name(trigger.substr(prop_str.length()));
140fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    size_t equal_pos = prop_name.find('=');
141fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    if (equal_pos == std::string::npos) {
142fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        *err = "property trigger found without matching '='";
143fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        return false;
144fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
145fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
146fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    std::string prop_value(prop_name.substr(equal_pos + 1));
147fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    prop_name.erase(equal_pos);
148fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
149fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    auto res = property_triggers_.emplace(prop_name, prop_value);
150fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    if (res.second == false) {
151fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        *err = "multiple property triggers found for same property";
152fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        return false;
153fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
154fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    return true;
155fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
156fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
157b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool Action::InitTriggers(const std::vector<std::string>& args, std::string* err) {
158fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    const static std::string prop_str("property:");
159fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    for (std::size_t i = 0; i < args.size(); ++i) {
16093df4e18a255262595acb862ab870e9fed721fb8Wei Wang        if (args[i].empty()) {
16193df4e18a255262595acb862ab870e9fed721fb8Wei Wang            *err = "empty trigger is not valid";
16293df4e18a255262595acb862ab870e9fed721fb8Wei Wang            return false;
16393df4e18a255262595acb862ab870e9fed721fb8Wei Wang        }
16493df4e18a255262595acb862ab870e9fed721fb8Wei Wang
165fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        if (i % 2) {
166cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry            if (args[i] != "&&") {
167fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                *err = "&& is the only symbol allowed to concatenate actions";
168fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                return false;
169fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            } else {
170fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                continue;
171fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            }
172fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        }
173fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
174fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        if (!args[i].compare(0, prop_str.length(), prop_str)) {
175fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            if (!ParsePropertyTrigger(args[i], err)) {
176fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                return false;
177fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            }
178fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        } else {
179fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            if (!event_trigger_.empty()) {
180fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                *err = "multiple event triggers are not allowed";
181fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                return false;
182fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            }
183fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
184fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            event_trigger_ = args[i];
185fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        }
186fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
187fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
188fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    return true;
189fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
190fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
191b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool Action::InitSingleTrigger(const std::string& trigger) {
192fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    std::vector<std::string> name_vector{trigger};
193fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    std::string err;
19493df4e18a255262595acb862ab870e9fed721fb8Wei Wang    bool ret = InitTriggers(name_vector, &err);
19593df4e18a255262595acb862ab870e9fed721fb8Wei Wang    if (!ret) {
19693df4e18a255262595acb862ab870e9fed721fb8Wei Wang        LOG(ERROR) << "InitSingleTrigger failed due to: " << err;
19793df4e18a255262595acb862ab870e9fed721fb8Wei Wang    }
19893df4e18a255262595acb862ab870e9fed721fb8Wei Wang    return ret;
199fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
200fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
201b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry// This function checks that all property triggers are satisfied, that is
202b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry// for each (name, value) in property_triggers_, check that the current
203b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry// value of the property 'name' == value.
204b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry//
205b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry// It takes an optional (name, value) pair, which if provided must
206b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry// be present in property_triggers_; it skips the check of the current
207b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry// property value for this pair.
208fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherrybool Action::CheckPropertyTriggers(const std::string& name,
209b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                                   const std::string& value) const {
210fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    if (property_triggers_.empty()) {
211fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        return true;
212fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
213fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
214b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    bool found = name.empty();
215fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    for (const auto& t : property_triggers_) {
216cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry        const auto& trigger_name = t.first;
217cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry        const auto& trigger_value = t.second;
218cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry        if (trigger_name == name) {
219cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry            if (trigger_value != "*" && trigger_value != value) {
220fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                return false;
221fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            } else {
222fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                found = true;
223fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            }
224fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        } else {
225cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry            std::string prop_val = property_get(trigger_name.c_str());
226cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry            if (prop_val.empty() || (trigger_value != "*" &&
227cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry                                     trigger_value != prop_val)) {
228fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                return false;
229fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            }
230fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        }
231fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
232fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    return found;
233fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
234fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
235b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool Action::CheckEventTrigger(const std::string& trigger) const {
236fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    return !event_trigger_.empty() &&
237cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry        trigger == event_trigger_ &&
238fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        CheckPropertyTriggers();
239fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
240fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
241fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherrybool Action::CheckPropertyTrigger(const std::string& name,
242b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                                  const std::string& value) const {
243fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    return event_trigger_.empty() && CheckPropertyTriggers(name, value);
244fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
245fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
246b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool Action::TriggersEqual(const Action& other) const {
247cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    return property_triggers_ == other.property_triggers_ &&
248cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry        event_trigger_ == other.event_trigger_;
249fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
250fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
251b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrystd::string Action::BuildTriggersString() const {
252fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    std::string result;
253fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
254fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    for (const auto& t : property_triggers_) {
255fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        result += t.first;
256fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        result += '=';
257fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        result += t.second;
258fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        result += ' ';
259fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
260fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    if (!event_trigger_.empty()) {
261fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        result += event_trigger_;
262fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        result += ' ';
263fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
26493df4e18a255262595acb862ab870e9fed721fb8Wei Wang    result.pop_back();
265fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    return result;
266fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
267fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
268b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid Action::DumpState() const {
269fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    std::string trigger_name = BuildTriggersString();
270f86b5a6b90619e02d1d034ef7b0adc3b439f4abbElliott Hughes    LOG(INFO) << "on " << trigger_name;
271fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
272fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    for (const auto& c : commands_) {
273b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        std::string cmd_str = c.BuildCommandString();
274f86b5a6b90619e02d1d034ef7b0adc3b439f4abbElliott Hughes        LOG(INFO) << "  %s" << cmd_str;
275fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
276fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
277fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
278cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherryclass EventTrigger : public Trigger {
279cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherrypublic:
2801c563d96f000876d77b2d33fbfb03c241bc503e1Chih-Hung Hsieh    explicit EventTrigger(const std::string& trigger) : trigger_(trigger) {
281cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    }
282b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    bool CheckTriggers(const Action& action) const override {
283b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        return action.CheckEventTrigger(trigger_);
284cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    }
285cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherryprivate:
286b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    const std::string trigger_;
287cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry};
288cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry
289cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherryclass PropertyTrigger : public Trigger {
290cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherrypublic:
291cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    PropertyTrigger(const std::string& name, const std::string& value)
292cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry        : name_(name), value_(value) {
293cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    }
294b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    bool CheckTriggers(const Action& action) const override {
295b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        return action.CheckPropertyTrigger(name_, value_);
296cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    }
297cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherryprivate:
298b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    const std::string name_;
299b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    const std::string value_;
300cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry};
301cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry
302cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherryclass BuiltinTrigger : public Trigger {
303cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherrypublic:
3041c563d96f000876d77b2d33fbfb03c241bc503e1Chih-Hung Hsieh    explicit BuiltinTrigger(Action* action) : action_(action) {
305cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    }
306b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    bool CheckTriggers(const Action& action) const override {
307b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        return action_ == &action;
308cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    }
309cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherryprivate:
310b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    const Action* action_;
311cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry};
312cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry
313b7349902a945903f9e36a569051f5131beb0bc24Tom CherryActionManager::ActionManager() : current_command_(0) {
314fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
315fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
316fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom CherryActionManager& ActionManager::GetInstance() {
317fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    static ActionManager instance;
318fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    return instance;
319fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
320fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
321b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid ActionManager::AddAction(std::unique_ptr<Action> action) {
322b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    auto old_action_it =
323b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        std::find_if(actions_.begin(), actions_.end(),
324b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                     [&action] (std::unique_ptr<Action>& a) {
325b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                         return action->TriggersEqual(*a);
326b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                     });
327b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry
328b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    if (old_action_it != actions_.end()) {
329b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        (*old_action_it)->CombineAction(*action);
330b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    } else {
331b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        actions_.emplace_back(std::move(action));
332b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    }
333b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry}
334b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry
335b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid ActionManager::QueueEventTrigger(const std::string& trigger) {
336cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    trigger_queue_.push(std::make_unique<EventTrigger>(trigger));
337fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
338fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
339fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherryvoid ActionManager::QueuePropertyTrigger(const std::string& name,
340b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                                         const std::string& value) {
341cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    trigger_queue_.push(std::make_unique<PropertyTrigger>(name, value));
342fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
343fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
344b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid ActionManager::QueueAllPropertyTriggers() {
345fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    QueuePropertyTrigger("", "");
346fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
347fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
348b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid ActionManager::QueueBuiltinAction(BuiltinFunction func,
349b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                                       const std::string& name) {
350b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    auto action = std::make_unique<Action>(true);
351fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    std::vector<std::string> name_vector{name};
352fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
353b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    if (!action->InitSingleTrigger(name)) {
354fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        return;
355fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
356fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
357b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    action->AddCommand(func, name_vector);
358fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
359b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    trigger_queue_.push(std::make_unique<BuiltinTrigger>(action.get()));
360b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    actions_.emplace_back(std::move(action));
361fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
362fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
363fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherryvoid ActionManager::ExecuteOneCommand() {
364b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    // Loop through the trigger queue until we have an action to execute
365cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    while (current_executing_actions_.empty() && !trigger_queue_.empty()) {
366b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        for (const auto& action : actions_) {
367b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry            if (trigger_queue_.front()->CheckTriggers(*action)) {
368b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                current_executing_actions_.emplace(action.get());
369b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry            }
370b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        }
371cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry        trigger_queue_.pop();
372cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    }
373cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry
374cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    if (current_executing_actions_.empty()) {
375fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        return;
376fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
377fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
378b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    auto action = current_executing_actions_.front();
379fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
380cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    if (current_command_ == 0) {
381fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        std::string trigger_name = action->BuildTriggersString();
382f86b5a6b90619e02d1d034ef7b0adc3b439f4abbElliott Hughes        LOG(INFO) << "processing action (" << trigger_name << ")";
383fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
384fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
385b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    action->ExecuteOneCommand(current_command_);
386b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry
387b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    // If this was the last command in the current action, then remove
388b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    // the action from the executing list.
389b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    // If this action was oneshot, then also remove it from actions_.
390b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    ++current_command_;
391cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    if (current_command_ == action->NumCommands()) {
392b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        current_executing_actions_.pop();
393cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry        current_command_ = 0;
394b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        if (action->oneshot()) {
395b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry            auto eraser = [&action] (std::unique_ptr<Action>& a) {
396b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                return a.get() == action;
397b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry            };
398b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry            actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser));
399b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        }
400fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
401fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
402fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
403b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool ActionManager::HasMoreCommands() const {
404cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    return !current_executing_actions_.empty() || !trigger_queue_.empty();
405fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
406fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
407b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid ActionManager::DumpState() const {
408b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    for (const auto& a : actions_) {
409b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        a->DumpState();
410fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
411b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry}
412fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
413b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool ActionParser::ParseSection(const std::vector<std::string>& args,
414b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                                std::string* err) {
415b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    std::vector<std::string> triggers(args.begin() + 1, args.end());
416b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    if (triggers.size() < 1) {
417b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        *err = "actions must have a trigger";
418b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        return false;
419fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
420fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
421b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    auto action = std::make_unique<Action>(false);
422b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    if (!action->InitTriggers(triggers, err)) {
423b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        return false;
424fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
425fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
426b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    action_ = std::move(action);
427b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    return true;
428fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
429fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
430b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool ActionParser::ParseLineSection(const std::vector<std::string>& args,
431b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                                    const std::string& filename, int line,
432b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                                    std::string* err) const {
433b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    return action_ ? action_->AddCommand(args, filename, line, err) : false;
434b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry}
435b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry
436b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid ActionParser::EndSection() {
437b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    if (action_ && action_->NumCommands() > 0) {
438b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        ActionManager::GetInstance().AddAction(std::move(action_));
439fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
440fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
441