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
31using android::base::Join;
32using android::base::StringPrintf;
33
34Command::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
39int 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
53std::string Command::BuildCommandString() const {
54    return Join(args_, ' ');
55}
56
57std::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
65Action::Action(bool oneshot) : oneshot_(oneshot) {
66}
67
68const KeywordMap<BuiltinFunction>* Action::function_map_ = nullptr;
69
70bool 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
91void 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
97void Action::CombineAction(const Action& action) {
98    for (const auto& c : action.commands_) {
99        commands_.emplace_back(c);
100    }
101}
102
103std::size_t Action::NumCommands() const {
104    return commands_.size();
105}
106
107void Action::ExecuteOneCommand(std::size_t command) const {
108    ExecuteCommand(commands_[command]);
109}
110
111void Action::ExecuteAllCommands() const {
112    for (const auto& c : commands_) {
113        ExecuteCommand(c);
114    }
115}
116
117void 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
132bool 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
152bool 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
181bool 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.
194bool 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
221bool Action::CheckEventTrigger(const std::string& trigger) const {
222    return !event_trigger_.empty() &&
223        trigger == event_trigger_ &&
224        CheckPropertyTriggers();
225}
226
227bool Action::CheckPropertyTrigger(const std::string& name,
228                                  const std::string& value) const {
229    return event_trigger_.empty() && CheckPropertyTriggers(name, value);
230}
231
232bool Action::TriggersEqual(const Action& other) const {
233    return property_triggers_ == other.property_triggers_ &&
234        event_trigger_ == other.event_trigger_;
235}
236
237std::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
254void 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
265class EventTrigger : public Trigger {
266public:
267    EventTrigger(const std::string& trigger) : trigger_(trigger) {
268    }
269    bool CheckTriggers(const Action& action) const override {
270        return action.CheckEventTrigger(trigger_);
271    }
272private:
273    const std::string trigger_;
274};
275
276class PropertyTrigger : public Trigger {
277public:
278    PropertyTrigger(const std::string& name, const std::string& value)
279        : name_(name), value_(value) {
280    }
281    bool CheckTriggers(const Action& action) const override {
282        return action.CheckPropertyTrigger(name_, value_);
283    }
284private:
285    const std::string name_;
286    const std::string value_;
287};
288
289class BuiltinTrigger : public Trigger {
290public:
291    BuiltinTrigger(Action* action) : action_(action) {
292    }
293    bool CheckTriggers(const Action& action) const override {
294        return action_ == &action;
295    }
296private:
297    const Action* action_;
298};
299
300ActionManager::ActionManager() : current_command_(0) {
301}
302
303ActionManager& ActionManager::GetInstance() {
304    static ActionManager instance;
305    return instance;
306}
307
308void 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
322void ActionManager::QueueEventTrigger(const std::string& trigger) {
323    trigger_queue_.push(std::make_unique<EventTrigger>(trigger));
324}
325
326void ActionManager::QueuePropertyTrigger(const std::string& name,
327                                         const std::string& value) {
328    trigger_queue_.push(std::make_unique<PropertyTrigger>(name, value));
329}
330
331void ActionManager::QueueAllPropertyTriggers() {
332    QueuePropertyTrigger("", "");
333}
334
335void 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
350void 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
390bool ActionManager::HasMoreCommands() const {
391    return !current_executing_actions_.empty() || !trigger_queue_.empty();
392}
393
394void ActionManager::DumpState() const {
395    for (const auto& a : actions_) {
396        a->DumpState();
397    }
398    INFO("\n");
399}
400
401bool 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
418bool 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
424void ActionParser::EndSection() {
425    if (action_ && action_->NumCommands() > 0) {
426        ActionManager::GetInstance().AddAction(std::move(action_));
427    }
428}
429