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])) {
45fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            ERROR("%s: cannot expand '%s'\n", args_[0].c_str(), args_[i].c_str());
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 {
108b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    ExecuteCommand(commands_[command]);
109fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
110fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
111b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid Action::ExecuteAllCommands() const {
112fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    for (const auto& c : commands_) {
113b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        ExecuteCommand(c);
114fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
115fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
116fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
117b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid Action::ExecuteCommand(const Command& command) const {
118fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    Timer t;
119fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    int result = command.InvokeFunc();
120fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
121fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    if (klog_get_level() >= KLOG_INFO_LEVEL) {
122fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        std::string trigger_name = BuildTriggersString();
123fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        std::string cmd_str = command.BuildCommandString();
124fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        std::string source = command.BuildSourceString();
125fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
126fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        INFO("Command '%s' action=%s%s returned %d took %.2fs\n",
127fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry             cmd_str.c_str(), trigger_name.c_str(), source.c_str(),
128fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry             result, t.duration());
129fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
130fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
131fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
132b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool Action::ParsePropertyTrigger(const std::string& trigger, std::string* err) {
133fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    const static std::string prop_str("property:");
134fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    std::string prop_name(trigger.substr(prop_str.length()));
135fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    size_t equal_pos = prop_name.find('=');
136fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    if (equal_pos == std::string::npos) {
137fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        *err = "property trigger found without matching '='";
138fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        return false;
139fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
140fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
141fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    std::string prop_value(prop_name.substr(equal_pos + 1));
142fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    prop_name.erase(equal_pos);
143fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
144fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    auto res = property_triggers_.emplace(prop_name, prop_value);
145fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    if (res.second == false) {
146fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        *err = "multiple property triggers found for same property";
147fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        return false;
148fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
149fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    return true;
150fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
151fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
152b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool Action::InitTriggers(const std::vector<std::string>& args, std::string* err) {
153fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    const static std::string prop_str("property:");
154fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    for (std::size_t i = 0; i < args.size(); ++i) {
155fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        if (i % 2) {
156cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry            if (args[i] != "&&") {
157fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                *err = "&& is the only symbol allowed to concatenate actions";
158fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                return false;
159fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            } else {
160fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                continue;
161fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            }
162fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        }
163fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
164fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        if (!args[i].compare(0, prop_str.length(), prop_str)) {
165fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            if (!ParsePropertyTrigger(args[i], err)) {
166fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                return false;
167fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            }
168fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        } else {
169fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            if (!event_trigger_.empty()) {
170fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                *err = "multiple event triggers are not allowed";
171fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                return false;
172fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            }
173fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
174fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            event_trigger_ = args[i];
175fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        }
176fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
177fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
178fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    return true;
179fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
180fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
181b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool Action::InitSingleTrigger(const std::string& trigger) {
182fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    std::vector<std::string> name_vector{trigger};
183fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    std::string err;
184fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    return InitTriggers(name_vector, &err);
185fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
186fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
187b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry// This function checks that all property triggers are satisfied, that is
188b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry// for each (name, value) in property_triggers_, check that the current
189b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry// value of the property 'name' == value.
190b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry//
191b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry// It takes an optional (name, value) pair, which if provided must
192b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry// be present in property_triggers_; it skips the check of the current
193b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry// property value for this pair.
194fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherrybool Action::CheckPropertyTriggers(const std::string& name,
195b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                                   const std::string& value) const {
196fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    if (property_triggers_.empty()) {
197fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        return true;
198fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
199fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
200b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    bool found = name.empty();
201fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    for (const auto& t : property_triggers_) {
202cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry        const auto& trigger_name = t.first;
203cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry        const auto& trigger_value = t.second;
204cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry        if (trigger_name == name) {
205cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry            if (trigger_value != "*" && trigger_value != value) {
206fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                return false;
207fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            } else {
208fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                found = true;
209fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            }
210fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        } else {
211cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry            std::string prop_val = property_get(trigger_name.c_str());
212cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry            if (prop_val.empty() || (trigger_value != "*" &&
213cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry                                     trigger_value != prop_val)) {
214fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                return false;
215fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            }
216fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        }
217fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
218fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    return found;
219fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
220fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
221b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool Action::CheckEventTrigger(const std::string& trigger) const {
222fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    return !event_trigger_.empty() &&
223cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry        trigger == event_trigger_ &&
224fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        CheckPropertyTriggers();
225fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
226fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
227fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherrybool Action::CheckPropertyTrigger(const std::string& name,
228b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                                  const std::string& value) const {
229fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    return event_trigger_.empty() && CheckPropertyTriggers(name, value);
230fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
231fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
232b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool Action::TriggersEqual(const Action& other) const {
233cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    return property_triggers_ == other.property_triggers_ &&
234cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry        event_trigger_ == other.event_trigger_;
235fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
236fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
237b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrystd::string Action::BuildTriggersString() const {
238fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    std::string result;
239fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
240fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    for (const auto& t : property_triggers_) {
241fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        result += t.first;
242fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        result += '=';
243fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        result += t.second;
244fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        result += ' ';
245fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
246fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    if (!event_trigger_.empty()) {
247fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        result += event_trigger_;
248fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        result += ' ';
249fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
250fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    result.pop_back();
251fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    return result;
252fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
253fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
254b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid Action::DumpState() const {
255fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    std::string trigger_name = BuildTriggersString();
256cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    INFO("on %s\n", trigger_name.c_str());
257fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
258fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    for (const auto& c : commands_) {
259b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        std::string cmd_str = c.BuildCommandString();
260cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry        INFO(" %s\n", cmd_str.c_str());
261fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
262fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    INFO("\n");
263fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
264fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
265cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherryclass EventTrigger : public Trigger {
266cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherrypublic:
267cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    EventTrigger(const std::string& trigger) : trigger_(trigger) {
268cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    }
269b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    bool CheckTriggers(const Action& action) const override {
270b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        return action.CheckEventTrigger(trigger_);
271cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    }
272cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherryprivate:
273b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    const std::string trigger_;
274cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry};
275cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry
276cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherryclass PropertyTrigger : public Trigger {
277cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherrypublic:
278cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    PropertyTrigger(const std::string& name, const std::string& value)
279cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry        : name_(name), value_(value) {
280cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    }
281b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    bool CheckTriggers(const Action& action) const override {
282b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        return action.CheckPropertyTrigger(name_, value_);
283cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    }
284cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherryprivate:
285b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    const std::string name_;
286b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    const std::string value_;
287cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry};
288cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry
289cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherryclass BuiltinTrigger : public Trigger {
290cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherrypublic:
291cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    BuiltinTrigger(Action* action) : action_(action) {
292cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    }
293b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    bool CheckTriggers(const Action& action) const override {
294b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        return action_ == &action;
295cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    }
296cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherryprivate:
297b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    const Action* action_;
298cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry};
299cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry
300b7349902a945903f9e36a569051f5131beb0bc24Tom CherryActionManager::ActionManager() : current_command_(0) {
301fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
302fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
303fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom CherryActionManager& ActionManager::GetInstance() {
304fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    static ActionManager instance;
305fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    return instance;
306fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
307fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
308b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid ActionManager::AddAction(std::unique_ptr<Action> action) {
309b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    auto old_action_it =
310b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        std::find_if(actions_.begin(), actions_.end(),
311b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                     [&action] (std::unique_ptr<Action>& a) {
312b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                         return action->TriggersEqual(*a);
313b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                     });
314b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry
315b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    if (old_action_it != actions_.end()) {
316b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        (*old_action_it)->CombineAction(*action);
317b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    } else {
318b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        actions_.emplace_back(std::move(action));
319b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    }
320b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry}
321b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry
322b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid ActionManager::QueueEventTrigger(const std::string& trigger) {
323cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    trigger_queue_.push(std::make_unique<EventTrigger>(trigger));
324fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
325fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
326fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherryvoid ActionManager::QueuePropertyTrigger(const std::string& name,
327b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                                         const std::string& value) {
328cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    trigger_queue_.push(std::make_unique<PropertyTrigger>(name, value));
329fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
330fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
331b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid ActionManager::QueueAllPropertyTriggers() {
332fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    QueuePropertyTrigger("", "");
333fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
334fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
335b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid ActionManager::QueueBuiltinAction(BuiltinFunction func,
336b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                                       const std::string& name) {
337b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    auto action = std::make_unique<Action>(true);
338fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    std::vector<std::string> name_vector{name};
339fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
340b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    if (!action->InitSingleTrigger(name)) {
341fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        return;
342fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
343fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
344b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    action->AddCommand(func, name_vector);
345fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
346b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    trigger_queue_.push(std::make_unique<BuiltinTrigger>(action.get()));
347b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    actions_.emplace_back(std::move(action));
348fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
349fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
350fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherryvoid ActionManager::ExecuteOneCommand() {
351b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    // Loop through the trigger queue until we have an action to execute
352cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    while (current_executing_actions_.empty() && !trigger_queue_.empty()) {
353b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        for (const auto& action : actions_) {
354b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry            if (trigger_queue_.front()->CheckTriggers(*action)) {
355b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                current_executing_actions_.emplace(action.get());
356b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry            }
357b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        }
358cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry        trigger_queue_.pop();
359cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    }
360cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry
361cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    if (current_executing_actions_.empty()) {
362fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        return;
363fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
364fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
365b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    auto action = current_executing_actions_.front();
366fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
367cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    if (current_command_ == 0) {
368fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        std::string trigger_name = action->BuildTriggersString();
369b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        INFO("processing action (%s)\n", trigger_name.c_str());
370fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
371fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
372b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    action->ExecuteOneCommand(current_command_);
373b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry
374b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    // If this was the last command in the current action, then remove
375b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    // the action from the executing list.
376b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    // If this action was oneshot, then also remove it from actions_.
377b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    ++current_command_;
378cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    if (current_command_ == action->NumCommands()) {
379b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        current_executing_actions_.pop();
380cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry        current_command_ = 0;
381b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        if (action->oneshot()) {
382b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry            auto eraser = [&action] (std::unique_ptr<Action>& a) {
383b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                return a.get() == action;
384b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry            };
385b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry            actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser));
386b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        }
387fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
388fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
389fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
390b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool ActionManager::HasMoreCommands() const {
391cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    return !current_executing_actions_.empty() || !trigger_queue_.empty();
392fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
393fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
394b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid ActionManager::DumpState() const {
395b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    for (const auto& a : actions_) {
396b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        a->DumpState();
397fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
398b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    INFO("\n");
399b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry}
400fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
401b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool ActionParser::ParseSection(const std::vector<std::string>& args,
402b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                                std::string* err) {
403b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    std::vector<std::string> triggers(args.begin() + 1, args.end());
404b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    if (triggers.size() < 1) {
405b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        *err = "actions must have a trigger";
406b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        return false;
407fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
408fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
409b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    auto action = std::make_unique<Action>(false);
410b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    if (!action->InitTriggers(triggers, err)) {
411b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        return false;
412fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
413fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
414b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    action_ = std::move(action);
415b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    return true;
416fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
417fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
418b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool ActionParser::ParseLineSection(const std::vector<std::string>& args,
419b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                                    const std::string& filename, int line,
420b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                                    std::string* err) const {
421b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    return action_ ? action_->AddCommand(args, filename, line, err) : false;
422b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry}
423b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry
424b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid ActionParser::EndSection() {
425b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    if (action_ && action_->NumCommands() > 0) {
426b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        ActionManager::GetInstance().AddAction(std::move(action_));
427fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
428fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
429