init_parser.cpp revision 4ad60fbae51ef4a0b4a4beaa5128d823063c158c
1/* 2 * Copyright (C) 2010 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 <ctype.h> 18#include <dirent.h> 19#include <errno.h> 20#include <fcntl.h> 21#include <inttypes.h> 22#include <stdarg.h> 23#include <stddef.h> 24#include <stdio.h> 25#include <stdlib.h> 26#include <string.h> 27#include <unistd.h> 28 29#include "action.h" 30#include "init.h" 31#include "init_parser.h" 32#include "log.h" 33#include "parser.h" 34#include "property_service.h" 35#include "service.h" 36#include "util.h" 37 38#include <base/stringprintf.h> 39#include <cutils/iosched_policy.h> 40#include <cutils/list.h> 41 42static list_declare(service_list); 43 44struct import { 45 struct listnode list; 46 const char *filename; 47}; 48 49static void *parse_service(struct parse_state *state, int nargs, char **args); 50static void parse_line_service(struct parse_state *state, int nargs, char **args); 51 52static void *parse_action(struct parse_state *state, int nargs, char **args); 53static void parse_line_action(struct parse_state *state, int nargs, char **args); 54 55#define SECTION 0x01 56#define COMMAND 0x02 57#define OPTION 0x04 58 59#include "keywords.h" 60 61#define KEYWORD(symbol, flags, nargs, func) \ 62 [ K_##symbol ] = { #symbol, func, nargs + 1, flags, }, 63 64static struct { 65 const char *name; 66 int (*func)(const std::vector<std::string>& args); 67 size_t nargs; 68 unsigned char flags; 69} keyword_info[KEYWORD_COUNT] = { 70 [ K_UNKNOWN ] = { "unknown", 0, 0, 0 }, 71#include "keywords.h" 72}; 73#undef KEYWORD 74 75#define kw_is(kw, type) (keyword_info[kw].flags & (type)) 76#define kw_name(kw) (keyword_info[kw].name) 77#define kw_func(kw) (keyword_info[kw].func) 78#define kw_nargs(kw) (keyword_info[kw].nargs) 79 80void dump_parser_state() { 81 ServiceManager::GetInstance().DumpState(); 82 ActionManager::GetInstance().DumpState(); 83} 84 85static int lookup_keyword(const char *s) 86{ 87 switch (*s++) { 88 case 'b': 89 if (!strcmp(s, "ootchart_init")) return K_bootchart_init; 90 break; 91 case 'c': 92 if (!strcmp(s, "opy")) return K_copy; 93 if (!strcmp(s, "lass")) return K_class; 94 if (!strcmp(s, "lass_start")) return K_class_start; 95 if (!strcmp(s, "lass_stop")) return K_class_stop; 96 if (!strcmp(s, "lass_reset")) return K_class_reset; 97 if (!strcmp(s, "onsole")) return K_console; 98 if (!strcmp(s, "hown")) return K_chown; 99 if (!strcmp(s, "hmod")) return K_chmod; 100 if (!strcmp(s, "ritical")) return K_critical; 101 break; 102 case 'd': 103 if (!strcmp(s, "isabled")) return K_disabled; 104 if (!strcmp(s, "omainname")) return K_domainname; 105 break; 106 case 'e': 107 if (!strcmp(s, "nable")) return K_enable; 108 if (!strcmp(s, "xec")) return K_exec; 109 if (!strcmp(s, "xport")) return K_export; 110 break; 111 case 'g': 112 if (!strcmp(s, "roup")) return K_group; 113 break; 114 case 'h': 115 if (!strcmp(s, "ostname")) return K_hostname; 116 break; 117 case 'i': 118 if (!strcmp(s, "oprio")) return K_ioprio; 119 if (!strcmp(s, "fup")) return K_ifup; 120 if (!strcmp(s, "nsmod")) return K_insmod; 121 if (!strcmp(s, "mport")) return K_import; 122 if (!strcmp(s, "nstallkey")) return K_installkey; 123 break; 124 case 'k': 125 if (!strcmp(s, "eycodes")) return K_keycodes; 126 break; 127 case 'l': 128 if (!strcmp(s, "oglevel")) return K_loglevel; 129 if (!strcmp(s, "oad_persist_props")) return K_load_persist_props; 130 if (!strcmp(s, "oad_all_props")) return K_load_all_props; 131 break; 132 case 'm': 133 if (!strcmp(s, "kdir")) return K_mkdir; 134 if (!strcmp(s, "ount_all")) return K_mount_all; 135 if (!strcmp(s, "ount")) return K_mount; 136 break; 137 case 'o': 138 if (!strcmp(s, "n")) return K_on; 139 if (!strcmp(s, "neshot")) return K_oneshot; 140 if (!strcmp(s, "nrestart")) return K_onrestart; 141 break; 142 case 'p': 143 if (!strcmp(s, "owerctl")) return K_powerctl; 144 break; 145 case 'r': 146 if (!strcmp(s, "estart")) return K_restart; 147 if (!strcmp(s, "estorecon")) return K_restorecon; 148 if (!strcmp(s, "estorecon_recursive")) return K_restorecon_recursive; 149 if (!strcmp(s, "mdir")) return K_rmdir; 150 if (!strcmp(s, "m")) return K_rm; 151 break; 152 case 's': 153 if (!strcmp(s, "eclabel")) return K_seclabel; 154 if (!strcmp(s, "ervice")) return K_service; 155 if (!strcmp(s, "etenv")) return K_setenv; 156 if (!strcmp(s, "etprop")) return K_setprop; 157 if (!strcmp(s, "etrlimit")) return K_setrlimit; 158 if (!strcmp(s, "ocket")) return K_socket; 159 if (!strcmp(s, "tart")) return K_start; 160 if (!strcmp(s, "top")) return K_stop; 161 if (!strcmp(s, "wapon_all")) return K_swapon_all; 162 if (!strcmp(s, "ymlink")) return K_symlink; 163 if (!strcmp(s, "ysclktz")) return K_sysclktz; 164 break; 165 case 't': 166 if (!strcmp(s, "rigger")) return K_trigger; 167 break; 168 case 'u': 169 if (!strcmp(s, "ser")) return K_user; 170 break; 171 case 'v': 172 if (!strcmp(s, "erity_load_state")) return K_verity_load_state; 173 if (!strcmp(s, "erity_update_state")) return K_verity_update_state; 174 break; 175 case 'w': 176 if (!strcmp(s, "rite")) return K_write; 177 if (!strcmp(s, "ritepid")) return K_writepid; 178 if (!strcmp(s, "ait")) return K_wait; 179 break; 180 } 181 return K_UNKNOWN; 182} 183 184static void parse_line_no_op(struct parse_state*, int, char**) { 185} 186 187int expand_props(const std::string& src, std::string* dst) { 188 const char *src_ptr = src.c_str(); 189 190 if (!dst) { 191 return -1; 192 } 193 194 /* - variables can either be $x.y or ${x.y}, in case they are only part 195 * of the string. 196 * - will accept $$ as a literal $. 197 * - no nested property expansion, i.e. ${foo.${bar}} is not supported, 198 * bad things will happen 199 */ 200 while (*src_ptr) { 201 const char *c; 202 203 c = strchr(src_ptr, '$'); 204 if (!c) { 205 dst->append(src_ptr); 206 break; 207 } 208 209 dst->append(src_ptr, c); 210 c++; 211 212 if (*c == '$') { 213 dst->push_back(*(c++)); 214 src_ptr = c; 215 continue; 216 } else if (*c == '\0') { 217 break; 218 } 219 220 std::string prop_name; 221 if (*c == '{') { 222 c++; 223 const char* end = strchr(c, '}'); 224 if (!end) { 225 // failed to find closing brace, abort. 226 ERROR("unexpected end of string in '%s', looking for }\n", src.c_str()); 227 goto err; 228 } 229 prop_name = std::string(c, end); 230 c = end + 1; 231 } else { 232 prop_name = c; 233 ERROR("using deprecated syntax for specifying property '%s', use ${name} instead\n", 234 c); 235 c += prop_name.size(); 236 } 237 238 if (prop_name.empty()) { 239 ERROR("invalid zero-length prop name in '%s'\n", src.c_str()); 240 goto err; 241 } 242 243 std::string prop_val = property_get(prop_name.c_str()); 244 if (prop_val.empty()) { 245 ERROR("property '%s' doesn't exist while expanding '%s'\n", 246 prop_name.c_str(), src.c_str()); 247 goto err; 248 } 249 250 dst->append(prop_val); 251 src_ptr = c; 252 continue; 253 } 254 255 return 0; 256err: 257 return -1; 258} 259 260static void parse_import(struct parse_state *state, int nargs, char **args) 261{ 262 if (nargs != 2) { 263 ERROR("single argument needed for import\n"); 264 return; 265 } 266 267 std::string conf_file; 268 int ret = expand_props(args[1], &conf_file); 269 if (ret) { 270 ERROR("error while handling import on line '%d' in '%s'\n", 271 state->line, state->filename); 272 return; 273 } 274 275 struct import* import = (struct import*) calloc(1, sizeof(struct import)); 276 import->filename = strdup(conf_file.c_str()); 277 278 struct listnode *import_list = (listnode*) state->priv; 279 list_add_tail(import_list, &import->list); 280 INFO("Added '%s' to import list\n", import->filename); 281} 282 283static void parse_new_section(struct parse_state *state, int kw, 284 int nargs, char **args) 285{ 286 printf("[ %s %s ]\n", args[0], 287 nargs > 1 ? args[1] : ""); 288 switch(kw) { 289 case K_service: 290 state->context = parse_service(state, nargs, args); 291 if (state->context) { 292 state->parse_line = parse_line_service; 293 return; 294 } 295 break; 296 case K_on: 297 state->context = parse_action(state, nargs, args); 298 if (state->context) { 299 state->parse_line = parse_line_action; 300 return; 301 } 302 break; 303 case K_import: 304 parse_import(state, nargs, args); 305 break; 306 } 307 state->parse_line = parse_line_no_op; 308} 309 310static void parse_config(const char *fn, const std::string& data) 311{ 312 struct listnode import_list; 313 struct listnode *node; 314 char *args[INIT_PARSER_MAXARGS]; 315 316 int nargs = 0; 317 318 //TODO: Use a parser with const input and remove this copy 319 std::vector<char> data_copy(data.begin(), data.end()); 320 data_copy.push_back('\0'); 321 322 parse_state state; 323 state.filename = fn; 324 state.line = 0; 325 state.ptr = &data_copy[0]; 326 state.nexttoken = 0; 327 state.parse_line = parse_line_no_op; 328 329 list_init(&import_list); 330 state.priv = &import_list; 331 332 for (;;) { 333 switch (next_token(&state)) { 334 case T_EOF: 335 state.parse_line(&state, 0, 0); 336 goto parser_done; 337 case T_NEWLINE: 338 state.line++; 339 if (nargs) { 340 int kw = lookup_keyword(args[0]); 341 if (kw_is(kw, SECTION)) { 342 state.parse_line(&state, 0, 0); 343 parse_new_section(&state, kw, nargs, args); 344 } else { 345 state.parse_line(&state, nargs, args); 346 } 347 nargs = 0; 348 } 349 break; 350 case T_TEXT: 351 if (nargs < INIT_PARSER_MAXARGS) { 352 args[nargs++] = state.text; 353 } 354 break; 355 } 356 } 357 358parser_done: 359 list_for_each(node, &import_list) { 360 struct import* import = node_to_item(node, struct import, list); 361 if (!init_parse_config(import->filename)) { 362 ERROR("could not import file '%s' from '%s': %s\n", 363 import->filename, fn, strerror(errno)); 364 } 365 } 366} 367 368static bool init_parse_config_file(const char* path) { 369 INFO("Parsing file %s...\n", path); 370 Timer t; 371 std::string data; 372 if (!read_file(path, &data)) { 373 return false; 374 } 375 376 data.push_back('\n'); // TODO: fix parse_config. 377 parse_config(path, data); 378 dump_parser_state(); 379 380 NOTICE("(Parsing %s took %.2fs.)\n", path, t.duration()); 381 return true; 382} 383 384static bool init_parse_config_dir(const char* path) { 385 INFO("Parsing directory %s...\n", path); 386 std::unique_ptr<DIR, int(*)(DIR*)> config_dir(opendir(path), closedir); 387 if (!config_dir) { 388 ERROR("Could not import directory '%s'\n", path); 389 return false; 390 } 391 dirent* current_file; 392 while ((current_file = readdir(config_dir.get()))) { 393 std::string current_path = 394 android::base::StringPrintf("%s/%s", path, current_file->d_name); 395 // Ignore directories and only process regular files. 396 if (current_file->d_type == DT_REG) { 397 if (!init_parse_config_file(current_path.c_str())) { 398 ERROR("could not import file '%s'\n", current_path.c_str()); 399 } 400 } 401 } 402 return true; 403} 404 405bool init_parse_config(const char* path) { 406 if (is_dir(path)) { 407 return init_parse_config_dir(path); 408 } 409 return init_parse_config_file(path); 410} 411 412static void *parse_service(struct parse_state *state, int nargs, char **args) 413{ 414 if (nargs < 3) { 415 parse_error(state, "services must have a name and a program\n"); 416 return nullptr; 417 } 418 std::vector<std::string> str_args(args + 2, args + nargs); 419 std::string ret_err; 420 Service* svc = ServiceManager::GetInstance().AddNewService(args[1], "default", 421 str_args, &ret_err); 422 423 if (!svc) { 424 parse_error(state, "%s\n", ret_err.c_str()); 425 } 426 427 return svc; 428} 429 430static void parse_line_service(struct parse_state *state, int nargs, char **args) 431{ 432 if (nargs == 0) { 433 return; 434 } 435 436 Service* svc = static_cast<Service*>(state->context); 437 int kw = lookup_keyword(args[0]); 438 std::vector<std::string> str_args(args, args + nargs); 439 std::string ret_err; 440 bool ret = svc->HandleLine(kw, str_args, &ret_err); 441 442 if (!ret) { 443 parse_error(state, "%s\n", ret_err.c_str()); 444 } 445} 446 447static void *parse_action(struct parse_state* state, int nargs, char **args) 448{ 449 std::string ret_err; 450 std::vector<std::string> triggers(args + 1, args + nargs); 451 Action* ret = ActionManager::GetInstance().AddNewAction(triggers, &ret_err); 452 453 if (!ret) { 454 parse_error(state, "%s\n", ret_err.c_str()); 455 } 456 457 return ret; 458} 459 460bool add_command_to_action(Action* action, const std::vector<std::string>& args, 461 const std::string& filename, int line, std::string* err) 462{ 463 int kw; 464 size_t n; 465 466 kw = lookup_keyword(args[0].c_str()); 467 if (!kw_is(kw, COMMAND)) { 468 *err = android::base::StringPrintf("invalid command '%s'\n", args[0].c_str()); 469 return false; 470 } 471 472 n = kw_nargs(kw); 473 if (args.size() < n) { 474 *err = android::base::StringPrintf("%s requires %zu %s\n", 475 args[0].c_str(), n - 1, 476 n > 2 ? "arguments" : "argument"); 477 return false; 478 } 479 480 action->AddCommand(kw_func(kw), args, filename, line); 481 return true; 482} 483 484static void parse_line_action(struct parse_state* state, int nargs, char **args) 485{ 486 if (nargs == 0) { 487 return; 488 } 489 490 Action* action = static_cast<Action*>(state->context); 491 std::vector<std::string> str_args(args, args + nargs); 492 std::string ret_err; 493 bool ret = add_command_to_action(action, str_args, state->filename, 494 state->line, &ret_err); 495 if (!ret) { 496 parse_error(state, "%s\n", ret_err.c_str()); 497 } 498} 499