| Tom Cherry | 2aeb1ad | 2019-06-26 10:46:20 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * Copyright (C) 2019 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 "service_parser.h" | 
 | 18 |  | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 19 | #include <linux/input.h> | 
| Tom Cherry | 2e4c85f | 2019-07-09 13:33:36 -0700 | [diff] [blame] | 20 | #include <stdlib.h> | 
 | 21 | #include <sys/socket.h> | 
| Tom Cherry | 2aeb1ad | 2019-06-26 10:46:20 -0700 | [diff] [blame] | 22 |  | 
| Daniel Norman | 3f42a76 | 2019-07-09 11:00:53 -0700 | [diff] [blame] | 23 | #include <algorithm> | 
 | 24 | #include <sstream> | 
 | 25 |  | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 26 | #include <android-base/logging.h> | 
 | 27 | #include <android-base/parseint.h> | 
 | 28 | #include <android-base/strings.h> | 
 | 29 | #include <hidl-util/FQName.h> | 
 | 30 | #include <system/thread_defs.h> | 
 | 31 |  | 
| Suren Baghdasaryan | c29c2ba | 2019-10-22 17:18:42 -0700 | [diff] [blame] | 32 | #include "lmkd_service.h" | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 33 | #include "rlimit_parser.h" | 
| Tom Cherry | 2e4c85f | 2019-07-09 13:33:36 -0700 | [diff] [blame] | 34 | #include "service_utils.h" | 
| Tom Cherry | 2aeb1ad | 2019-06-26 10:46:20 -0700 | [diff] [blame] | 35 | #include "util.h" | 
 | 36 |  | 
| Tom Cherry | a2f9136 | 2020-02-20 10:50:00 -0800 | [diff] [blame] | 37 | #ifdef INIT_FULL_SOURCES | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 38 | #include <android/api-level.h> | 
| Tom Cherry | 2aeb1ad | 2019-06-26 10:46:20 -0700 | [diff] [blame] | 39 | #include <sys/system_properties.h> | 
 | 40 |  | 
 | 41 | #include "selinux.h" | 
 | 42 | #else | 
 | 43 | #include "host_init_stubs.h" | 
 | 44 | #endif | 
 | 45 |  | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 46 | using android::base::ParseInt; | 
 | 47 | using android::base::Split; | 
| Tom Cherry | 2aeb1ad | 2019-06-26 10:46:20 -0700 | [diff] [blame] | 48 | using android::base::StartsWith; | 
 | 49 |  | 
 | 50 | namespace android { | 
 | 51 | namespace init { | 
 | 52 |  | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 53 | Result<void> ServiceParser::ParseCapabilities(std::vector<std::string>&& args) { | 
 | 54 |     service_->capabilities_ = 0; | 
 | 55 |  | 
 | 56 |     if (!CapAmbientSupported()) { | 
 | 57 |         return Error() | 
 | 58 |                << "capabilities requested but the kernel does not support ambient capabilities"; | 
 | 59 |     } | 
 | 60 |  | 
 | 61 |     unsigned int last_valid_cap = GetLastValidCap(); | 
 | 62 |     if (last_valid_cap >= service_->capabilities_->size()) { | 
 | 63 |         LOG(WARNING) << "last valid run-time capability is larger than CAP_LAST_CAP"; | 
 | 64 |     } | 
 | 65 |  | 
 | 66 |     for (size_t i = 1; i < args.size(); i++) { | 
 | 67 |         const std::string& arg = args[i]; | 
 | 68 |         int res = LookupCap(arg); | 
 | 69 |         if (res < 0) { | 
 | 70 |             return Errorf("invalid capability '{}'", arg); | 
 | 71 |         } | 
 | 72 |         unsigned int cap = static_cast<unsigned int>(res);  // |res| is >= 0. | 
 | 73 |         if (cap > last_valid_cap) { | 
 | 74 |             return Errorf("capability '{}' not supported by the kernel", arg); | 
 | 75 |         } | 
 | 76 |         (*service_->capabilities_)[cap] = true; | 
 | 77 |     } | 
 | 78 |     return {}; | 
 | 79 | } | 
 | 80 |  | 
 | 81 | Result<void> ServiceParser::ParseClass(std::vector<std::string>&& args) { | 
 | 82 |     service_->classnames_ = std::set<std::string>(args.begin() + 1, args.end()); | 
 | 83 |     return {}; | 
 | 84 | } | 
 | 85 |  | 
 | 86 | Result<void> ServiceParser::ParseConsole(std::vector<std::string>&& args) { | 
| Tom Cherry | f74b7f5 | 2019-09-23 16:16:54 -0700 | [diff] [blame] | 87 |     if (service_->proc_attr_.stdio_to_kmsg) { | 
 | 88 |         return Error() << "'console' and 'stdio_to_kmsg' are mutually exclusive"; | 
 | 89 |     } | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 90 |     service_->flags_ |= SVC_CONSOLE; | 
 | 91 |     service_->proc_attr_.console = args.size() > 1 ? "/dev/" + args[1] : ""; | 
 | 92 |     return {}; | 
 | 93 | } | 
 | 94 |  | 
 | 95 | Result<void> ServiceParser::ParseCritical(std::vector<std::string>&& args) { | 
 | 96 |     service_->flags_ |= SVC_CRITICAL; | 
 | 97 |     return {}; | 
 | 98 | } | 
 | 99 |  | 
 | 100 | Result<void> ServiceParser::ParseDisabled(std::vector<std::string>&& args) { | 
 | 101 |     service_->flags_ |= SVC_DISABLED; | 
 | 102 |     service_->flags_ |= SVC_RC_DISABLED; | 
 | 103 |     return {}; | 
 | 104 | } | 
 | 105 |  | 
 | 106 | Result<void> ServiceParser::ParseEnterNamespace(std::vector<std::string>&& args) { | 
 | 107 |     if (args[1] != "net") { | 
 | 108 |         return Error() << "Init only supports entering network namespaces"; | 
 | 109 |     } | 
 | 110 |     if (!service_->namespaces_.namespaces_to_enter.empty()) { | 
 | 111 |         return Error() << "Only one network namespace may be entered"; | 
 | 112 |     } | 
 | 113 |     // Network namespaces require that /sys is remounted, otherwise the old adapters will still be | 
 | 114 |     // present. Therefore, they also require mount namespaces. | 
 | 115 |     service_->namespaces_.flags |= CLONE_NEWNS; | 
 | 116 |     service_->namespaces_.namespaces_to_enter.emplace_back(CLONE_NEWNET, std::move(args[2])); | 
 | 117 |     return {}; | 
 | 118 | } | 
 | 119 |  | 
 | 120 | Result<void> ServiceParser::ParseGroup(std::vector<std::string>&& args) { | 
 | 121 |     auto gid = DecodeUid(args[1]); | 
| Bernie Innocenti | cecebbb | 2020-02-06 03:49:33 +0900 | [diff] [blame] | 122 |     if (!gid.ok()) { | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 123 |         return Error() << "Unable to decode GID for '" << args[1] << "': " << gid.error(); | 
 | 124 |     } | 
 | 125 |     service_->proc_attr_.gid = *gid; | 
 | 126 |  | 
 | 127 |     for (std::size_t n = 2; n < args.size(); n++) { | 
 | 128 |         gid = DecodeUid(args[n]); | 
| Bernie Innocenti | cecebbb | 2020-02-06 03:49:33 +0900 | [diff] [blame] | 129 |         if (!gid.ok()) { | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 130 |             return Error() << "Unable to decode GID for '" << args[n] << "': " << gid.error(); | 
 | 131 |         } | 
 | 132 |         service_->proc_attr_.supp_gids.emplace_back(*gid); | 
 | 133 |     } | 
 | 134 |     return {}; | 
 | 135 | } | 
 | 136 |  | 
 | 137 | Result<void> ServiceParser::ParsePriority(std::vector<std::string>&& args) { | 
 | 138 |     service_->proc_attr_.priority = 0; | 
 | 139 |     if (!ParseInt(args[1], &service_->proc_attr_.priority, | 
 | 140 |                   static_cast<int>(ANDROID_PRIORITY_HIGHEST),  // highest is negative | 
 | 141 |                   static_cast<int>(ANDROID_PRIORITY_LOWEST))) { | 
 | 142 |         return Errorf("process priority value must be range {} - {}", ANDROID_PRIORITY_HIGHEST, | 
 | 143 |                       ANDROID_PRIORITY_LOWEST); | 
 | 144 |     } | 
 | 145 |     return {}; | 
 | 146 | } | 
 | 147 |  | 
 | 148 | Result<void> ServiceParser::ParseInterface(std::vector<std::string>&& args) { | 
 | 149 |     const std::string& interface_name = args[1]; | 
 | 150 |     const std::string& instance_name = args[2]; | 
 | 151 |  | 
| Jon Spivack | 16fb3f9 | 2019-07-26 13:14:42 -0700 | [diff] [blame] | 152 |     // AIDL services don't use fully qualified names and instead just use "interface aidl <name>" | 
 | 153 |     if (interface_name != "aidl") { | 
 | 154 |         FQName fq_name; | 
 | 155 |         if (!FQName::parse(interface_name, &fq_name)) { | 
 | 156 |             return Error() << "Invalid fully-qualified name for interface '" << interface_name | 
 | 157 |                            << "'"; | 
 | 158 |         } | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 159 |  | 
| Jon Spivack | 16fb3f9 | 2019-07-26 13:14:42 -0700 | [diff] [blame] | 160 |         if (!fq_name.isFullyQualified()) { | 
 | 161 |             return Error() << "Interface name not fully-qualified '" << interface_name << "'"; | 
 | 162 |         } | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 163 |  | 
| Jon Spivack | 16fb3f9 | 2019-07-26 13:14:42 -0700 | [diff] [blame] | 164 |         if (fq_name.isValidValueName()) { | 
 | 165 |             return Error() << "Interface name must not be a value name '" << interface_name << "'"; | 
 | 166 |         } | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 167 |     } | 
 | 168 |  | 
 | 169 |     const std::string fullname = interface_name + "/" + instance_name; | 
 | 170 |  | 
 | 171 |     for (const auto& svc : *service_list_) { | 
 | 172 |         if (svc->interfaces().count(fullname) > 0) { | 
 | 173 |             return Error() << "Interface '" << fullname << "' redefined in " << service_->name() | 
 | 174 |                            << " but is already defined by " << svc->name(); | 
 | 175 |         } | 
 | 176 |     } | 
 | 177 |  | 
 | 178 |     service_->interfaces_.insert(fullname); | 
 | 179 |  | 
 | 180 |     return {}; | 
 | 181 | } | 
 | 182 |  | 
 | 183 | Result<void> ServiceParser::ParseIoprio(std::vector<std::string>&& args) { | 
 | 184 |     if (!ParseInt(args[2], &service_->proc_attr_.ioprio_pri, 0, 7)) { | 
 | 185 |         return Error() << "priority value must be range 0 - 7"; | 
 | 186 |     } | 
 | 187 |  | 
 | 188 |     if (args[1] == "rt") { | 
 | 189 |         service_->proc_attr_.ioprio_class = IoSchedClass_RT; | 
 | 190 |     } else if (args[1] == "be") { | 
 | 191 |         service_->proc_attr_.ioprio_class = IoSchedClass_BE; | 
 | 192 |     } else if (args[1] == "idle") { | 
 | 193 |         service_->proc_attr_.ioprio_class = IoSchedClass_IDLE; | 
 | 194 |     } else { | 
 | 195 |         return Error() << "ioprio option usage: ioprio <rt|be|idle> <0-7>"; | 
 | 196 |     } | 
 | 197 |  | 
 | 198 |     return {}; | 
 | 199 | } | 
 | 200 |  | 
 | 201 | Result<void> ServiceParser::ParseKeycodes(std::vector<std::string>&& args) { | 
 | 202 |     auto it = args.begin() + 1; | 
 | 203 |     if (args.size() == 2 && StartsWith(args[1], "$")) { | 
| Tom Cherry | c5cf85d | 2019-07-31 13:59:15 -0700 | [diff] [blame] | 204 |         auto expanded = ExpandProps(args[1]); | 
| Bernie Innocenti | cecebbb | 2020-02-06 03:49:33 +0900 | [diff] [blame] | 205 |         if (!expanded.ok()) { | 
| Tom Cherry | c5cf85d | 2019-07-31 13:59:15 -0700 | [diff] [blame] | 206 |             return expanded.error(); | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 207 |         } | 
 | 208 |  | 
 | 209 |         // If the property is not set, it defaults to none, in which case there are no keycodes | 
 | 210 |         // for this service. | 
| Bernie Innocenti | 1cc76df | 2020-02-03 23:54:02 +0900 | [diff] [blame] | 211 |         if (*expanded == "none") { | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 212 |             return {}; | 
 | 213 |         } | 
 | 214 |  | 
| Tom Cherry | c5cf85d | 2019-07-31 13:59:15 -0700 | [diff] [blame] | 215 |         args = Split(*expanded, ","); | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 216 |         it = args.begin(); | 
 | 217 |     } | 
 | 218 |  | 
 | 219 |     for (; it != args.end(); ++it) { | 
 | 220 |         int code; | 
 | 221 |         if (ParseInt(*it, &code, 0, KEY_MAX)) { | 
 | 222 |             for (auto& key : service_->keycodes_) { | 
 | 223 |                 if (key == code) return Error() << "duplicate keycode: " << *it; | 
 | 224 |             } | 
 | 225 |             service_->keycodes_.insert( | 
 | 226 |                     std::upper_bound(service_->keycodes_.begin(), service_->keycodes_.end(), code), | 
 | 227 |                     code); | 
 | 228 |         } else { | 
 | 229 |             return Error() << "invalid keycode: " << *it; | 
 | 230 |         } | 
 | 231 |     } | 
 | 232 |     return {}; | 
 | 233 | } | 
 | 234 |  | 
 | 235 | Result<void> ServiceParser::ParseOneshot(std::vector<std::string>&& args) { | 
 | 236 |     service_->flags_ |= SVC_ONESHOT; | 
 | 237 |     return {}; | 
 | 238 | } | 
 | 239 |  | 
 | 240 | Result<void> ServiceParser::ParseOnrestart(std::vector<std::string>&& args) { | 
 | 241 |     args.erase(args.begin()); | 
 | 242 |     int line = service_->onrestart_.NumCommands() + 1; | 
| Bernie Innocenti | cecebbb | 2020-02-06 03:49:33 +0900 | [diff] [blame] | 243 |     if (auto result = service_->onrestart_.AddCommand(std::move(args), line); !result.ok()) { | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 244 |         return Error() << "cannot add Onrestart command: " << result.error(); | 
 | 245 |     } | 
 | 246 |     return {}; | 
 | 247 | } | 
 | 248 |  | 
 | 249 | Result<void> ServiceParser::ParseNamespace(std::vector<std::string>&& args) { | 
 | 250 |     for (size_t i = 1; i < args.size(); i++) { | 
 | 251 |         if (args[i] == "pid") { | 
 | 252 |             service_->namespaces_.flags |= CLONE_NEWPID; | 
 | 253 |             // PID namespaces require mount namespaces. | 
 | 254 |             service_->namespaces_.flags |= CLONE_NEWNS; | 
 | 255 |         } else if (args[i] == "mnt") { | 
 | 256 |             service_->namespaces_.flags |= CLONE_NEWNS; | 
 | 257 |         } else { | 
 | 258 |             return Error() << "namespace must be 'pid' or 'mnt'"; | 
 | 259 |         } | 
 | 260 |     } | 
 | 261 |     return {}; | 
 | 262 | } | 
 | 263 |  | 
 | 264 | Result<void> ServiceParser::ParseOomScoreAdjust(std::vector<std::string>&& args) { | 
| Suren Baghdasaryan | c29c2ba | 2019-10-22 17:18:42 -0700 | [diff] [blame] | 265 |     if (!ParseInt(args[1], &service_->oom_score_adjust_, MIN_OOM_SCORE_ADJUST, | 
 | 266 |                   MAX_OOM_SCORE_ADJUST)) { | 
 | 267 |         return Error() << "oom_score_adjust value must be in range " << MIN_OOM_SCORE_ADJUST | 
 | 268 |                        << " - +" << MAX_OOM_SCORE_ADJUST; | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 269 |     } | 
 | 270 |     return {}; | 
 | 271 | } | 
 | 272 |  | 
 | 273 | Result<void> ServiceParser::ParseOverride(std::vector<std::string>&& args) { | 
 | 274 |     service_->override_ = true; | 
 | 275 |     return {}; | 
 | 276 | } | 
 | 277 |  | 
 | 278 | Result<void> ServiceParser::ParseMemcgSwappiness(std::vector<std::string>&& args) { | 
 | 279 |     if (!ParseInt(args[1], &service_->swappiness_, 0)) { | 
 | 280 |         return Error() << "swappiness value must be equal or greater than 0"; | 
 | 281 |     } | 
 | 282 |     return {}; | 
 | 283 | } | 
 | 284 |  | 
 | 285 | Result<void> ServiceParser::ParseMemcgLimitInBytes(std::vector<std::string>&& args) { | 
 | 286 |     if (!ParseInt(args[1], &service_->limit_in_bytes_, 0)) { | 
 | 287 |         return Error() << "limit_in_bytes value must be equal or greater than 0"; | 
 | 288 |     } | 
 | 289 |     return {}; | 
 | 290 | } | 
 | 291 |  | 
 | 292 | Result<void> ServiceParser::ParseMemcgLimitPercent(std::vector<std::string>&& args) { | 
 | 293 |     if (!ParseInt(args[1], &service_->limit_percent_, 0)) { | 
 | 294 |         return Error() << "limit_percent value must be equal or greater than 0"; | 
 | 295 |     } | 
 | 296 |     return {}; | 
 | 297 | } | 
 | 298 |  | 
 | 299 | Result<void> ServiceParser::ParseMemcgLimitProperty(std::vector<std::string>&& args) { | 
 | 300 |     service_->limit_property_ = std::move(args[1]); | 
 | 301 |     return {}; | 
 | 302 | } | 
 | 303 |  | 
 | 304 | Result<void> ServiceParser::ParseMemcgSoftLimitInBytes(std::vector<std::string>&& args) { | 
 | 305 |     if (!ParseInt(args[1], &service_->soft_limit_in_bytes_, 0)) { | 
 | 306 |         return Error() << "soft_limit_in_bytes value must be equal or greater than 0"; | 
 | 307 |     } | 
 | 308 |     return {}; | 
 | 309 | } | 
 | 310 |  | 
 | 311 | Result<void> ServiceParser::ParseProcessRlimit(std::vector<std::string>&& args) { | 
 | 312 |     auto rlimit = ParseRlimit(args); | 
| Bernie Innocenti | cecebbb | 2020-02-06 03:49:33 +0900 | [diff] [blame] | 313 |     if (!rlimit.ok()) return rlimit.error(); | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 314 |  | 
 | 315 |     service_->proc_attr_.rlimits.emplace_back(*rlimit); | 
 | 316 |     return {}; | 
 | 317 | } | 
 | 318 |  | 
| Tom Cherry | 60971e6 | 2019-09-10 10:40:47 -0700 | [diff] [blame] | 319 | Result<void> ServiceParser::ParseRebootOnFailure(std::vector<std::string>&& args) { | 
 | 320 |     if (service_->on_failure_reboot_target_) { | 
 | 321 |         return Error() << "Only one reboot_on_failure command may be specified"; | 
 | 322 |     } | 
 | 323 |     if (!StartsWith(args[1], "shutdown") && !StartsWith(args[1], "reboot")) { | 
 | 324 |         return Error() | 
 | 325 |                << "reboot_on_failure commands must begin with either 'shutdown' or 'reboot'"; | 
 | 326 |     } | 
 | 327 |     service_->on_failure_reboot_target_ = std::move(args[1]); | 
 | 328 |     return {}; | 
 | 329 | } | 
 | 330 |  | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 331 | Result<void> ServiceParser::ParseRestartPeriod(std::vector<std::string>&& args) { | 
 | 332 |     int period; | 
 | 333 |     if (!ParseInt(args[1], &period, 5)) { | 
 | 334 |         return Error() << "restart_period value must be an integer >= 5"; | 
 | 335 |     } | 
 | 336 |     service_->restart_period_ = std::chrono::seconds(period); | 
 | 337 |     return {}; | 
 | 338 | } | 
 | 339 |  | 
 | 340 | Result<void> ServiceParser::ParseSeclabel(std::vector<std::string>&& args) { | 
 | 341 |     service_->seclabel_ = std::move(args[1]); | 
 | 342 |     return {}; | 
 | 343 | } | 
 | 344 |  | 
 | 345 | Result<void> ServiceParser::ParseSigstop(std::vector<std::string>&& args) { | 
 | 346 |     service_->sigstop_ = true; | 
 | 347 |     return {}; | 
 | 348 | } | 
 | 349 |  | 
 | 350 | Result<void> ServiceParser::ParseSetenv(std::vector<std::string>&& args) { | 
 | 351 |     service_->environment_vars_.emplace_back(std::move(args[1]), std::move(args[2])); | 
 | 352 |     return {}; | 
 | 353 | } | 
 | 354 |  | 
 | 355 | Result<void> ServiceParser::ParseShutdown(std::vector<std::string>&& args) { | 
 | 356 |     if (args[1] == "critical") { | 
 | 357 |         service_->flags_ |= SVC_SHUTDOWN_CRITICAL; | 
 | 358 |         return {}; | 
 | 359 |     } | 
 | 360 |     return Error() << "Invalid shutdown option"; | 
 | 361 | } | 
 | 362 |  | 
 | 363 | Result<void> ServiceParser::ParseTimeoutPeriod(std::vector<std::string>&& args) { | 
 | 364 |     int period; | 
 | 365 |     if (!ParseInt(args[1], &period, 1)) { | 
 | 366 |         return Error() << "timeout_period value must be an integer >= 1"; | 
 | 367 |     } | 
 | 368 |     service_->timeout_period_ = std::chrono::seconds(period); | 
 | 369 |     return {}; | 
 | 370 | } | 
 | 371 |  | 
| Tom Cherry | 2e4c85f | 2019-07-09 13:33:36 -0700 | [diff] [blame] | 372 | // name type perm [ uid gid context ] | 
 | 373 | Result<void> ServiceParser::ParseSocket(std::vector<std::string>&& args) { | 
 | 374 |     SocketDescriptor socket; | 
 | 375 |     socket.name = std::move(args[1]); | 
 | 376 |  | 
 | 377 |     auto types = Split(args[2], "+"); | 
 | 378 |     if (types[0] == "stream") { | 
 | 379 |         socket.type = SOCK_STREAM; | 
 | 380 |     } else if (types[0] == "dgram") { | 
 | 381 |         socket.type = SOCK_DGRAM; | 
 | 382 |     } else if (types[0] == "seqpacket") { | 
 | 383 |         socket.type = SOCK_SEQPACKET; | 
 | 384 |     } else { | 
 | 385 |         return Error() << "socket type must be 'dgram', 'stream' or 'seqpacket', got '" << types[0] | 
 | 386 |                        << "' instead."; | 
 | 387 |     } | 
 | 388 |  | 
 | 389 |     if (types.size() > 1) { | 
 | 390 |         if (types.size() == 2 && types[1] == "passcred") { | 
 | 391 |             socket.passcred = true; | 
 | 392 |         } else { | 
 | 393 |             return Error() << "Only 'passcred' may be used to modify the socket type"; | 
 | 394 |         } | 
 | 395 |     } | 
 | 396 |  | 
 | 397 |     errno = 0; | 
 | 398 |     char* end = nullptr; | 
 | 399 |     socket.perm = strtol(args[3].c_str(), &end, 8); | 
 | 400 |     if (errno != 0) { | 
 | 401 |         return ErrnoError() << "Unable to parse permissions '" << args[3] << "'"; | 
 | 402 |     } | 
 | 403 |     if (end == args[3].c_str() || *end != '\0') { | 
 | 404 |         errno = EINVAL; | 
 | 405 |         return ErrnoError() << "Unable to parse permissions '" << args[3] << "'"; | 
 | 406 |     } | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 407 |  | 
 | 408 |     if (args.size() > 4) { | 
| Tom Cherry | 2e4c85f | 2019-07-09 13:33:36 -0700 | [diff] [blame] | 409 |         auto uid = DecodeUid(args[4]); | 
| Bernie Innocenti | cecebbb | 2020-02-06 03:49:33 +0900 | [diff] [blame] | 410 |         if (!uid.ok()) { | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 411 |             return Error() << "Unable to find UID for '" << args[4] << "': " << uid.error(); | 
 | 412 |         } | 
| Tom Cherry | 2e4c85f | 2019-07-09 13:33:36 -0700 | [diff] [blame] | 413 |         socket.uid = *uid; | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 414 |     } | 
 | 415 |  | 
 | 416 |     if (args.size() > 5) { | 
| Tom Cherry | 2e4c85f | 2019-07-09 13:33:36 -0700 | [diff] [blame] | 417 |         auto gid = DecodeUid(args[5]); | 
| Bernie Innocenti | cecebbb | 2020-02-06 03:49:33 +0900 | [diff] [blame] | 418 |         if (!gid.ok()) { | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 419 |             return Error() << "Unable to find GID for '" << args[5] << "': " << gid.error(); | 
 | 420 |         } | 
| Tom Cherry | 2e4c85f | 2019-07-09 13:33:36 -0700 | [diff] [blame] | 421 |         socket.gid = *gid; | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 422 |     } | 
 | 423 |  | 
| Tom Cherry | 2e4c85f | 2019-07-09 13:33:36 -0700 | [diff] [blame] | 424 |     socket.context = args.size() > 6 ? args[6] : ""; | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 425 |  | 
| Tom Cherry | 2e4c85f | 2019-07-09 13:33:36 -0700 | [diff] [blame] | 426 |     auto old = std::find_if(service_->sockets_.begin(), service_->sockets_.end(), | 
 | 427 |                             [&socket](const auto& other) { return socket.name == other.name; }); | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 428 |  | 
| Tom Cherry | 2e4c85f | 2019-07-09 13:33:36 -0700 | [diff] [blame] | 429 |     if (old != service_->sockets_.end()) { | 
 | 430 |         return Error() << "duplicate socket descriptor '" << socket.name << "'"; | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 431 |     } | 
 | 432 |  | 
| Tom Cherry | 2e4c85f | 2019-07-09 13:33:36 -0700 | [diff] [blame] | 433 |     service_->sockets_.emplace_back(std::move(socket)); | 
 | 434 |  | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 435 |     return {}; | 
 | 436 | } | 
 | 437 |  | 
| Tom Cherry | f74b7f5 | 2019-09-23 16:16:54 -0700 | [diff] [blame] | 438 | Result<void> ServiceParser::ParseStdioToKmsg(std::vector<std::string>&& args) { | 
 | 439 |     if (service_->flags_ & SVC_CONSOLE) { | 
 | 440 |         return Error() << "'stdio_to_kmsg' and 'console' are mutually exclusive"; | 
 | 441 |     } | 
 | 442 |     service_->proc_attr_.stdio_to_kmsg = true; | 
 | 443 |     return {}; | 
 | 444 | } | 
 | 445 |  | 
| Tom Cherry | 2e4c85f | 2019-07-09 13:33:36 -0700 | [diff] [blame] | 446 | // name type | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 447 | Result<void> ServiceParser::ParseFile(std::vector<std::string>&& args) { | 
 | 448 |     if (args[2] != "r" && args[2] != "w" && args[2] != "rw") { | 
 | 449 |         return Error() << "file type must be 'r', 'w' or 'rw'"; | 
 | 450 |     } | 
| Tom Cherry | 2e4c85f | 2019-07-09 13:33:36 -0700 | [diff] [blame] | 451 |  | 
 | 452 |     FileDescriptor file; | 
 | 453 |     file.type = args[2]; | 
 | 454 |  | 
| Tom Cherry | c5cf85d | 2019-07-31 13:59:15 -0700 | [diff] [blame] | 455 |     auto file_name = ExpandProps(args[1]); | 
| Bernie Innocenti | cecebbb | 2020-02-06 03:49:33 +0900 | [diff] [blame] | 456 |     if (!file_name.ok()) { | 
| Tom Cherry | c5cf85d | 2019-07-31 13:59:15 -0700 | [diff] [blame] | 457 |         return Error() << "Could not expand file path ': " << file_name.error(); | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 458 |     } | 
| Tom Cherry | c5cf85d | 2019-07-31 13:59:15 -0700 | [diff] [blame] | 459 |     file.name = *file_name; | 
| Tom Cherry | 2e4c85f | 2019-07-09 13:33:36 -0700 | [diff] [blame] | 460 |     if (file.name[0] != '/' || file.name.find("../") != std::string::npos) { | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 461 |         return Error() << "file name must not be relative"; | 
 | 462 |     } | 
| Tom Cherry | 2e4c85f | 2019-07-09 13:33:36 -0700 | [diff] [blame] | 463 |  | 
 | 464 |     auto old = std::find_if(service_->files_.begin(), service_->files_.end(), | 
 | 465 |                             [&file](const auto& other) { return other.name == file.name; }); | 
 | 466 |  | 
 | 467 |     if (old != service_->files_.end()) { | 
 | 468 |         return Error() << "duplicate file descriptor '" << file.name << "'"; | 
 | 469 |     } | 
 | 470 |  | 
 | 471 |     service_->files_.emplace_back(std::move(file)); | 
 | 472 |  | 
 | 473 |     return {}; | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 474 | } | 
 | 475 |  | 
 | 476 | Result<void> ServiceParser::ParseUser(std::vector<std::string>&& args) { | 
 | 477 |     auto uid = DecodeUid(args[1]); | 
| Bernie Innocenti | cecebbb | 2020-02-06 03:49:33 +0900 | [diff] [blame] | 478 |     if (!uid.ok()) { | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 479 |         return Error() << "Unable to find UID for '" << args[1] << "': " << uid.error(); | 
 | 480 |     } | 
 | 481 |     service_->proc_attr_.uid = *uid; | 
 | 482 |     return {}; | 
 | 483 | } | 
 | 484 |  | 
 | 485 | Result<void> ServiceParser::ParseWritepid(std::vector<std::string>&& args) { | 
 | 486 |     args.erase(args.begin()); | 
 | 487 |     service_->writepid_files_ = std::move(args); | 
 | 488 |     return {}; | 
 | 489 | } | 
 | 490 |  | 
 | 491 | Result<void> ServiceParser::ParseUpdatable(std::vector<std::string>&& args) { | 
 | 492 |     service_->updatable_ = true; | 
 | 493 |     return {}; | 
 | 494 | } | 
 | 495 |  | 
| Tom Cherry | d52a5b3 | 2019-07-22 16:05:36 -0700 | [diff] [blame] | 496 | const KeywordMap<ServiceParser::OptionParser>& ServiceParser::GetParserMap() const { | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 497 |     constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max(); | 
 | 498 |     // clang-format off | 
| Tom Cherry | d52a5b3 | 2019-07-22 16:05:36 -0700 | [diff] [blame] | 499 |     static const KeywordMap<ServiceParser::OptionParser> parser_map = { | 
| Tom Cherry | 60971e6 | 2019-09-10 10:40:47 -0700 | [diff] [blame] | 500 |         {"capabilities",            {0,     kMax, &ServiceParser::ParseCapabilities}}, | 
 | 501 |         {"class",                   {1,     kMax, &ServiceParser::ParseClass}}, | 
 | 502 |         {"console",                 {0,     1,    &ServiceParser::ParseConsole}}, | 
 | 503 |         {"critical",                {0,     0,    &ServiceParser::ParseCritical}}, | 
 | 504 |         {"disabled",                {0,     0,    &ServiceParser::ParseDisabled}}, | 
 | 505 |         {"enter_namespace",         {2,     2,    &ServiceParser::ParseEnterNamespace}}, | 
 | 506 |         {"file",                    {2,     2,    &ServiceParser::ParseFile}}, | 
 | 507 |         {"group",                   {1,     NR_SVC_SUPP_GIDS + 1, &ServiceParser::ParseGroup}}, | 
 | 508 |         {"interface",               {2,     2,    &ServiceParser::ParseInterface}}, | 
 | 509 |         {"ioprio",                  {2,     2,    &ServiceParser::ParseIoprio}}, | 
 | 510 |         {"keycodes",                {1,     kMax, &ServiceParser::ParseKeycodes}}, | 
 | 511 |         {"memcg.limit_in_bytes",    {1,     1,    &ServiceParser::ParseMemcgLimitInBytes}}, | 
 | 512 |         {"memcg.limit_percent",     {1,     1,    &ServiceParser::ParseMemcgLimitPercent}}, | 
 | 513 |         {"memcg.limit_property",    {1,     1,    &ServiceParser::ParseMemcgLimitProperty}}, | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 514 |         {"memcg.soft_limit_in_bytes", | 
| Tom Cherry | 60971e6 | 2019-09-10 10:40:47 -0700 | [diff] [blame] | 515 |                                     {1,     1,    &ServiceParser::ParseMemcgSoftLimitInBytes}}, | 
 | 516 |         {"memcg.swappiness",        {1,     1,    &ServiceParser::ParseMemcgSwappiness}}, | 
 | 517 |         {"namespace",               {1,     2,    &ServiceParser::ParseNamespace}}, | 
 | 518 |         {"oneshot",                 {0,     0,    &ServiceParser::ParseOneshot}}, | 
 | 519 |         {"onrestart",               {1,     kMax, &ServiceParser::ParseOnrestart}}, | 
 | 520 |         {"oom_score_adjust",        {1,     1,    &ServiceParser::ParseOomScoreAdjust}}, | 
 | 521 |         {"override",                {0,     0,    &ServiceParser::ParseOverride}}, | 
 | 522 |         {"priority",                {1,     1,    &ServiceParser::ParsePriority}}, | 
 | 523 |         {"reboot_on_failure",       {1,     1,    &ServiceParser::ParseRebootOnFailure}}, | 
 | 524 |         {"restart_period",          {1,     1,    &ServiceParser::ParseRestartPeriod}}, | 
 | 525 |         {"rlimit",                  {3,     3,    &ServiceParser::ParseProcessRlimit}}, | 
 | 526 |         {"seclabel",                {1,     1,    &ServiceParser::ParseSeclabel}}, | 
 | 527 |         {"setenv",                  {2,     2,    &ServiceParser::ParseSetenv}}, | 
 | 528 |         {"shutdown",                {1,     1,    &ServiceParser::ParseShutdown}}, | 
 | 529 |         {"sigstop",                 {0,     0,    &ServiceParser::ParseSigstop}}, | 
 | 530 |         {"socket",                  {3,     6,    &ServiceParser::ParseSocket}}, | 
| Tom Cherry | f74b7f5 | 2019-09-23 16:16:54 -0700 | [diff] [blame] | 531 |         {"stdio_to_kmsg",           {0,     0,    &ServiceParser::ParseStdioToKmsg}}, | 
| Tom Cherry | 60971e6 | 2019-09-10 10:40:47 -0700 | [diff] [blame] | 532 |         {"timeout_period",          {1,     1,    &ServiceParser::ParseTimeoutPeriod}}, | 
 | 533 |         {"updatable",               {0,     0,    &ServiceParser::ParseUpdatable}}, | 
 | 534 |         {"user",                    {1,     1,    &ServiceParser::ParseUser}}, | 
 | 535 |         {"writepid",                {1,     kMax, &ServiceParser::ParseWritepid}}, | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 536 |     }; | 
 | 537 |     // clang-format on | 
| Tom Cherry | d52a5b3 | 2019-07-22 16:05:36 -0700 | [diff] [blame] | 538 |     return parser_map; | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 539 | } | 
 | 540 |  | 
| Tom Cherry | 2aeb1ad | 2019-06-26 10:46:20 -0700 | [diff] [blame] | 541 | Result<void> ServiceParser::ParseSection(std::vector<std::string>&& args, | 
 | 542 |                                          const std::string& filename, int line) { | 
 | 543 |     if (args.size() < 3) { | 
 | 544 |         return Error() << "services must have a name and a program"; | 
 | 545 |     } | 
 | 546 |  | 
 | 547 |     const std::string& name = args[1]; | 
 | 548 |     if (!IsValidName(name)) { | 
 | 549 |         return Error() << "invalid service name '" << name << "'"; | 
 | 550 |     } | 
 | 551 |  | 
 | 552 |     filename_ = filename; | 
 | 553 |  | 
 | 554 |     Subcontext* restart_action_subcontext = nullptr; | 
| Tom Cherry | 14c2472 | 2019-09-18 13:47:19 -0700 | [diff] [blame] | 555 |     if (subcontext_ && subcontext_->PathMatchesSubcontext(filename)) { | 
 | 556 |         restart_action_subcontext = subcontext_; | 
| Tom Cherry | 2aeb1ad | 2019-06-26 10:46:20 -0700 | [diff] [blame] | 557 |     } | 
 | 558 |  | 
 | 559 |     std::vector<std::string> str_args(args.begin() + 2, args.end()); | 
 | 560 |  | 
 | 561 |     if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_P__) { | 
 | 562 |         if (str_args[0] == "/sbin/watchdogd") { | 
 | 563 |             str_args[0] = "/system/bin/watchdogd"; | 
 | 564 |         } | 
 | 565 |     } | 
| Yifan Hong | 8fb7f77 | 2019-10-16 14:22:12 -0700 | [diff] [blame] | 566 |     if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_Q__) { | 
 | 567 |         if (str_args[0] == "/charger") { | 
 | 568 |             str_args[0] = "/system/bin/charger"; | 
 | 569 |         } | 
 | 570 |     } | 
| Tom Cherry | 2aeb1ad | 2019-06-26 10:46:20 -0700 | [diff] [blame] | 571 |  | 
| Nikita Ioffe | 091c4d1 | 2019-12-05 12:35:19 +0000 | [diff] [blame] | 572 |     service_ = std::make_unique<Service>(name, restart_action_subcontext, str_args, from_apex_); | 
| Tom Cherry | 2aeb1ad | 2019-06-26 10:46:20 -0700 | [diff] [blame] | 573 |     return {}; | 
 | 574 | } | 
 | 575 |  | 
 | 576 | Result<void> ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line) { | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 577 |     if (!service_) { | 
 | 578 |         return {}; | 
 | 579 |     } | 
 | 580 |  | 
| Tom Cherry | d52a5b3 | 2019-07-22 16:05:36 -0700 | [diff] [blame] | 581 |     auto parser = GetParserMap().Find(args); | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 582 |  | 
| Bernie Innocenti | cecebbb | 2020-02-06 03:49:33 +0900 | [diff] [blame] | 583 |     if (!parser.ok()) return parser.error(); | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 584 |  | 
 | 585 |     return std::invoke(*parser, this, std::move(args)); | 
| Tom Cherry | 2aeb1ad | 2019-06-26 10:46:20 -0700 | [diff] [blame] | 586 | } | 
 | 587 |  | 
 | 588 | Result<void> ServiceParser::EndSection() { | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 589 |     if (!service_) { | 
 | 590 |         return {}; | 
 | 591 |     } | 
| Tom Cherry | 2aeb1ad | 2019-06-26 10:46:20 -0700 | [diff] [blame] | 592 |  | 
| Daniel Norman | 3f42a76 | 2019-07-09 11:00:53 -0700 | [diff] [blame] | 593 |     if (interface_inheritance_hierarchy_) { | 
| Daniel Norman | d2533c3 | 2019-08-02 15:13:50 -0700 | [diff] [blame] | 594 |         if (const auto& check_hierarchy_result = CheckInterfaceInheritanceHierarchy( | 
 | 595 |                     service_->interfaces(), *interface_inheritance_hierarchy_); | 
| Bernie Innocenti | cecebbb | 2020-02-06 03:49:33 +0900 | [diff] [blame] | 596 |             !check_hierarchy_result.ok()) { | 
| Daniel Norman | d2533c3 | 2019-08-02 15:13:50 -0700 | [diff] [blame] | 597 |             return Error() << check_hierarchy_result.error(); | 
| Daniel Norman | 3f42a76 | 2019-07-09 11:00:53 -0700 | [diff] [blame] | 598 |         } | 
 | 599 |     } | 
 | 600 |  | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 601 |     Service* old_service = service_list_->FindService(service_->name()); | 
 | 602 |     if (old_service) { | 
 | 603 |         if (!service_->is_override()) { | 
 | 604 |             return Error() << "ignored duplicate definition of service '" << service_->name() | 
 | 605 |                            << "'"; | 
| Tom Cherry | 2aeb1ad | 2019-06-26 10:46:20 -0700 | [diff] [blame] | 606 |         } | 
 | 607 |  | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 608 |         if (StartsWith(filename_, "/apex/") && !old_service->is_updatable()) { | 
 | 609 |             return Error() << "cannot update a non-updatable service '" << service_->name() | 
 | 610 |                            << "' with a config in APEX"; | 
 | 611 |         } | 
 | 612 |  | 
 | 613 |         service_list_->RemoveService(*old_service); | 
 | 614 |         old_service = nullptr; | 
| Tom Cherry | 2aeb1ad | 2019-06-26 10:46:20 -0700 | [diff] [blame] | 615 |     } | 
 | 616 |  | 
| Tom Cherry | b1ffb1d | 2019-06-26 11:22:52 -0700 | [diff] [blame] | 617 |     service_list_->AddService(std::move(service_)); | 
 | 618 |  | 
| Tom Cherry | 2aeb1ad | 2019-06-26 10:46:20 -0700 | [diff] [blame] | 619 |     return {}; | 
 | 620 | } | 
 | 621 |  | 
 | 622 | bool ServiceParser::IsValidName(const std::string& name) const { | 
 | 623 |     // Property names can be any length, but may only contain certain characters. | 
 | 624 |     // Property values can contain any characters, but may only be a certain length. | 
 | 625 |     // (The latter restriction is needed because `start` and `stop` work by writing | 
 | 626 |     // the service name to the "ctl.start" and "ctl.stop" properties.) | 
 | 627 |     return IsLegalPropertyName("init.svc." + name) && name.size() <= PROP_VALUE_MAX; | 
 | 628 | } | 
 | 629 |  | 
 | 630 | }  // namespace init | 
 | 631 | }  // namespace android |