• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "action.h"
18 
19 #include <errno.h>
20 
21 #include <android-base/strings.h>
22 #include <android-base/stringprintf.h>
23 
24 #include "builtins.h"
25 #include "error.h"
26 #include "init_parser.h"
27 #include "log.h"
28 #include "property_service.h"
29 #include "util.h"
30 
31 using android::base::Join;
32 using android::base::StringPrintf;
33 
Command(BuiltinFunction f,const std::vector<std::string> & args,const std::string & filename,int line)34 Command::Command(BuiltinFunction f, const std::vector<std::string>& args,
35                  const std::string& filename, int line)
36     : func_(f), args_(args), filename_(filename), line_(line) {
37 }
38 
InvokeFunc() const39 int Command::InvokeFunc() const {
40     std::vector<std::string> expanded_args;
41     expanded_args.resize(args_.size());
42     expanded_args[0] = args_[0];
43     for (std::size_t i = 1; i < args_.size(); ++i) {
44         if (!expand_props(args_[i], &expanded_args[i])) {
45             ERROR("%s: cannot expand '%s'\n", args_[0].c_str(), args_[i].c_str());
46             return -EINVAL;
47         }
48     }
49 
50     return func_(expanded_args);
51 }
52 
BuildCommandString() const53 std::string Command::BuildCommandString() const {
54     return Join(args_, ' ');
55 }
56 
BuildSourceString() const57 std::string Command::BuildSourceString() const {
58     if (!filename_.empty()) {
59         return StringPrintf(" (%s:%d)", filename_.c_str(), line_);
60     } else {
61         return std::string();
62     }
63 }
64 
Action(bool oneshot)65 Action::Action(bool oneshot) : oneshot_(oneshot) {
66 }
67 
68 const KeywordMap<BuiltinFunction>* Action::function_map_ = nullptr;
69 
AddCommand(const std::vector<std::string> & args,const std::string & filename,int line,std::string * err)70 bool Action::AddCommand(const std::vector<std::string>& args,
71                         const std::string& filename, int line, std::string* err) {
72     if (!function_map_) {
73         *err = "no function map available";
74         return false;
75     }
76 
77     if (args.empty()) {
78         *err = "command needed, but not provided";
79         return false;
80     }
81 
82     auto function = function_map_->FindFunction(args[0], args.size() - 1, err);
83     if (!function) {
84         return false;
85     }
86 
87     AddCommand(function, args, filename, line);
88     return true;
89 }
90 
AddCommand(BuiltinFunction f,const std::vector<std::string> & args,const std::string & filename,int line)91 void Action::AddCommand(BuiltinFunction f,
92                         const std::vector<std::string>& args,
93                         const std::string& filename, int line) {
94     commands_.emplace_back(f, args, filename, line);
95 }
96 
CombineAction(const Action & action)97 void Action::CombineAction(const Action& action) {
98     for (const auto& c : action.commands_) {
99         commands_.emplace_back(c);
100     }
101 }
102 
NumCommands() const103 std::size_t Action::NumCommands() const {
104     return commands_.size();
105 }
106 
ExecuteOneCommand(std::size_t command) const107 void Action::ExecuteOneCommand(std::size_t command) const {
108     ExecuteCommand(commands_[command]);
109 }
110 
ExecuteAllCommands() const111 void Action::ExecuteAllCommands() const {
112     for (const auto& c : commands_) {
113         ExecuteCommand(c);
114     }
115 }
116 
ExecuteCommand(const Command & command) const117 void Action::ExecuteCommand(const Command& command) const {
118     Timer t;
119     int result = command.InvokeFunc();
120 
121     if (klog_get_level() >= KLOG_INFO_LEVEL) {
122         std::string trigger_name = BuildTriggersString();
123         std::string cmd_str = command.BuildCommandString();
124         std::string source = command.BuildSourceString();
125 
126         INFO("Command '%s' action=%s%s returned %d took %.2fs\n",
127              cmd_str.c_str(), trigger_name.c_str(), source.c_str(),
128              result, t.duration());
129     }
130 }
131 
ParsePropertyTrigger(const std::string & trigger,std::string * err)132 bool Action::ParsePropertyTrigger(const std::string& trigger, std::string* err) {
133     const static std::string prop_str("property:");
134     std::string prop_name(trigger.substr(prop_str.length()));
135     size_t equal_pos = prop_name.find('=');
136     if (equal_pos == std::string::npos) {
137         *err = "property trigger found without matching '='";
138         return false;
139     }
140 
141     std::string prop_value(prop_name.substr(equal_pos + 1));
142     prop_name.erase(equal_pos);
143 
144     auto res = property_triggers_.emplace(prop_name, prop_value);
145     if (res.second == false) {
146         *err = "multiple property triggers found for same property";
147         return false;
148     }
149     return true;
150 }
151 
InitTriggers(const std::vector<std::string> & args,std::string * err)152 bool Action::InitTriggers(const std::vector<std::string>& args, std::string* err) {
153     const static std::string prop_str("property:");
154     for (std::size_t i = 0; i < args.size(); ++i) {
155         if (i % 2) {
156             if (args[i] != "&&") {
157                 *err = "&& is the only symbol allowed to concatenate actions";
158                 return false;
159             } else {
160                 continue;
161             }
162         }
163 
164         if (!args[i].compare(0, prop_str.length(), prop_str)) {
165             if (!ParsePropertyTrigger(args[i], err)) {
166                 return false;
167             }
168         } else {
169             if (!event_trigger_.empty()) {
170                 *err = "multiple event triggers are not allowed";
171                 return false;
172             }
173 
174             event_trigger_ = args[i];
175         }
176     }
177 
178     return true;
179 }
180 
InitSingleTrigger(const std::string & trigger)181 bool Action::InitSingleTrigger(const std::string& trigger) {
182     std::vector<std::string> name_vector{trigger};
183     std::string err;
184     return InitTriggers(name_vector, &err);
185 }
186 
187 // This function checks that all property triggers are satisfied, that is
188 // for each (name, value) in property_triggers_, check that the current
189 // value of the property 'name' == value.
190 //
191 // It takes an optional (name, value) pair, which if provided must
192 // be present in property_triggers_; it skips the check of the current
193 // property value for this pair.
CheckPropertyTriggers(const std::string & name,const std::string & value) const194 bool Action::CheckPropertyTriggers(const std::string& name,
195                                    const std::string& value) const {
196     if (property_triggers_.empty()) {
197         return true;
198     }
199 
200     bool found = name.empty();
201     for (const auto& t : property_triggers_) {
202         const auto& trigger_name = t.first;
203         const auto& trigger_value = t.second;
204         if (trigger_name == name) {
205             if (trigger_value != "*" && trigger_value != value) {
206                 return false;
207             } else {
208                 found = true;
209             }
210         } else {
211             std::string prop_val = property_get(trigger_name.c_str());
212             if (prop_val.empty() || (trigger_value != "*" &&
213                                      trigger_value != prop_val)) {
214                 return false;
215             }
216         }
217     }
218     return found;
219 }
220 
CheckEventTrigger(const std::string & trigger) const221 bool Action::CheckEventTrigger(const std::string& trigger) const {
222     return !event_trigger_.empty() &&
223         trigger == event_trigger_ &&
224         CheckPropertyTriggers();
225 }
226 
CheckPropertyTrigger(const std::string & name,const std::string & value) const227 bool Action::CheckPropertyTrigger(const std::string& name,
228                                   const std::string& value) const {
229     return event_trigger_.empty() && CheckPropertyTriggers(name, value);
230 }
231 
TriggersEqual(const Action & other) const232 bool Action::TriggersEqual(const Action& other) const {
233     return property_triggers_ == other.property_triggers_ &&
234         event_trigger_ == other.event_trigger_;
235 }
236 
BuildTriggersString() const237 std::string Action::BuildTriggersString() const {
238     std::string result;
239 
240     for (const auto& t : property_triggers_) {
241         result += t.first;
242         result += '=';
243         result += t.second;
244         result += ' ';
245     }
246     if (!event_trigger_.empty()) {
247         result += event_trigger_;
248         result += ' ';
249     }
250     result.pop_back();
251     return result;
252 }
253 
DumpState() const254 void Action::DumpState() const {
255     std::string trigger_name = BuildTriggersString();
256     INFO("on %s\n", trigger_name.c_str());
257 
258     for (const auto& c : commands_) {
259         std::string cmd_str = c.BuildCommandString();
260         INFO(" %s\n", cmd_str.c_str());
261     }
262     INFO("\n");
263 }
264 
265 class EventTrigger : public Trigger {
266 public:
EventTrigger(const std::string & trigger)267     EventTrigger(const std::string& trigger) : trigger_(trigger) {
268     }
CheckTriggers(const Action & action) const269     bool CheckTriggers(const Action& action) const override {
270         return action.CheckEventTrigger(trigger_);
271     }
272 private:
273     const std::string trigger_;
274 };
275 
276 class PropertyTrigger : public Trigger {
277 public:
PropertyTrigger(const std::string & name,const std::string & value)278     PropertyTrigger(const std::string& name, const std::string& value)
279         : name_(name), value_(value) {
280     }
CheckTriggers(const Action & action) const281     bool CheckTriggers(const Action& action) const override {
282         return action.CheckPropertyTrigger(name_, value_);
283     }
284 private:
285     const std::string name_;
286     const std::string value_;
287 };
288 
289 class BuiltinTrigger : public Trigger {
290 public:
BuiltinTrigger(Action * action)291     BuiltinTrigger(Action* action) : action_(action) {
292     }
CheckTriggers(const Action & action) const293     bool CheckTriggers(const Action& action) const override {
294         return action_ == &action;
295     }
296 private:
297     const Action* action_;
298 };
299 
ActionManager()300 ActionManager::ActionManager() : current_command_(0) {
301 }
302 
GetInstance()303 ActionManager& ActionManager::GetInstance() {
304     static ActionManager instance;
305     return instance;
306 }
307 
AddAction(std::unique_ptr<Action> action)308 void ActionManager::AddAction(std::unique_ptr<Action> action) {
309     auto old_action_it =
310         std::find_if(actions_.begin(), actions_.end(),
311                      [&action] (std::unique_ptr<Action>& a) {
312                          return action->TriggersEqual(*a);
313                      });
314 
315     if (old_action_it != actions_.end()) {
316         (*old_action_it)->CombineAction(*action);
317     } else {
318         actions_.emplace_back(std::move(action));
319     }
320 }
321 
QueueEventTrigger(const std::string & trigger)322 void ActionManager::QueueEventTrigger(const std::string& trigger) {
323     trigger_queue_.push(std::make_unique<EventTrigger>(trigger));
324 }
325 
QueuePropertyTrigger(const std::string & name,const std::string & value)326 void ActionManager::QueuePropertyTrigger(const std::string& name,
327                                          const std::string& value) {
328     trigger_queue_.push(std::make_unique<PropertyTrigger>(name, value));
329 }
330 
QueueAllPropertyTriggers()331 void ActionManager::QueueAllPropertyTriggers() {
332     QueuePropertyTrigger("", "");
333 }
334 
QueueBuiltinAction(BuiltinFunction func,const std::string & name)335 void ActionManager::QueueBuiltinAction(BuiltinFunction func,
336                                        const std::string& name) {
337     auto action = std::make_unique<Action>(true);
338     std::vector<std::string> name_vector{name};
339 
340     if (!action->InitSingleTrigger(name)) {
341         return;
342     }
343 
344     action->AddCommand(func, name_vector);
345 
346     trigger_queue_.push(std::make_unique<BuiltinTrigger>(action.get()));
347     actions_.emplace_back(std::move(action));
348 }
349 
ExecuteOneCommand()350 void ActionManager::ExecuteOneCommand() {
351     // Loop through the trigger queue until we have an action to execute
352     while (current_executing_actions_.empty() && !trigger_queue_.empty()) {
353         for (const auto& action : actions_) {
354             if (trigger_queue_.front()->CheckTriggers(*action)) {
355                 current_executing_actions_.emplace(action.get());
356             }
357         }
358         trigger_queue_.pop();
359     }
360 
361     if (current_executing_actions_.empty()) {
362         return;
363     }
364 
365     auto action = current_executing_actions_.front();
366 
367     if (current_command_ == 0) {
368         std::string trigger_name = action->BuildTriggersString();
369         INFO("processing action (%s)\n", trigger_name.c_str());
370     }
371 
372     action->ExecuteOneCommand(current_command_);
373 
374     // If this was the last command in the current action, then remove
375     // the action from the executing list.
376     // If this action was oneshot, then also remove it from actions_.
377     ++current_command_;
378     if (current_command_ == action->NumCommands()) {
379         current_executing_actions_.pop();
380         current_command_ = 0;
381         if (action->oneshot()) {
382             auto eraser = [&action] (std::unique_ptr<Action>& a) {
383                 return a.get() == action;
384             };
385             actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser));
386         }
387     }
388 }
389 
HasMoreCommands() const390 bool ActionManager::HasMoreCommands() const {
391     return !current_executing_actions_.empty() || !trigger_queue_.empty();
392 }
393 
DumpState() const394 void ActionManager::DumpState() const {
395     for (const auto& a : actions_) {
396         a->DumpState();
397     }
398     INFO("\n");
399 }
400 
ParseSection(const std::vector<std::string> & args,std::string * err)401 bool ActionParser::ParseSection(const std::vector<std::string>& args,
402                                 std::string* err) {
403     std::vector<std::string> triggers(args.begin() + 1, args.end());
404     if (triggers.size() < 1) {
405         *err = "actions must have a trigger";
406         return false;
407     }
408 
409     auto action = std::make_unique<Action>(false);
410     if (!action->InitTriggers(triggers, err)) {
411         return false;
412     }
413 
414     action_ = std::move(action);
415     return true;
416 }
417 
ParseLineSection(const std::vector<std::string> & args,const std::string & filename,int line,std::string * err) const418 bool ActionParser::ParseLineSection(const std::vector<std::string>& args,
419                                     const std::string& filename, int line,
420                                     std::string* err) const {
421     return action_ ? action_->AddCommand(args, filename, line, err) : false;
422 }
423 
EndSection()424 void ActionParser::EndSection() {
425     if (action_ && action_->NumCommands() > 0) {
426         ActionManager::GetInstance().AddAction(std::move(action_));
427     }
428 }
429