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