| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 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 "service.h" | 
 | 18 |  | 
 | 19 | #include <fcntl.h> | 
| Jorge Lucangeli Obes | 1b3fa3d | 2016-04-21 15:35:09 -0700 | [diff] [blame] | 20 | #include <sched.h> | 
 | 21 | #include <sys/mount.h> | 
 | 22 | #include <sys/prctl.h> | 
| Vitalii Tomkiv | 081705c | 2016-05-18 17:36:30 -0700 | [diff] [blame] | 23 | #include <sys/resource.h> | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 24 | #include <sys/stat.h> | 
| Vitalii Tomkiv | 081705c | 2016-05-18 17:36:30 -0700 | [diff] [blame] | 25 | #include <sys/time.h> | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 26 | #include <sys/types.h> | 
| Bertrand SIMONNET | b7e03e8 | 2015-12-18 11:39:59 -0800 | [diff] [blame] | 27 | #include <sys/wait.h> | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 28 | #include <termios.h> | 
| Dan Albert | af9ba4d | 2015-08-11 16:37:04 -0700 | [diff] [blame] | 29 | #include <unistd.h> | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 30 |  | 
 | 31 | #include <selinux/selinux.h> | 
 | 32 |  | 
| Elliott Hughes | 4f71319 | 2015-12-04 22:00:26 -0800 | [diff] [blame] | 33 | #include <android-base/file.h> | 
 | 34 | #include <android-base/stringprintf.h> | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 35 | #include <android-base/strings.h> | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 36 | #include <cutils/android_reboot.h> | 
 | 37 | #include <cutils/sockets.h> | 
| Vitalii Tomkiv | 081705c | 2016-05-18 17:36:30 -0700 | [diff] [blame] | 38 | #include <system/thread_defs.h> | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 39 |  | 
| Collin Mulliner | f7e79b9 | 2016-06-01 21:03:55 +0000 | [diff] [blame] | 40 | #include <processgroup/processgroup.h> | 
 | 41 |  | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 42 | #include "action.h" | 
 | 43 | #include "init.h" | 
 | 44 | #include "init_parser.h" | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 45 | #include "log.h" | 
 | 46 | #include "property_service.h" | 
 | 47 | #include "util.h" | 
 | 48 |  | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 49 | using android::base::StringPrintf; | 
 | 50 | using android::base::WriteStringToFile; | 
 | 51 |  | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 52 | #define CRITICAL_CRASH_THRESHOLD    4       // if we crash >4 times ... | 
 | 53 | #define CRITICAL_CRASH_WINDOW       (4*60)  // ... in 4 minutes, goto recovery | 
 | 54 |  | 
| Jorge Lucangeli Obes | 1b3fa3d | 2016-04-21 15:35:09 -0700 | [diff] [blame] | 55 | static void SetUpPidNamespace(const std::string& service_name) { | 
 | 56 |     constexpr unsigned int kSafeFlags = MS_NODEV | MS_NOEXEC | MS_NOSUID; | 
 | 57 |  | 
 | 58 |     // It's OK to LOG(FATAL) in this function since it's running in the first | 
 | 59 |     // child process. | 
 | 60 |     if (mount("", "/proc", "proc", kSafeFlags | MS_REMOUNT, "") == -1) { | 
 | 61 |         PLOG(FATAL) << "couldn't remount(/proc)"; | 
 | 62 |     } | 
 | 63 |  | 
 | 64 |     if (prctl(PR_SET_NAME, service_name.c_str()) == -1) { | 
 | 65 |         PLOG(FATAL) << "couldn't set name"; | 
 | 66 |     } | 
 | 67 |  | 
 | 68 |     pid_t child_pid = fork(); | 
 | 69 |     if (child_pid == -1) { | 
 | 70 |         PLOG(FATAL) << "couldn't fork init inside the PID namespace"; | 
 | 71 |     } | 
 | 72 |  | 
 | 73 |     if (child_pid > 0) { | 
 | 74 |         // So that we exit with the right status. | 
 | 75 |         static int init_exitstatus = 0; | 
 | 76 |         signal(SIGTERM, [](int) { _exit(init_exitstatus); }); | 
 | 77 |  | 
 | 78 |         pid_t waited_pid; | 
 | 79 |         int status; | 
 | 80 |         while ((waited_pid = wait(&status)) > 0) { | 
 | 81 |              // This loop will end when there are no processes left inside the | 
 | 82 |              // PID namespace or when the init process inside the PID namespace | 
 | 83 |              // gets a signal. | 
 | 84 |             if (waited_pid == child_pid) { | 
 | 85 |                 init_exitstatus = status; | 
 | 86 |             } | 
 | 87 |         } | 
 | 88 |         if (!WIFEXITED(init_exitstatus)) { | 
 | 89 |             _exit(EXIT_FAILURE); | 
 | 90 |         } | 
 | 91 |         _exit(WEXITSTATUS(init_exitstatus)); | 
 | 92 |     } | 
 | 93 | } | 
 | 94 |  | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 95 | SocketInfo::SocketInfo() : uid(0), gid(0), perm(0) { | 
 | 96 | } | 
 | 97 |  | 
 | 98 | SocketInfo::SocketInfo(const std::string& name, const std::string& type, uid_t uid, | 
 | 99 |                        gid_t gid, int perm, const std::string& socketcon) | 
 | 100 |     : name(name), type(type), uid(uid), gid(gid), perm(perm), socketcon(socketcon) { | 
 | 101 | } | 
 | 102 |  | 
 | 103 | ServiceEnvironmentInfo::ServiceEnvironmentInfo() { | 
 | 104 | } | 
 | 105 |  | 
 | 106 | ServiceEnvironmentInfo::ServiceEnvironmentInfo(const std::string& name, | 
 | 107 |                                                const std::string& value) | 
 | 108 |     : name(name), value(value) { | 
 | 109 | } | 
 | 110 |  | 
 | 111 | Service::Service(const std::string& name, const std::string& classname, | 
 | 112 |                  const std::vector<std::string>& args) | 
 | 113 |     : name_(name), classname_(classname), flags_(0), pid_(0), time_started_(0), | 
| Jorge Lucangeli Obes | 1b3fa3d | 2016-04-21 15:35:09 -0700 | [diff] [blame] | 114 |       time_crashed_(0), nr_crashed_(0), uid_(0), gid_(0), namespace_flags_(0), | 
 | 115 |       seclabel_(""), ioprio_class_(IoSchedClass_NONE), ioprio_pri_(0), | 
 | 116 |       priority_(0), args_(args) { | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 117 |     onrestart_.InitSingleTrigger("onrestart"); | 
 | 118 | } | 
 | 119 |  | 
 | 120 | Service::Service(const std::string& name, const std::string& classname, | 
| Jorge Lucangeli Obes | 1b3fa3d | 2016-04-21 15:35:09 -0700 | [diff] [blame] | 121 |                  unsigned flags, uid_t uid, gid_t gid, | 
 | 122 |                  const std::vector<gid_t>& supp_gids, unsigned namespace_flags, | 
 | 123 |                  const std::string& seclabel, | 
 | 124 |                  const std::vector<std::string>& args) | 
 | 125 |     : name_(name), classname_(classname), flags_(flags), pid_(0), | 
 | 126 |       time_started_(0), time_crashed_(0), nr_crashed_(0), uid_(uid), gid_(gid), | 
 | 127 |       supp_gids_(supp_gids), namespace_flags_(namespace_flags), | 
 | 128 |       seclabel_(seclabel), ioprio_class_(IoSchedClass_NONE), ioprio_pri_(0), | 
 | 129 |       priority_(0), args_(args) { | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 130 |     onrestart_.InitSingleTrigger("onrestart"); | 
 | 131 | } | 
 | 132 |  | 
 | 133 | void Service::NotifyStateChange(const std::string& new_state) const { | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 134 |     if ((flags_ & SVC_EXEC) != 0) { | 
 | 135 |         // 'exec' commands don't have properties tracking their state. | 
 | 136 |         return; | 
 | 137 |     } | 
 | 138 |  | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 139 |     std::string prop_name = StringPrintf("init.svc.%s", name_.c_str()); | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 140 |     if (prop_name.length() >= PROP_NAME_MAX) { | 
 | 141 |         // If the property name would be too long, we can't set it. | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 142 |         LOG(ERROR) << "Property name \"init.svc." << name_ << "\" too long; not setting to " << new_state; | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 143 |         return; | 
 | 144 |     } | 
 | 145 |  | 
 | 146 |     property_set(prop_name.c_str(), new_state.c_str()); | 
 | 147 | } | 
 | 148 |  | 
| Elliott Hughes | ad8e94e | 2016-06-15 14:49:57 -0700 | [diff] [blame] | 149 | void Service::KillProcessGroup(int signal) { | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 150 |     LOG(VERBOSE) << "Sending signal " << signal | 
 | 151 |                  << " to service '" << name_ | 
 | 152 |                  << "' (pid " << pid_ << ") process group...\n", | 
| Elliott Hughes | ad8e94e | 2016-06-15 14:49:57 -0700 | [diff] [blame] | 153 |     kill(pid_, signal); | 
 | 154 |     killProcessGroup(uid_, pid_, signal); | 
 | 155 | } | 
 | 156 |  | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 157 | bool Service::Reap() { | 
 | 158 |     if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) { | 
| Elliott Hughes | ad8e94e | 2016-06-15 14:49:57 -0700 | [diff] [blame] | 159 |         KillProcessGroup(SIGKILL); | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 160 |     } | 
 | 161 |  | 
 | 162 |     // Remove any sockets we may have created. | 
 | 163 |     for (const auto& si : sockets_) { | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 164 |         std::string tmp = StringPrintf(ANDROID_SOCKET_DIR "/%s", si.name.c_str()); | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 165 |         unlink(tmp.c_str()); | 
 | 166 |     } | 
 | 167 |  | 
 | 168 |     if (flags_ & SVC_EXEC) { | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 169 |         LOG(INFO) << "SVC_EXEC pid " << pid_ << " finished..."; | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 170 |         return true; | 
 | 171 |     } | 
 | 172 |  | 
 | 173 |     pid_ = 0; | 
 | 174 |     flags_ &= (~SVC_RUNNING); | 
 | 175 |  | 
 | 176 |     // Oneshot processes go into the disabled state on exit, | 
 | 177 |     // except when manually restarted. | 
 | 178 |     if ((flags_ & SVC_ONESHOT) && !(flags_ & SVC_RESTART)) { | 
 | 179 |         flags_ |= SVC_DISABLED; | 
 | 180 |     } | 
 | 181 |  | 
 | 182 |     // Disabled and reset processes do not get restarted automatically. | 
 | 183 |     if (flags_ & (SVC_DISABLED | SVC_RESET))  { | 
 | 184 |         NotifyStateChange("stopped"); | 
 | 185 |         return false; | 
 | 186 |     } | 
 | 187 |  | 
 | 188 |     time_t now = gettime(); | 
 | 189 |     if ((flags_ & SVC_CRITICAL) && !(flags_ & SVC_RESTART)) { | 
 | 190 |         if (time_crashed_ + CRITICAL_CRASH_WINDOW >= now) { | 
 | 191 |             if (++nr_crashed_ > CRITICAL_CRASH_THRESHOLD) { | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 192 |                 LOG(ERROR) << "critical process '" << name_ << "' exited " | 
 | 193 |                            << CRITICAL_CRASH_THRESHOLD << " times in " | 
 | 194 |                            << (CRITICAL_CRASH_WINDOW / 60) << " minutes; " | 
 | 195 |                            << "rebooting into recovery mode"; | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 196 |                 android_reboot(ANDROID_RB_RESTART2, 0, "recovery"); | 
 | 197 |                 return false; | 
 | 198 |             } | 
 | 199 |         } else { | 
 | 200 |             time_crashed_ = now; | 
 | 201 |             nr_crashed_ = 1; | 
 | 202 |         } | 
 | 203 |     } | 
 | 204 |  | 
 | 205 |     flags_ &= (~SVC_RESTART); | 
 | 206 |     flags_ |= SVC_RESTARTING; | 
 | 207 |  | 
 | 208 |     // Execute all onrestart commands for this service. | 
 | 209 |     onrestart_.ExecuteAllCommands(); | 
 | 210 |  | 
 | 211 |     NotifyStateChange("restarting"); | 
 | 212 |     return false; | 
 | 213 | } | 
 | 214 |  | 
 | 215 | void Service::DumpState() const { | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 216 |     LOG(INFO) << "service " << name_; | 
 | 217 |     LOG(INFO) << "  class '" << classname_ << "'"; | 
 | 218 |     LOG(INFO) << "  exec "<< android::base::Join(args_, " "); | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 219 |     for (const auto& si : sockets_) { | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 220 |         LOG(INFO) << "  socket " << si.name << " " << si.type << " " << std::oct << si.perm; | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 221 |     } | 
 | 222 | } | 
 | 223 |  | 
| Jorge Lucangeli Obes | 177b27d | 2016-06-29 14:32:49 -0400 | [diff] [blame] | 224 | bool Service::ParseClass(const std::vector<std::string>& args, std::string* err) { | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 225 |     classname_ = args[1]; | 
 | 226 |     return true; | 
 | 227 | } | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 228 |  | 
| Jorge Lucangeli Obes | 177b27d | 2016-06-29 14:32:49 -0400 | [diff] [blame] | 229 | bool Service::ParseConsole(const std::vector<std::string>& args, std::string* err) { | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 230 |     flags_ |= SVC_CONSOLE; | 
| Viorel Suman | 70daa67 | 2016-03-21 10:08:07 +0200 | [diff] [blame] | 231 |     console_ = args.size() > 1 ? "/dev/" + args[1] : ""; | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 232 |     return true; | 
 | 233 | } | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 234 |  | 
| Jorge Lucangeli Obes | 177b27d | 2016-06-29 14:32:49 -0400 | [diff] [blame] | 235 | bool Service::ParseCritical(const std::vector<std::string>& args, std::string* err) { | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 236 |     flags_ |= SVC_CRITICAL; | 
 | 237 |     return true; | 
 | 238 | } | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 239 |  | 
| Jorge Lucangeli Obes | 177b27d | 2016-06-29 14:32:49 -0400 | [diff] [blame] | 240 | bool Service::ParseDisabled(const std::vector<std::string>& args, std::string* err) { | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 241 |     flags_ |= SVC_DISABLED; | 
 | 242 |     flags_ |= SVC_RC_DISABLED; | 
 | 243 |     return true; | 
 | 244 | } | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 245 |  | 
| Jorge Lucangeli Obes | 177b27d | 2016-06-29 14:32:49 -0400 | [diff] [blame] | 246 | bool Service::ParseGroup(const std::vector<std::string>& args, std::string* err) { | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 247 |     gid_ = decode_uid(args[1].c_str()); | 
 | 248 |     for (std::size_t n = 2; n < args.size(); n++) { | 
 | 249 |         supp_gids_.emplace_back(decode_uid(args[n].c_str())); | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 250 |     } | 
 | 251 |     return true; | 
 | 252 | } | 
 | 253 |  | 
| Jorge Lucangeli Obes | 177b27d | 2016-06-29 14:32:49 -0400 | [diff] [blame] | 254 | bool Service::ParsePriority(const std::vector<std::string>& args, std::string* err) { | 
| Vitalii Tomkiv | 081705c | 2016-05-18 17:36:30 -0700 | [diff] [blame] | 255 |     priority_ = std::stoi(args[1]); | 
 | 256 |  | 
 | 257 |     if (priority_ < ANDROID_PRIORITY_HIGHEST || priority_ > ANDROID_PRIORITY_LOWEST) { | 
 | 258 |         priority_ = 0; | 
 | 259 |         *err = StringPrintf("process priority value must be range %d - %d", | 
 | 260 |                 ANDROID_PRIORITY_HIGHEST, ANDROID_PRIORITY_LOWEST); | 
 | 261 |         return false; | 
 | 262 |     } | 
 | 263 |  | 
 | 264 |     return true; | 
 | 265 | } | 
 | 266 |  | 
| Jorge Lucangeli Obes | 177b27d | 2016-06-29 14:32:49 -0400 | [diff] [blame] | 267 | bool Service::ParseIoprio(const std::vector<std::string>& args, std::string* err) { | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 268 |     ioprio_pri_ = std::stoul(args[2], 0, 8); | 
 | 269 |  | 
 | 270 |     if (ioprio_pri_ < 0 || ioprio_pri_ > 7) { | 
 | 271 |         *err = "priority value must be range 0 - 7"; | 
 | 272 |         return false; | 
 | 273 |     } | 
 | 274 |  | 
 | 275 |     if (args[1] == "rt") { | 
 | 276 |         ioprio_class_ = IoSchedClass_RT; | 
 | 277 |     } else if (args[1] == "be") { | 
 | 278 |         ioprio_class_ = IoSchedClass_BE; | 
 | 279 |     } else if (args[1] == "idle") { | 
 | 280 |         ioprio_class_ = IoSchedClass_IDLE; | 
 | 281 |     } else { | 
 | 282 |         *err = "ioprio option usage: ioprio <rt|be|idle> <0-7>"; | 
 | 283 |         return false; | 
 | 284 |     } | 
 | 285 |  | 
 | 286 |     return true; | 
 | 287 | } | 
 | 288 |  | 
| Jorge Lucangeli Obes | 177b27d | 2016-06-29 14:32:49 -0400 | [diff] [blame] | 289 | bool Service::ParseKeycodes(const std::vector<std::string>& args, std::string* err) { | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 290 |     for (std::size_t i = 1; i < args.size(); i++) { | 
 | 291 |         keycodes_.emplace_back(std::stoi(args[i])); | 
 | 292 |     } | 
 | 293 |     return true; | 
 | 294 | } | 
 | 295 |  | 
| Jorge Lucangeli Obes | 177b27d | 2016-06-29 14:32:49 -0400 | [diff] [blame] | 296 | bool Service::ParseOneshot(const std::vector<std::string>& args, std::string* err) { | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 297 |     flags_ |= SVC_ONESHOT; | 
 | 298 |     return true; | 
 | 299 | } | 
 | 300 |  | 
| Jorge Lucangeli Obes | 177b27d | 2016-06-29 14:32:49 -0400 | [diff] [blame] | 301 | bool Service::ParseOnrestart(const std::vector<std::string>& args, std::string* err) { | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 302 |     std::vector<std::string> str_args(args.begin() + 1, args.end()); | 
 | 303 |     onrestart_.AddCommand(str_args, "", 0, err); | 
 | 304 |     return true; | 
 | 305 | } | 
 | 306 |  | 
| Jorge Lucangeli Obes | 177b27d | 2016-06-29 14:32:49 -0400 | [diff] [blame] | 307 | bool Service::ParseNamespace(const std::vector<std::string>& args, std::string* err) { | 
| Jorge Lucangeli Obes | 1b3fa3d | 2016-04-21 15:35:09 -0700 | [diff] [blame] | 308 |     for (size_t i = 1; i < args.size(); i++) { | 
 | 309 |         if (args[i] == "pid") { | 
 | 310 |             namespace_flags_ |= CLONE_NEWPID; | 
 | 311 |             // PID namespaces require mount namespaces. | 
 | 312 |             namespace_flags_ |= CLONE_NEWNS; | 
 | 313 |         } else if (args[i] == "mnt") { | 
 | 314 |             namespace_flags_ |= CLONE_NEWNS; | 
 | 315 |         } else { | 
 | 316 |             *err = "namespace must be 'pid' or 'mnt'"; | 
 | 317 |             return false; | 
 | 318 |         } | 
 | 319 |     } | 
 | 320 |     return true; | 
 | 321 | } | 
 | 322 |  | 
| Jorge Lucangeli Obes | 177b27d | 2016-06-29 14:32:49 -0400 | [diff] [blame] | 323 | bool Service::ParseSeclabel(const std::vector<std::string>& args, std::string* err) { | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 324 |     seclabel_ = args[1]; | 
 | 325 |     return true; | 
 | 326 | } | 
 | 327 |  | 
| Jorge Lucangeli Obes | 177b27d | 2016-06-29 14:32:49 -0400 | [diff] [blame] | 328 | bool Service::ParseSetenv(const std::vector<std::string>& args, std::string* err) { | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 329 |     envvars_.emplace_back(args[1], args[2]); | 
 | 330 |     return true; | 
 | 331 | } | 
 | 332 |  | 
 | 333 | /* name type perm [ uid gid context ] */ | 
| Jorge Lucangeli Obes | 177b27d | 2016-06-29 14:32:49 -0400 | [diff] [blame] | 334 | bool Service::ParseSocket(const std::vector<std::string>& args, std::string* err) { | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 335 |     if (args[2] != "dgram" && args[2] != "stream" && args[2] != "seqpacket") { | 
 | 336 |         *err = "socket type must be 'dgram', 'stream' or 'seqpacket'"; | 
 | 337 |         return false; | 
 | 338 |     } | 
 | 339 |  | 
 | 340 |     int perm = std::stoul(args[3], 0, 8); | 
 | 341 |     uid_t uid = args.size() > 4 ? decode_uid(args[4].c_str()) : 0; | 
 | 342 |     gid_t gid = args.size() > 5 ? decode_uid(args[5].c_str()) : 0; | 
 | 343 |     std::string socketcon = args.size() > 6 ? args[6] : ""; | 
 | 344 |  | 
 | 345 |     sockets_.emplace_back(args[1], args[2], uid, gid, perm, socketcon); | 
 | 346 |     return true; | 
 | 347 | } | 
 | 348 |  | 
| Jorge Lucangeli Obes | 177b27d | 2016-06-29 14:32:49 -0400 | [diff] [blame] | 349 | bool Service::ParseUser(const std::vector<std::string>& args, std::string* err) { | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 350 |     uid_ = decode_uid(args[1].c_str()); | 
 | 351 |     return true; | 
 | 352 | } | 
 | 353 |  | 
| Jorge Lucangeli Obes | 177b27d | 2016-06-29 14:32:49 -0400 | [diff] [blame] | 354 | bool Service::ParseWritepid(const std::vector<std::string>& args, std::string* err) { | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 355 |     writepid_files_.assign(args.begin() + 1, args.end()); | 
 | 356 |     return true; | 
 | 357 | } | 
 | 358 |  | 
| Jorge Lucangeli Obes | 177b27d | 2016-06-29 14:32:49 -0400 | [diff] [blame] | 359 | class Service::OptionParserMap : public KeywordMap<OptionParser> { | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 360 | public: | 
| Jorge Lucangeli Obes | 177b27d | 2016-06-29 14:32:49 -0400 | [diff] [blame] | 361 |     OptionParserMap() { | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 362 |     } | 
 | 363 | private: | 
 | 364 |     Map& map() const override; | 
 | 365 | }; | 
 | 366 |  | 
| Jorge Lucangeli Obes | 177b27d | 2016-06-29 14:32:49 -0400 | [diff] [blame] | 367 | Service::OptionParserMap::Map& Service::OptionParserMap::map() const { | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 368 |     constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max(); | 
| Jorge Lucangeli Obes | 177b27d | 2016-06-29 14:32:49 -0400 | [diff] [blame] | 369 |     static const Map option_parsers = { | 
 | 370 |         {"class",       {1,     1,    &Service::ParseClass}}, | 
 | 371 |         {"console",     {0,     1,    &Service::ParseConsole}}, | 
 | 372 |         {"critical",    {0,     0,    &Service::ParseCritical}}, | 
 | 373 |         {"disabled",    {0,     0,    &Service::ParseDisabled}}, | 
 | 374 |         {"group",       {1,     NR_SVC_SUPP_GIDS + 1, &Service::ParseGroup}}, | 
 | 375 |         {"ioprio",      {2,     2,    &Service::ParseIoprio}}, | 
 | 376 |         {"priority",    {1,     1,    &Service::ParsePriority}}, | 
 | 377 |         {"keycodes",    {1,     kMax, &Service::ParseKeycodes}}, | 
 | 378 |         {"oneshot",     {0,     0,    &Service::ParseOneshot}}, | 
 | 379 |         {"onrestart",   {1,     kMax, &Service::ParseOnrestart}}, | 
 | 380 |         {"namespace",   {1,     2,    &Service::ParseNamespace}}, | 
 | 381 |         {"seclabel",    {1,     1,    &Service::ParseSeclabel}}, | 
 | 382 |         {"setenv",      {2,     2,    &Service::ParseSetenv}}, | 
 | 383 |         {"socket",      {3,     6,    &Service::ParseSocket}}, | 
 | 384 |         {"user",        {1,     1,    &Service::ParseUser}}, | 
 | 385 |         {"writepid",    {1,     kMax, &Service::ParseWritepid}}, | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 386 |     }; | 
| Jorge Lucangeli Obes | 177b27d | 2016-06-29 14:32:49 -0400 | [diff] [blame] | 387 |     return option_parsers; | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 388 | } | 
 | 389 |  | 
| Jorge Lucangeli Obes | 177b27d | 2016-06-29 14:32:49 -0400 | [diff] [blame] | 390 | bool Service::ParseLine(const std::vector<std::string>& args, std::string* err) { | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 391 |     if (args.empty()) { | 
 | 392 |         *err = "option needed, but not provided"; | 
 | 393 |         return false; | 
 | 394 |     } | 
 | 395 |  | 
| Jorge Lucangeli Obes | 177b27d | 2016-06-29 14:32:49 -0400 | [diff] [blame] | 396 |     static const OptionParserMap parser_map; | 
 | 397 |     auto parser = parser_map.FindFunction(args[0], args.size() - 1, err); | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 398 |  | 
| Jorge Lucangeli Obes | 177b27d | 2016-06-29 14:32:49 -0400 | [diff] [blame] | 399 |     if (!parser) { | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 400 |         return false; | 
 | 401 |     } | 
 | 402 |  | 
| Jorge Lucangeli Obes | 177b27d | 2016-06-29 14:32:49 -0400 | [diff] [blame] | 403 |     return (this->*parser)(args, err); | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 404 | } | 
 | 405 |  | 
| Elliott Hughes | bdeac39 | 2016-04-12 15:38:27 -0700 | [diff] [blame] | 406 | bool Service::Start() { | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 407 |     // Starting a service removes it from the disabled or reset state and | 
 | 408 |     // immediately takes it out of the restarting state if it was in there. | 
 | 409 |     flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START)); | 
 | 410 |     time_started_ = 0; | 
 | 411 |  | 
 | 412 |     // Running processes require no additional work --- if they're in the | 
 | 413 |     // process of exiting, we've ensured that they will immediately restart | 
 | 414 |     // on exit, unless they are ONESHOT. | 
 | 415 |     if (flags_ & SVC_RUNNING) { | 
 | 416 |         return false; | 
 | 417 |     } | 
 | 418 |  | 
 | 419 |     bool needs_console = (flags_ & SVC_CONSOLE); | 
| Viorel Suman | 70daa67 | 2016-03-21 10:08:07 +0200 | [diff] [blame] | 420 |     if (needs_console) { | 
 | 421 |         if (console_.empty()) { | 
 | 422 |             console_ = default_console; | 
 | 423 |         } | 
 | 424 |  | 
 | 425 |         bool have_console = (open(console_.c_str(), O_RDWR | O_CLOEXEC) != -1); | 
 | 426 |         if (!have_console) { | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 427 |             PLOG(ERROR) << "service '" << name_ << "' couldn't open console '" << console_ << "'"; | 
| Viorel Suman | 70daa67 | 2016-03-21 10:08:07 +0200 | [diff] [blame] | 428 |             flags_ |= SVC_DISABLED; | 
 | 429 |             return false; | 
 | 430 |         } | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 431 |     } | 
 | 432 |  | 
 | 433 |     struct stat sb; | 
 | 434 |     if (stat(args_[0].c_str(), &sb) == -1) { | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 435 |         PLOG(ERROR) << "cannot find '" << args_[0] << "', disabling '" << name_ << "'"; | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 436 |         flags_ |= SVC_DISABLED; | 
 | 437 |         return false; | 
 | 438 |     } | 
 | 439 |  | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 440 |     std::string scon; | 
 | 441 |     if (!seclabel_.empty()) { | 
 | 442 |         scon = seclabel_; | 
 | 443 |     } else { | 
 | 444 |         char* mycon = nullptr; | 
 | 445 |         char* fcon = nullptr; | 
 | 446 |  | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 447 |         LOG(INFO) << "computing context for service '" << args_[0] << "'"; | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 448 |         int rc = getcon(&mycon); | 
 | 449 |         if (rc < 0) { | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 450 |             LOG(ERROR) << "could not get context while starting '" << name_ << "'"; | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 451 |             return false; | 
 | 452 |         } | 
 | 453 |  | 
 | 454 |         rc = getfilecon(args_[0].c_str(), &fcon); | 
 | 455 |         if (rc < 0) { | 
| Jorge Lucangeli Obes | 1b3fa3d | 2016-04-21 15:35:09 -0700 | [diff] [blame] | 456 |             LOG(ERROR) << "could not get file context while starting '" << name_ << "'"; | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 457 |             free(mycon); | 
 | 458 |             return false; | 
 | 459 |         } | 
 | 460 |  | 
 | 461 |         char* ret_scon = nullptr; | 
 | 462 |         rc = security_compute_create(mycon, fcon, string_to_security_class("process"), | 
 | 463 |                                      &ret_scon); | 
 | 464 |         if (rc == 0) { | 
 | 465 |             scon = ret_scon; | 
 | 466 |             free(ret_scon); | 
 | 467 |         } | 
 | 468 |         if (rc == 0 && scon == mycon) { | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 469 |             LOG(ERROR) << "Service " << name_ << " does not have a SELinux domain defined."; | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 470 |             free(mycon); | 
 | 471 |             free(fcon); | 
 | 472 |             return false; | 
 | 473 |         } | 
 | 474 |         free(mycon); | 
 | 475 |         free(fcon); | 
 | 476 |         if (rc < 0) { | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 477 |             LOG(ERROR) << "could not get context while starting '" << name_ << "'"; | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 478 |             return false; | 
 | 479 |         } | 
 | 480 |     } | 
 | 481 |  | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 482 |     LOG(VERBOSE) << "Starting service '" << name_ << "'..."; | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 483 |  | 
| Jorge Lucangeli Obes | 1b3fa3d | 2016-04-21 15:35:09 -0700 | [diff] [blame] | 484 |     pid_t pid = -1; | 
 | 485 |     if (namespace_flags_) { | 
 | 486 |         pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, | 
 | 487 |                     nullptr); | 
 | 488 |     } else { | 
 | 489 |         pid = fork(); | 
 | 490 |     } | 
 | 491 |  | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 492 |     if (pid == 0) { | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 493 |         umask(077); | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 494 |  | 
| Jorge Lucangeli Obes | 1b3fa3d | 2016-04-21 15:35:09 -0700 | [diff] [blame] | 495 |         if (namespace_flags_ & CLONE_NEWPID) { | 
 | 496 |             // This will fork again to run an init process inside the PID | 
 | 497 |             // namespace. | 
 | 498 |             SetUpPidNamespace(name_); | 
 | 499 |         } | 
 | 500 |  | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 501 |         for (const auto& ei : envvars_) { | 
 | 502 |             add_environment(ei.name.c_str(), ei.value.c_str()); | 
 | 503 |         } | 
 | 504 |  | 
 | 505 |         for (const auto& si : sockets_) { | 
 | 506 |             int socket_type = ((si.type == "stream" ? SOCK_STREAM : | 
 | 507 |                                 (si.type == "dgram" ? SOCK_DGRAM : | 
 | 508 |                                  SOCK_SEQPACKET))); | 
 | 509 |             const char* socketcon = | 
 | 510 |                 !si.socketcon.empty() ? si.socketcon.c_str() : scon.c_str(); | 
 | 511 |  | 
 | 512 |             int s = create_socket(si.name.c_str(), socket_type, si.perm, | 
 | 513 |                                   si.uid, si.gid, socketcon); | 
 | 514 |             if (s >= 0) { | 
 | 515 |                 PublishSocket(si.name, s); | 
 | 516 |             } | 
 | 517 |         } | 
 | 518 |  | 
| Anestis Bechtsoudis | b702b46 | 2016-02-05 16:38:48 +0200 | [diff] [blame] | 519 |         std::string pid_str = StringPrintf("%d", getpid()); | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 520 |         for (const auto& file : writepid_files_) { | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 521 |             if (!WriteStringToFile(pid_str, file)) { | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 522 |                 PLOG(ERROR) << "couldn't write " << pid_str << " to " << file; | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 523 |             } | 
 | 524 |         } | 
 | 525 |  | 
 | 526 |         if (ioprio_class_ != IoSchedClass_NONE) { | 
 | 527 |             if (android_set_ioprio(getpid(), ioprio_class_, ioprio_pri_)) { | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 528 |                 PLOG(ERROR) << "Failed to set pid " << getpid() | 
 | 529 |                             << " ioprio=" << ioprio_class_ << "," << ioprio_pri_; | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 530 |             } | 
 | 531 |         } | 
 | 532 |  | 
 | 533 |         if (needs_console) { | 
 | 534 |             setsid(); | 
 | 535 |             OpenConsole(); | 
 | 536 |         } else { | 
 | 537 |             ZapStdio(); | 
 | 538 |         } | 
 | 539 |  | 
 | 540 |         setpgid(0, getpid()); | 
 | 541 |  | 
 | 542 |         // As requested, set our gid, supplemental gids, and uid. | 
 | 543 |         if (gid_) { | 
 | 544 |             if (setgid(gid_) != 0) { | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 545 |                 PLOG(ERROR) << "setgid failed"; | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 546 |                 _exit(127); | 
 | 547 |             } | 
 | 548 |         } | 
 | 549 |         if (!supp_gids_.empty()) { | 
 | 550 |             if (setgroups(supp_gids_.size(), &supp_gids_[0]) != 0) { | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 551 |                 PLOG(ERROR) << "setgroups failed"; | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 552 |                 _exit(127); | 
 | 553 |             } | 
 | 554 |         } | 
 | 555 |         if (uid_) { | 
 | 556 |             if (setuid(uid_) != 0) { | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 557 |                 PLOG(ERROR) << "setuid failed"; | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 558 |                 _exit(127); | 
 | 559 |             } | 
 | 560 |         } | 
 | 561 |         if (!seclabel_.empty()) { | 
 | 562 |             if (setexeccon(seclabel_.c_str()) < 0) { | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 563 |                 PLOG(ERROR) << "cannot setexeccon('" << seclabel_ << "')"; | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 564 |                 _exit(127); | 
 | 565 |             } | 
 | 566 |         } | 
| Vitalii Tomkiv | 081705c | 2016-05-18 17:36:30 -0700 | [diff] [blame] | 567 |         if (priority_ != 0) { | 
 | 568 |             if (setpriority(PRIO_PROCESS, 0, priority_) != 0) { | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 569 |                 PLOG(ERROR) << "setpriority failed"; | 
| Vitalii Tomkiv | 081705c | 2016-05-18 17:36:30 -0700 | [diff] [blame] | 570 |                 _exit(127); | 
 | 571 |             } | 
 | 572 |         } | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 573 |  | 
| Tom Cherry | bac3536 | 2016-06-07 11:22:00 -0700 | [diff] [blame] | 574 |         std::vector<std::string> expanded_args; | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 575 |         std::vector<char*> strs; | 
| Tom Cherry | bac3536 | 2016-06-07 11:22:00 -0700 | [diff] [blame] | 576 |         expanded_args.resize(args_.size()); | 
 | 577 |         strs.push_back(const_cast<char*>(args_[0].c_str())); | 
 | 578 |         for (std::size_t i = 1; i < args_.size(); ++i) { | 
 | 579 |             if (!expand_props(args_[i], &expanded_args[i])) { | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 580 |                 LOG(ERROR) << args_[0] << ": cannot expand '" << args_[i] << "'"; | 
| Tom Cherry | bac3536 | 2016-06-07 11:22:00 -0700 | [diff] [blame] | 581 |                 _exit(127); | 
 | 582 |             } | 
 | 583 |             strs.push_back(const_cast<char*>(expanded_args[i].c_str())); | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 584 |         } | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 585 |         strs.push_back(nullptr); | 
| Tom Cherry | bac3536 | 2016-06-07 11:22:00 -0700 | [diff] [blame] | 586 |  | 
 | 587 |         if (execve(strs[0], (char**) &strs[0], (char**) ENV) < 0) { | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 588 |             PLOG(ERROR) << "cannot execve('" << strs[0] << "')"; | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 589 |         } | 
 | 590 |  | 
 | 591 |         _exit(127); | 
 | 592 |     } | 
 | 593 |  | 
 | 594 |     if (pid < 0) { | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 595 |         PLOG(ERROR) << "failed to fork for '" << name_ << "'"; | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 596 |         pid_ = 0; | 
 | 597 |         return false; | 
 | 598 |     } | 
 | 599 |  | 
 | 600 |     time_started_ = gettime(); | 
 | 601 |     pid_ = pid; | 
 | 602 |     flags_ |= SVC_RUNNING; | 
| Elliott Hughes | ad8e94e | 2016-06-15 14:49:57 -0700 | [diff] [blame] | 603 |  | 
 | 604 |     errno = -createProcessGroup(uid_, pid_); | 
 | 605 |     if (errno != 0) { | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 606 |         PLOG(ERROR) << "createProcessGroup(" << uid_ << ", " << pid_ << ") failed for service '" << name_ << "'"; | 
| Elliott Hughes | ad8e94e | 2016-06-15 14:49:57 -0700 | [diff] [blame] | 607 |     } | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 608 |  | 
 | 609 |     if ((flags_ & SVC_EXEC) != 0) { | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 610 |         LOG(INFO) << android::base::StringPrintf("SVC_EXEC pid %d (uid %d gid %d+%zu context %s) started; waiting...", | 
 | 611 |                                                  pid_, uid_, gid_, supp_gids_.size(), | 
 | 612 |                                                  !seclabel_.empty() ? seclabel_.c_str() : "default"); | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 613 |     } | 
 | 614 |  | 
 | 615 |     NotifyStateChange("running"); | 
 | 616 |     return true; | 
 | 617 | } | 
 | 618 |  | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 619 | bool Service::StartIfNotDisabled() { | 
 | 620 |     if (!(flags_ & SVC_DISABLED)) { | 
 | 621 |         return Start(); | 
 | 622 |     } else { | 
 | 623 |         flags_ |= SVC_DISABLED_START; | 
 | 624 |     } | 
 | 625 |     return true; | 
 | 626 | } | 
 | 627 |  | 
 | 628 | bool Service::Enable() { | 
 | 629 |     flags_ &= ~(SVC_DISABLED | SVC_RC_DISABLED); | 
 | 630 |     if (flags_ & SVC_DISABLED_START) { | 
 | 631 |         return Start(); | 
 | 632 |     } | 
 | 633 |     return true; | 
 | 634 | } | 
 | 635 |  | 
 | 636 | void Service::Reset() { | 
 | 637 |     StopOrReset(SVC_RESET); | 
 | 638 | } | 
 | 639 |  | 
 | 640 | void Service::Stop() { | 
 | 641 |     StopOrReset(SVC_DISABLED); | 
 | 642 | } | 
 | 643 |  | 
| Bertrand SIMONNET | b7e03e8 | 2015-12-18 11:39:59 -0800 | [diff] [blame] | 644 | void Service::Terminate() { | 
 | 645 |     flags_ &= ~(SVC_RESTARTING | SVC_DISABLED_START); | 
 | 646 |     flags_ |= SVC_DISABLED; | 
 | 647 |     if (pid_) { | 
| Elliott Hughes | ad8e94e | 2016-06-15 14:49:57 -0700 | [diff] [blame] | 648 |         KillProcessGroup(SIGTERM); | 
| Bertrand SIMONNET | b7e03e8 | 2015-12-18 11:39:59 -0800 | [diff] [blame] | 649 |         NotifyStateChange("stopping"); | 
 | 650 |     } | 
 | 651 | } | 
 | 652 |  | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 653 | void Service::Restart() { | 
 | 654 |     if (flags_ & SVC_RUNNING) { | 
 | 655 |         /* Stop, wait, then start the service. */ | 
 | 656 |         StopOrReset(SVC_RESTART); | 
 | 657 |     } else if (!(flags_ & SVC_RESTARTING)) { | 
 | 658 |         /* Just start the service since it's not running. */ | 
 | 659 |         Start(); | 
 | 660 |     } /* else: Service is restarting anyways. */ | 
 | 661 | } | 
 | 662 |  | 
 | 663 | void Service::RestartIfNeeded(time_t& process_needs_restart) { | 
 | 664 |     time_t next_start_time = time_started_ + 5; | 
 | 665 |  | 
 | 666 |     if (next_start_time <= gettime()) { | 
 | 667 |         flags_ &= (~SVC_RESTARTING); | 
 | 668 |         Start(); | 
 | 669 |         return; | 
 | 670 |     } | 
 | 671 |  | 
 | 672 |     if ((next_start_time < process_needs_restart) || | 
 | 673 |         (process_needs_restart == 0)) { | 
 | 674 |         process_needs_restart = next_start_time; | 
 | 675 |     } | 
 | 676 | } | 
 | 677 |  | 
| Elliott Hughes | ad8e94e | 2016-06-15 14:49:57 -0700 | [diff] [blame] | 678 | // The how field should be either SVC_DISABLED, SVC_RESET, or SVC_RESTART. | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 679 | void Service::StopOrReset(int how) { | 
| Elliott Hughes | ad8e94e | 2016-06-15 14:49:57 -0700 | [diff] [blame] | 680 |     // The service is still SVC_RUNNING until its process exits, but if it has | 
 | 681 |     // already exited it shoudn't attempt a restart yet. | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 682 |     flags_ &= ~(SVC_RESTARTING | SVC_DISABLED_START); | 
 | 683 |  | 
 | 684 |     if ((how != SVC_DISABLED) && (how != SVC_RESET) && (how != SVC_RESTART)) { | 
| Elliott Hughes | ad8e94e | 2016-06-15 14:49:57 -0700 | [diff] [blame] | 685 |         // An illegal flag: default to SVC_DISABLED. | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 686 |         how = SVC_DISABLED; | 
 | 687 |     } | 
| Elliott Hughes | ad8e94e | 2016-06-15 14:49:57 -0700 | [diff] [blame] | 688 |  | 
 | 689 |     // If the service has not yet started, prevent it from auto-starting with its class. | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 690 |     if (how == SVC_RESET) { | 
 | 691 |         flags_ |= (flags_ & SVC_RC_DISABLED) ? SVC_DISABLED : SVC_RESET; | 
 | 692 |     } else { | 
 | 693 |         flags_ |= how; | 
 | 694 |     } | 
 | 695 |  | 
 | 696 |     if (pid_) { | 
| Elliott Hughes | ad8e94e | 2016-06-15 14:49:57 -0700 | [diff] [blame] | 697 |         KillProcessGroup(SIGKILL); | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 698 |         NotifyStateChange("stopping"); | 
 | 699 |     } else { | 
 | 700 |         NotifyStateChange("stopped"); | 
 | 701 |     } | 
 | 702 | } | 
 | 703 |  | 
 | 704 | void Service::ZapStdio() const { | 
 | 705 |     int fd; | 
 | 706 |     fd = open("/dev/null", O_RDWR); | 
 | 707 |     dup2(fd, 0); | 
 | 708 |     dup2(fd, 1); | 
 | 709 |     dup2(fd, 2); | 
 | 710 |     close(fd); | 
 | 711 | } | 
 | 712 |  | 
 | 713 | void Service::OpenConsole() const { | 
| Viorel Suman | 70daa67 | 2016-03-21 10:08:07 +0200 | [diff] [blame] | 714 |     int fd = open(console_.c_str(), O_RDWR); | 
 | 715 |     if (fd == -1) fd = open("/dev/null", O_RDWR); | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 716 |     ioctl(fd, TIOCSCTTY, 0); | 
 | 717 |     dup2(fd, 0); | 
 | 718 |     dup2(fd, 1); | 
 | 719 |     dup2(fd, 2); | 
 | 720 |     close(fd); | 
 | 721 | } | 
 | 722 |  | 
 | 723 | void Service::PublishSocket(const std::string& name, int fd) const { | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 724 |     std::string key = StringPrintf(ANDROID_SOCKET_ENV_PREFIX "%s", name.c_str()); | 
 | 725 |     std::string val = StringPrintf("%d", fd); | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 726 |     add_environment(key.c_str(), val.c_str()); | 
 | 727 |  | 
 | 728 |     /* make sure we don't close-on-exec */ | 
 | 729 |     fcntl(fd, F_SETFD, 0); | 
 | 730 | } | 
 | 731 |  | 
 | 732 | int ServiceManager::exec_count_ = 0; | 
 | 733 |  | 
 | 734 | ServiceManager::ServiceManager() { | 
 | 735 | } | 
 | 736 |  | 
 | 737 | ServiceManager& ServiceManager::GetInstance() { | 
 | 738 |     static ServiceManager instance; | 
 | 739 |     return instance; | 
 | 740 | } | 
 | 741 |  | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 742 | void ServiceManager::AddService(std::unique_ptr<Service> service) { | 
 | 743 |     Service* old_service = FindServiceByName(service->name()); | 
 | 744 |     if (old_service) { | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 745 |         LOG(ERROR) << "ignored duplicate definition of service '" << service->name() << "'"; | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 746 |         return; | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 747 |     } | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 748 |     services_.emplace_back(std::move(service)); | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 749 | } | 
 | 750 |  | 
 | 751 | Service* ServiceManager::MakeExecOneshotService(const std::vector<std::string>& args) { | 
 | 752 |     // Parse the arguments: exec [SECLABEL [UID [GID]*] --] COMMAND ARGS... | 
 | 753 |     // SECLABEL can be a - to denote default | 
 | 754 |     std::size_t command_arg = 1; | 
 | 755 |     for (std::size_t i = 1; i < args.size(); ++i) { | 
 | 756 |         if (args[i] == "--") { | 
 | 757 |             command_arg = i + 1; | 
 | 758 |             break; | 
 | 759 |         } | 
 | 760 |     } | 
 | 761 |     if (command_arg > 4 + NR_SVC_SUPP_GIDS) { | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 762 |         LOG(ERROR) << "exec called with too many supplementary group ids"; | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 763 |         return nullptr; | 
 | 764 |     } | 
 | 765 |  | 
 | 766 |     if (command_arg >= args.size()) { | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 767 |         LOG(ERROR) << "exec called without command"; | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 768 |         return nullptr; | 
 | 769 |     } | 
 | 770 |     std::vector<std::string> str_args(args.begin() + command_arg, args.end()); | 
 | 771 |  | 
 | 772 |     exec_count_++; | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 773 |     std::string name = StringPrintf("exec %d (%s)", exec_count_, str_args[0].c_str()); | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 774 |     unsigned flags = SVC_EXEC | SVC_ONESHOT; | 
| Jorge Lucangeli Obes | 1b3fa3d | 2016-04-21 15:35:09 -0700 | [diff] [blame] | 775 |     unsigned namespace_flags = 0; | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 776 |  | 
 | 777 |     std::string seclabel = ""; | 
 | 778 |     if (command_arg > 2 && args[1] != "-") { | 
 | 779 |         seclabel = args[1]; | 
 | 780 |     } | 
 | 781 |     uid_t uid = 0; | 
 | 782 |     if (command_arg > 3) { | 
 | 783 |         uid = decode_uid(args[2].c_str()); | 
 | 784 |     } | 
 | 785 |     gid_t gid = 0; | 
 | 786 |     std::vector<gid_t> supp_gids; | 
 | 787 |     if (command_arg > 4) { | 
 | 788 |         gid = decode_uid(args[3].c_str()); | 
 | 789 |         std::size_t nr_supp_gids = command_arg - 1 /* -- */ - 4 /* exec SECLABEL UID GID */; | 
 | 790 |         for (size_t i = 0; i < nr_supp_gids; ++i) { | 
 | 791 |             supp_gids.push_back(decode_uid(args[4 + i].c_str())); | 
 | 792 |         } | 
 | 793 |     } | 
 | 794 |  | 
 | 795 |     std::unique_ptr<Service> svc_p(new Service(name, "default", flags, uid, gid, | 
| Jorge Lucangeli Obes | 1b3fa3d | 2016-04-21 15:35:09 -0700 | [diff] [blame] | 796 |                                                supp_gids, namespace_flags, | 
 | 797 |                                                seclabel, str_args)); | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 798 |     if (!svc_p) { | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 799 |         LOG(ERROR) << "Couldn't allocate service for exec of '" << str_args[0] << "'"; | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 800 |         return nullptr; | 
 | 801 |     } | 
 | 802 |     Service* svc = svc_p.get(); | 
 | 803 |     services_.push_back(std::move(svc_p)); | 
 | 804 |  | 
 | 805 |     return svc; | 
 | 806 | } | 
 | 807 |  | 
 | 808 | Service* ServiceManager::FindServiceByName(const std::string& name) const { | 
 | 809 |     auto svc = std::find_if(services_.begin(), services_.end(), | 
 | 810 |                             [&name] (const std::unique_ptr<Service>& s) { | 
 | 811 |                                 return name == s->name(); | 
 | 812 |                             }); | 
 | 813 |     if (svc != services_.end()) { | 
 | 814 |         return svc->get(); | 
 | 815 |     } | 
 | 816 |     return nullptr; | 
 | 817 | } | 
 | 818 |  | 
 | 819 | Service* ServiceManager::FindServiceByPid(pid_t pid) const { | 
 | 820 |     auto svc = std::find_if(services_.begin(), services_.end(), | 
 | 821 |                             [&pid] (const std::unique_ptr<Service>& s) { | 
 | 822 |                                 return s->pid() == pid; | 
 | 823 |                             }); | 
 | 824 |     if (svc != services_.end()) { | 
 | 825 |         return svc->get(); | 
 | 826 |     } | 
 | 827 |     return nullptr; | 
 | 828 | } | 
 | 829 |  | 
 | 830 | Service* ServiceManager::FindServiceByKeychord(int keychord_id) const { | 
 | 831 |     auto svc = std::find_if(services_.begin(), services_.end(), | 
 | 832 |                             [&keychord_id] (const std::unique_ptr<Service>& s) { | 
 | 833 |                                 return s->keychord_id() == keychord_id; | 
 | 834 |                             }); | 
 | 835 |  | 
 | 836 |     if (svc != services_.end()) { | 
 | 837 |         return svc->get(); | 
 | 838 |     } | 
 | 839 |     return nullptr; | 
 | 840 | } | 
 | 841 |  | 
| Bertrand SIMONNET | b7e03e8 | 2015-12-18 11:39:59 -0800 | [diff] [blame] | 842 | void ServiceManager::ForEachService(std::function<void(Service*)> callback) const { | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 843 |     for (const auto& s : services_) { | 
| Bertrand SIMONNET | b7e03e8 | 2015-12-18 11:39:59 -0800 | [diff] [blame] | 844 |         callback(s.get()); | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 845 |     } | 
 | 846 | } | 
 | 847 |  | 
 | 848 | void ServiceManager::ForEachServiceInClass(const std::string& classname, | 
 | 849 |                                            void (*func)(Service* svc)) const { | 
 | 850 |     for (const auto& s : services_) { | 
 | 851 |         if (classname == s->classname()) { | 
 | 852 |             func(s.get()); | 
 | 853 |         } | 
 | 854 |     } | 
 | 855 | } | 
 | 856 |  | 
 | 857 | void ServiceManager::ForEachServiceWithFlags(unsigned matchflags, | 
 | 858 |                                              void (*func)(Service* svc)) const { | 
 | 859 |     for (const auto& s : services_) { | 
 | 860 |         if (s->flags() & matchflags) { | 
 | 861 |             func(s.get()); | 
 | 862 |         } | 
 | 863 |     } | 
 | 864 | } | 
 | 865 |  | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 866 | void ServiceManager::RemoveService(const Service& svc) { | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 867 |     auto svc_it = std::find_if(services_.begin(), services_.end(), | 
 | 868 |                                [&svc] (const std::unique_ptr<Service>& s) { | 
 | 869 |                                    return svc.name() == s->name(); | 
 | 870 |                                }); | 
 | 871 |     if (svc_it == services_.end()) { | 
 | 872 |         return; | 
 | 873 |     } | 
 | 874 |  | 
 | 875 |     services_.erase(svc_it); | 
 | 876 | } | 
 | 877 |  | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 878 | void ServiceManager::DumpState() const { | 
 | 879 |     for (const auto& s : services_) { | 
 | 880 |         s->DumpState(); | 
 | 881 |     } | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 882 | } | 
 | 883 |  | 
| Bertrand SIMONNET | b7e03e8 | 2015-12-18 11:39:59 -0800 | [diff] [blame] | 884 | bool ServiceManager::ReapOneProcess() { | 
 | 885 |     int status; | 
 | 886 |     pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG)); | 
 | 887 |     if (pid == 0) { | 
 | 888 |         return false; | 
 | 889 |     } else if (pid == -1) { | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 890 |         PLOG(ERROR) << "waitpid failed"; | 
| Bertrand SIMONNET | b7e03e8 | 2015-12-18 11:39:59 -0800 | [diff] [blame] | 891 |         return false; | 
 | 892 |     } | 
 | 893 |  | 
 | 894 |     Service* svc = FindServiceByPid(pid); | 
 | 895 |  | 
 | 896 |     std::string name; | 
 | 897 |     if (svc) { | 
 | 898 |         name = android::base::StringPrintf("Service '%s' (pid %d)", | 
 | 899 |                                            svc->name().c_str(), pid); | 
 | 900 |     } else { | 
 | 901 |         name = android::base::StringPrintf("Untracked pid %d", pid); | 
 | 902 |     } | 
 | 903 |  | 
 | 904 |     if (WIFEXITED(status)) { | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 905 |         LOG(VERBOSE) << name << " exited with status " << WEXITSTATUS(status); | 
| Bertrand SIMONNET | b7e03e8 | 2015-12-18 11:39:59 -0800 | [diff] [blame] | 906 |     } else if (WIFSIGNALED(status)) { | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 907 |         LOG(VERBOSE) << name << " killed by signal " << WTERMSIG(status); | 
| Bertrand SIMONNET | b7e03e8 | 2015-12-18 11:39:59 -0800 | [diff] [blame] | 908 |     } else if (WIFSTOPPED(status)) { | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 909 |         LOG(VERBOSE) << name << " stopped by signal " << WSTOPSIG(status); | 
| Bertrand SIMONNET | b7e03e8 | 2015-12-18 11:39:59 -0800 | [diff] [blame] | 910 |     } else { | 
| Elliott Hughes | f86b5a6 | 2016-06-24 15:12:21 -0700 | [diff] [blame] | 911 |         LOG(VERBOSE) << name << " state changed"; | 
| Bertrand SIMONNET | b7e03e8 | 2015-12-18 11:39:59 -0800 | [diff] [blame] | 912 |     } | 
 | 913 |  | 
 | 914 |     if (!svc) { | 
 | 915 |         return true; | 
 | 916 |     } | 
 | 917 |  | 
 | 918 |     if (svc->Reap()) { | 
 | 919 |         waiting_for_exec = false; | 
 | 920 |         RemoveService(*svc); | 
 | 921 |     } | 
 | 922 |  | 
 | 923 |     return true; | 
 | 924 | } | 
 | 925 |  | 
 | 926 | void ServiceManager::ReapAnyOutstandingChildren() { | 
 | 927 |     while (ReapOneProcess()) { | 
 | 928 |     } | 
 | 929 | } | 
 | 930 |  | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 931 | bool ServiceParser::ParseSection(const std::vector<std::string>& args, | 
 | 932 |                                  std::string* err) { | 
 | 933 |     if (args.size() < 3) { | 
 | 934 |         *err = "services must have a name and a program"; | 
 | 935 |         return false; | 
 | 936 |     } | 
 | 937 |  | 
 | 938 |     const std::string& name = args[1]; | 
 | 939 |     if (!IsValidName(name)) { | 
 | 940 |         *err = StringPrintf("invalid service name '%s'", name.c_str()); | 
 | 941 |         return false; | 
 | 942 |     } | 
 | 943 |  | 
 | 944 |     std::vector<std::string> str_args(args.begin() + 2, args.end()); | 
 | 945 |     service_ = std::make_unique<Service>(name, "default", str_args); | 
 | 946 |     return true; | 
 | 947 | } | 
 | 948 |  | 
 | 949 | bool ServiceParser::ParseLineSection(const std::vector<std::string>& args, | 
 | 950 |                                      const std::string& filename, int line, | 
 | 951 |                                      std::string* err) const { | 
| Jorge Lucangeli Obes | 177b27d | 2016-06-29 14:32:49 -0400 | [diff] [blame] | 952 |     return service_ ? service_->ParseLine(args, err) : false; | 
| Tom Cherry | b734990 | 2015-08-26 11:43:36 -0700 | [diff] [blame] | 953 | } | 
 | 954 |  | 
 | 955 | void ServiceParser::EndSection() { | 
 | 956 |     if (service_) { | 
 | 957 |         ServiceManager::GetInstance().AddService(std::move(service_)); | 
 | 958 |     } | 
 | 959 | } | 
 | 960 |  | 
 | 961 | bool ServiceParser::IsValidName(const std::string& name) const { | 
| Tom Cherry | bac3299 | 2015-07-31 12:45:25 -0700 | [diff] [blame] | 962 |     if (name.size() > 16) { | 
 | 963 |         return false; | 
 | 964 |     } | 
 | 965 |     for (const auto& c : name) { | 
 | 966 |         if (!isalnum(c) && (c != '_') && (c != '-')) { | 
 | 967 |             return false; | 
 | 968 |         } | 
 | 969 |     } | 
 | 970 |     return true; | 
 | 971 | } |