• 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 <android-base/chrono_utils.h>
20 #include <android-base/logging.h>
21 #include <android-base/properties.h>
22 #include <android-base/strings.h>
23 
24 #include "util.h"
25 
26 using android::base::Join;
27 
28 namespace android {
29 namespace init {
30 
RunBuiltinFunction(const BuiltinFunction & function,const std::vector<std::string> & args,const std::string & context)31 Result<Success> RunBuiltinFunction(const BuiltinFunction& function,
32                                    const std::vector<std::string>& args,
33                                    const std::string& context) {
34     auto builtin_arguments = BuiltinArguments(context);
35 
36     builtin_arguments.args.resize(args.size());
37     builtin_arguments.args[0] = args[0];
38     for (std::size_t i = 1; i < args.size(); ++i) {
39         if (!expand_props(args[i], &builtin_arguments.args[i])) {
40             return Error() << "cannot expand '" << args[i] << "'";
41         }
42     }
43 
44     return function(builtin_arguments);
45 }
46 
Command(BuiltinFunction f,bool execute_in_subcontext,std::vector<std::string> && args,int line)47 Command::Command(BuiltinFunction f, bool execute_in_subcontext, std::vector<std::string>&& args,
48                  int line)
49     : func_(std::move(f)),
50       execute_in_subcontext_(execute_in_subcontext),
51       args_(std::move(args)),
52       line_(line) {}
53 
InvokeFunc(Subcontext * subcontext) const54 Result<Success> Command::InvokeFunc(Subcontext* subcontext) const {
55     if (subcontext) {
56         if (execute_in_subcontext_) {
57             return subcontext->Execute(args_);
58         }
59 
60         auto expanded_args = subcontext->ExpandArgs(args_);
61         if (!expanded_args) {
62             return expanded_args.error();
63         }
64         return RunBuiltinFunction(func_, *expanded_args, subcontext->context());
65     }
66 
67     return RunBuiltinFunction(func_, args_, kInitContext);
68 }
69 
BuildCommandString() const70 std::string Command::BuildCommandString() const {
71     return Join(args_, ' ');
72 }
73 
Action(bool oneshot,Subcontext * subcontext,const std::string & filename,int line,const std::string & event_trigger,const std::map<std::string,std::string> & property_triggers)74 Action::Action(bool oneshot, Subcontext* subcontext, const std::string& filename, int line,
75                const std::string& event_trigger,
76                const std::map<std::string, std::string>& property_triggers)
77     : property_triggers_(property_triggers),
78       event_trigger_(event_trigger),
79       oneshot_(oneshot),
80       subcontext_(subcontext),
81       filename_(filename),
82       line_(line) {}
83 
84 const KeywordFunctionMap* Action::function_map_ = nullptr;
85 
AddCommand(std::vector<std::string> && args,int line)86 Result<Success> Action::AddCommand(std::vector<std::string>&& args, int line) {
87     if (!function_map_) {
88         return Error() << "no function map available";
89     }
90 
91     auto function = function_map_->FindFunction(args);
92     if (!function) return Error() << function.error();
93 
94     commands_.emplace_back(function->second, function->first, std::move(args), line);
95     return Success();
96 }
97 
AddCommand(BuiltinFunction f,std::vector<std::string> && args,int line)98 void Action::AddCommand(BuiltinFunction f, std::vector<std::string>&& args, int line) {
99     commands_.emplace_back(f, false, std::move(args), line);
100 }
101 
NumCommands() const102 std::size_t Action::NumCommands() const {
103     return commands_.size();
104 }
105 
ExecuteOneCommand(std::size_t command) const106 void Action::ExecuteOneCommand(std::size_t command) const {
107     // We need a copy here since some Command execution may result in
108     // changing commands_ vector by importing .rc files through parser
109     Command cmd = commands_[command];
110     ExecuteCommand(cmd);
111 }
112 
ExecuteAllCommands() const113 void Action::ExecuteAllCommands() const {
114     for (const auto& c : commands_) {
115         ExecuteCommand(c);
116     }
117 }
118 
ExecuteCommand(const Command & command) const119 void Action::ExecuteCommand(const Command& command) const {
120     android::base::Timer t;
121     auto result = command.InvokeFunc(subcontext_);
122     auto duration = t.duration();
123 
124     // There are many legacy paths in rootdir/init.rc that will virtually never exist on a new
125     // device, such as '/sys/class/leds/jogball-backlight/brightness'.  As of this writing, there
126     // are 198 such failures on bullhead.  Instead of spamming the log reporting them, we do not
127     // report such failures unless we're running at the DEBUG log level.
128     bool report_failure = !result.has_value();
129     if (report_failure && android::base::GetMinimumLogSeverity() > android::base::DEBUG &&
130         result.error_errno() == ENOENT) {
131         report_failure = false;
132     }
133 
134     // Any action longer than 50ms will be warned to user as slow operation
135     if (report_failure || duration > 50ms ||
136         android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
137         std::string trigger_name = BuildTriggersString();
138         std::string cmd_str = command.BuildCommandString();
139 
140         LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << " (" << filename_
141                   << ":" << command.line() << ") took " << duration.count() << "ms and "
142                   << (result ? "succeeded" : "failed: " + result.error_string());
143     }
144 }
145 
146 // This function checks that all property triggers are satisfied, that is
147 // for each (name, value) in property_triggers_, check that the current
148 // value of the property 'name' == value.
149 //
150 // It takes an optional (name, value) pair, which if provided must
151 // be present in property_triggers_; it skips the check of the current
152 // property value for this pair.
CheckPropertyTriggers(const std::string & name,const std::string & value) const153 bool Action::CheckPropertyTriggers(const std::string& name,
154                                    const std::string& value) const {
155     if (property_triggers_.empty()) {
156         return true;
157     }
158 
159     bool found = name.empty();
160     for (const auto& [trigger_name, trigger_value] : property_triggers_) {
161         if (trigger_name == name) {
162             if (trigger_value != "*" && trigger_value != value) {
163                 return false;
164             } else {
165                 found = true;
166             }
167         } else {
168             std::string prop_val = android::base::GetProperty(trigger_name, "");
169             if (prop_val.empty() || (trigger_value != "*" && trigger_value != prop_val)) {
170                 return false;
171             }
172         }
173     }
174     return found;
175 }
176 
CheckEvent(const EventTrigger & event_trigger) const177 bool Action::CheckEvent(const EventTrigger& event_trigger) const {
178     return event_trigger == event_trigger_ && CheckPropertyTriggers();
179 }
180 
CheckEvent(const PropertyChange & property_change) const181 bool Action::CheckEvent(const PropertyChange& property_change) const {
182     const auto& [name, value] = property_change;
183     return event_trigger_.empty() && CheckPropertyTriggers(name, value);
184 }
185 
CheckEvent(const BuiltinAction & builtin_action) const186 bool Action::CheckEvent(const BuiltinAction& builtin_action) const {
187     return this == builtin_action;
188 }
189 
BuildTriggersString() const190 std::string Action::BuildTriggersString() const {
191     std::vector<std::string> triggers;
192 
193     for (const auto& [trigger_name, trigger_value] : property_triggers_) {
194         triggers.emplace_back(trigger_name + '=' + trigger_value);
195     }
196     if (!event_trigger_.empty()) {
197         triggers.emplace_back(event_trigger_);
198     }
199 
200     return Join(triggers, " && ");
201 }
202 
DumpState() const203 void Action::DumpState() const {
204     std::string trigger_name = BuildTriggersString();
205     LOG(INFO) << "on " << trigger_name;
206 
207     for (const auto& c : commands_) {
208         std::string cmd_str = c.BuildCommandString();
209         LOG(INFO) << "  " << cmd_str;
210     }
211 }
212 
213 }  // namespace init
214 }  // namespace android
215