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