blob: 54622cce2e253d77c1dde49e42b6fba310e146ba [file] [log] [blame]
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -08001#include <stdio.h>
2#include <stdlib.h>
3#include <unistd.h>
4#include <fcntl.h>
5#include <stdarg.h>
6#include <string.h>
7#include <stddef.h>
8#include <ctype.h>
9
10#include "init.h"
11#include "property_service.h"
12
13#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
14#include <sys/_system_properties.h>
15
16static list_declare(service_list);
17static list_declare(action_list);
18static list_declare(action_queue);
19
20#define RAW(x...) log_write(6, x)
21
22void DUMP(void)
23{
24#if 0
25 struct service *svc;
26 struct action *act;
27 struct command *cmd;
28 struct listnode *node;
29 struct listnode *node2;
30 struct socketinfo *si;
31 int n;
32
33 list_for_each(node, &service_list) {
34 svc = node_to_item(node, struct service, slist);
35 RAW("service %s\n", svc->name);
36 RAW(" class '%s'\n", svc->classname);
37 RAW(" exec");
38 for (n = 0; n < svc->nargs; n++) {
39 RAW(" '%s'", svc->args[n]);
40 }
41 RAW("\n");
42 for (si = svc->sockets; si; si = si->next) {
43 RAW(" socket %s %s 0%o\n", si->name, si->type, si->perm);
44 }
45 }
46
47 list_for_each(node, &action_list) {
48 act = node_to_item(node, struct action, alist);
49 RAW("on %s\n", act->name);
50 list_for_each(node2, &act->commands) {
51 cmd = node_to_item(node2, struct command, clist);
52 RAW(" %p", cmd->func);
53 for (n = 0; n < cmd->nargs; n++) {
54 RAW(" %s", cmd->args[n]);
55 }
56 RAW("\n");
57 }
58 RAW("\n");
59 }
60#endif
61}
62
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080063#define T_EOF 0
64#define T_TEXT 1
65#define T_NEWLINE 2
66
67struct parse_state
68{
69 char *ptr;
70 char *text;
71 int line;
72 int nexttoken;
73 void *context;
74 void (*parse_line)(struct parse_state *state, int nargs, char **args);
75 const char *filename;
76};
77
78static void *parse_service(struct parse_state *state, int nargs, char **args);
79static void parse_line_service(struct parse_state *state, int nargs, char **args);
80
81static void *parse_action(struct parse_state *state, int nargs, char **args);
82static void parse_line_action(struct parse_state *state, int nargs, char **args);
83
84void parse_error(struct parse_state *state, const char *fmt, ...)
85{
86 va_list ap;
87 char buf[128];
88 int off;
89
90 snprintf(buf, 128, "%s: %d: ", state->filename, state->line);
91 buf[127] = 0;
92 off = strlen(buf);
93
94 va_start(ap, fmt);
95 vsnprintf(buf + off, 128 - off, fmt, ap);
96 va_end(ap);
97 buf[127] = 0;
98 ERROR("%s", buf);
99}
100
101#define SECTION 0x01
102#define COMMAND 0x02
103#define OPTION 0x04
104
105#include "keywords.h"
106
107#define KEYWORD(symbol, flags, nargs, func) \
108 [ K_##symbol ] = { #symbol, func, nargs + 1, flags, },
109
110struct {
111 const char *name;
112 int (*func)(int nargs, char **args);
113 unsigned char nargs;
114 unsigned char flags;
115} keyword_info[KEYWORD_COUNT] = {
116 [ K_UNKNOWN ] = { "unknown", 0, 0, 0 },
117#include "keywords.h"
118};
119#undef KEYWORD
120
121#define kw_is(kw, type) (keyword_info[kw].flags & (type))
122#define kw_name(kw) (keyword_info[kw].name)
123#define kw_func(kw) (keyword_info[kw].func)
124#define kw_nargs(kw) (keyword_info[kw].nargs)
125
126int lookup_keyword(const char *s)
127{
128 switch (*s++) {
129 case 'c':
San Mehat7c44fe52009-08-26 16:39:25 -0700130 if (!strcmp(s, "opy")) return K_copy;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800131 if (!strcmp(s, "apability")) return K_capability;
Jay Freeman (saurik)e7cb1372008-11-17 06:41:10 +0000132 if (!strcmp(s, "hdir")) return K_chdir;
133 if (!strcmp(s, "hroot")) return K_chroot;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800134 if (!strcmp(s, "lass")) return K_class;
135 if (!strcmp(s, "lass_start")) return K_class_start;
136 if (!strcmp(s, "lass_stop")) return K_class_stop;
137 if (!strcmp(s, "onsole")) return K_console;
138 if (!strcmp(s, "hown")) return K_chown;
139 if (!strcmp(s, "hmod")) return K_chmod;
140 if (!strcmp(s, "ritical")) return K_critical;
141 break;
142 case 'd':
143 if (!strcmp(s, "isabled")) return K_disabled;
144 if (!strcmp(s, "omainname")) return K_domainname;
145 if (!strcmp(s, "evice")) return K_device;
146 break;
147 case 'e':
148 if (!strcmp(s, "xec")) return K_exec;
149 if (!strcmp(s, "xport")) return K_export;
150 break;
151 case 'g':
152 if (!strcmp(s, "roup")) return K_group;
153 break;
154 case 'h':
155 if (!strcmp(s, "ostname")) return K_hostname;
156 break;
157 case 'i':
158 if (!strcmp(s, "fup")) return K_ifup;
159 if (!strcmp(s, "nsmod")) return K_insmod;
160 if (!strcmp(s, "mport")) return K_import;
161 break;
162 case 'k':
163 if (!strcmp(s, "eycodes")) return K_keycodes;
164 break;
165 case 'l':
166 if (!strcmp(s, "oglevel")) return K_loglevel;
167 break;
168 case 'm':
169 if (!strcmp(s, "kdir")) return K_mkdir;
170 if (!strcmp(s, "ount")) return K_mount;
171 break;
172 case 'o':
173 if (!strcmp(s, "n")) return K_on;
174 if (!strcmp(s, "neshot")) return K_oneshot;
175 if (!strcmp(s, "nrestart")) return K_onrestart;
176 break;
177 case 'r':
178 if (!strcmp(s, "estart")) return K_restart;
179 break;
180 case 's':
181 if (!strcmp(s, "ervice")) return K_service;
182 if (!strcmp(s, "etenv")) return K_setenv;
183 if (!strcmp(s, "etkey")) return K_setkey;
184 if (!strcmp(s, "etprop")) return K_setprop;
185 if (!strcmp(s, "etrlimit")) return K_setrlimit;
186 if (!strcmp(s, "ocket")) return K_socket;
187 if (!strcmp(s, "tart")) return K_start;
188 if (!strcmp(s, "top")) return K_stop;
189 if (!strcmp(s, "ymlink")) return K_symlink;
190 if (!strcmp(s, "ysclktz")) return K_sysclktz;
191 break;
192 case 't':
193 if (!strcmp(s, "rigger")) return K_trigger;
194 break;
195 case 'u':
196 if (!strcmp(s, "ser")) return K_user;
197 break;
198 case 'w':
199 if (!strcmp(s, "rite")) return K_write;
200 break;
201 }
202 return K_UNKNOWN;
203}
204
205void parse_line_no_op(struct parse_state *state, int nargs, char **args)
206{
207}
208
209int next_token(struct parse_state *state)
210{
211 char *x = state->ptr;
212 char *s;
213
214 if (state->nexttoken) {
215 int t = state->nexttoken;
216 state->nexttoken = 0;
217 return t;
218 }
219
220 for (;;) {
221 switch (*x) {
222 case 0:
223 state->ptr = x;
224 return T_EOF;
225 case '\n':
226 state->line++;
227 x++;
228 state->ptr = x;
229 return T_NEWLINE;
230 case ' ':
231 case '\t':
232 case '\r':
233 x++;
234 continue;
235 case '#':
236 while (*x && (*x != '\n')) x++;
237 state->line++;
238 state->ptr = x;
239 return T_NEWLINE;
240 default:
241 goto text;
242 }
243 }
244
245textdone:
246 state->ptr = x;
247 *s = 0;
248 return T_TEXT;
249text:
250 state->text = s = x;
251textresume:
252 for (;;) {
253 switch (*x) {
254 case 0:
255 goto textdone;
256 case ' ':
257 case '\t':
258 case '\r':
259 x++;
260 goto textdone;
261 case '\n':
262 state->nexttoken = T_NEWLINE;
263 x++;
264 goto textdone;
265 case '"':
266 x++;
267 for (;;) {
268 switch (*x) {
269 case 0:
270 /* unterminated quoted thing */
271 state->ptr = x;
272 return T_EOF;
273 case '"':
274 x++;
275 goto textresume;
276 default:
277 *s++ = *x++;
278 }
279 }
280 break;
281 case '\\':
282 x++;
283 switch (*x) {
284 case 0:
285 goto textdone;
286 case 'n':
287 *s++ = '\n';
288 break;
289 case 'r':
290 *s++ = '\r';
291 break;
292 case 't':
293 *s++ = '\t';
294 break;
295 case '\\':
296 *s++ = '\\';
297 break;
298 case '\r':
299 /* \ <cr> <lf> -> line continuation */
300 if (x[1] != '\n') {
301 x++;
302 continue;
303 }
304 case '\n':
305 /* \ <lf> -> line continuation */
306 state->line++;
307 x++;
308 /* eat any extra whitespace */
309 while((*x == ' ') || (*x == '\t')) x++;
310 continue;
311 default:
312 /* unknown escape -- just copy */
313 *s++ = *x++;
314 }
315 continue;
316 default:
317 *s++ = *x++;
318 }
319 }
320 return T_EOF;
321}
322
323void parse_line(int nargs, char **args)
324{
325 int n;
326 int id = lookup_keyword(args[0]);
327 printf("%s(%d)", args[0], id);
328 for (n = 1; n < nargs; n++) {
329 printf(" '%s'", args[n]);
330 }
331 printf("\n");
332}
333
334void parse_new_section(struct parse_state *state, int kw,
335 int nargs, char **args)
336{
337 printf("[ %s %s ]\n", args[0],
338 nargs > 1 ? args[1] : "");
339 switch(kw) {
340 case K_service:
341 state->context = parse_service(state, nargs, args);
342 if (state->context) {
343 state->parse_line = parse_line_service;
344 return;
345 }
346 break;
347 case K_on:
348 state->context = parse_action(state, nargs, args);
349 if (state->context) {
350 state->parse_line = parse_line_action;
351 return;
352 }
353 break;
354 }
355 state->parse_line = parse_line_no_op;
356}
357
358static void parse_config(const char *fn, char *s)
359{
360 struct parse_state state;
San Mehatf24e2522009-05-19 13:30:46 -0700361 char *args[SVC_MAXARGS];
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800362 int nargs;
363
364 nargs = 0;
365 state.filename = fn;
366 state.line = 1;
367 state.ptr = s;
368 state.nexttoken = 0;
369 state.parse_line = parse_line_no_op;
370 for (;;) {
371 switch (next_token(&state)) {
372 case T_EOF:
373 state.parse_line(&state, 0, 0);
374 return;
375 case T_NEWLINE:
376 if (nargs) {
377 int kw = lookup_keyword(args[0]);
378 if (kw_is(kw, SECTION)) {
379 state.parse_line(&state, 0, 0);
380 parse_new_section(&state, kw, nargs, args);
381 } else {
382 state.parse_line(&state, nargs, args);
383 }
384 nargs = 0;
385 }
386 break;
387 case T_TEXT:
San Mehatf24e2522009-05-19 13:30:46 -0700388 if (nargs < SVC_MAXARGS) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800389 args[nargs++] = state.text;
390 }
391 break;
392 }
393 }
394}
395
396int parse_config_file(const char *fn)
397{
398 char *data;
399 data = read_file(fn, 0);
400 if (!data) return -1;
401
402 parse_config(fn, data);
403 DUMP();
404 return 0;
405}
406
407static int valid_name(const char *name)
408{
409 if (strlen(name) > 16) {
410 return 0;
411 }
412 while (*name) {
413 if (!isalnum(*name) && (*name != '_') && (*name != '-')) {
414 return 0;
415 }
416 name++;
417 }
418 return 1;
419}
420
421struct service *service_find_by_name(const char *name)
422{
423 struct listnode *node;
424 struct service *svc;
425 list_for_each(node, &service_list) {
426 svc = node_to_item(node, struct service, slist);
427 if (!strcmp(svc->name, name)) {
428 return svc;
429 }
430 }
431 return 0;
432}
433
434struct service *service_find_by_pid(pid_t pid)
435{
436 struct listnode *node;
437 struct service *svc;
438 list_for_each(node, &service_list) {
439 svc = node_to_item(node, struct service, slist);
440 if (svc->pid == pid) {
441 return svc;
442 }
443 }
444 return 0;
445}
446
447struct service *service_find_by_keychord(int keychord_id)
448{
449 struct listnode *node;
450 struct service *svc;
451 list_for_each(node, &service_list) {
452 svc = node_to_item(node, struct service, slist);
453 if (svc->keychord_id == keychord_id) {
454 return svc;
455 }
456 }
457 return 0;
458}
459
460void service_for_each(void (*func)(struct service *svc))
461{
462 struct listnode *node;
463 struct service *svc;
464 list_for_each(node, &service_list) {
465 svc = node_to_item(node, struct service, slist);
466 func(svc);
467 }
468}
469
470void service_for_each_class(const char *classname,
471 void (*func)(struct service *svc))
472{
473 struct listnode *node;
474 struct service *svc;
475 list_for_each(node, &service_list) {
476 svc = node_to_item(node, struct service, slist);
477 if (!strcmp(svc->classname, classname)) {
478 func(svc);
479 }
480 }
481}
482
483void service_for_each_flags(unsigned matchflags,
484 void (*func)(struct service *svc))
485{
486 struct listnode *node;
487 struct service *svc;
488 list_for_each(node, &service_list) {
489 svc = node_to_item(node, struct service, slist);
490 if (svc->flags & matchflags) {
491 func(svc);
492 }
493 }
494}
495
496void action_for_each_trigger(const char *trigger,
497 void (*func)(struct action *act))
498{
499 struct listnode *node;
500 struct action *act;
501 list_for_each(node, &action_list) {
502 act = node_to_item(node, struct action, alist);
503 if (!strcmp(act->name, trigger)) {
504 func(act);
505 }
506 }
507}
508
509void queue_property_triggers(const char *name, const char *value)
510{
511 struct listnode *node;
512 struct action *act;
513 list_for_each(node, &action_list) {
514 act = node_to_item(node, struct action, alist);
515 if (!strncmp(act->name, "property:", strlen("property:"))) {
516 const char *test = act->name + strlen("property:");
517 int name_length = strlen(name);
518
519 if (!strncmp(name, test, name_length) &&
520 test[name_length] == '=' &&
521 !strcmp(test + name_length + 1, value)) {
522 action_add_queue_tail(act);
523 }
524 }
525 }
526}
527
528void queue_all_property_triggers()
529{
530 struct listnode *node;
531 struct action *act;
532 list_for_each(node, &action_list) {
533 act = node_to_item(node, struct action, alist);
534 if (!strncmp(act->name, "property:", strlen("property:"))) {
535 /* parse property name and value
536 syntax is property:<name>=<value> */
537 const char* name = act->name + strlen("property:");
538 const char* equals = strchr(name, '=');
539 if (equals) {
Mike Lockwoodb3779552009-05-08 14:27:42 -0400540 char prop_name[PROP_NAME_MAX + 1];
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800541 const char* value;
542 int length = equals - name;
543 if (length > PROP_NAME_MAX) {
544 ERROR("property name too long in trigger %s", act->name);
545 } else {
546 memcpy(prop_name, name, length);
547 prop_name[length] = 0;
548
549 /* does the property exist, and match the trigger value? */
Mike Lockwoodb3779552009-05-08 14:27:42 -0400550 value = property_get(prop_name);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800551 if (value && !strcmp(equals + 1, value)) {
552 action_add_queue_tail(act);
553 }
554 }
555 }
556 }
557 }
558}
559
560void action_add_queue_tail(struct action *act)
561{
562 list_add_tail(&action_queue, &act->qlist);
563}
564
565struct action *action_remove_queue_head(void)
566{
567 if (list_empty(&action_queue)) {
568 return 0;
569 } else {
570 struct listnode *node = list_head(&action_queue);
571 struct action *act = node_to_item(node, struct action, qlist);
572 list_remove(node);
573 return act;
574 }
575}
576
577static void *parse_service(struct parse_state *state, int nargs, char **args)
578{
579 struct service *svc;
580 if (nargs < 3) {
581 parse_error(state, "services must have a name and a program\n");
582 return 0;
583 }
584 if (!valid_name(args[1])) {
585 parse_error(state, "invalid service name '%s'\n", args[1]);
586 return 0;
587 }
588
589 svc = service_find_by_name(args[1]);
590 if (svc) {
591 parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]);
592 return 0;
593 }
594
595 nargs -= 2;
596 svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs);
597 if (!svc) {
598 parse_error(state, "out of memory\n");
599 return 0;
600 }
601 svc->name = args[1];
602 svc->classname = "default";
603 memcpy(svc->args, args + 2, sizeof(char*) * nargs);
604 svc->args[nargs] = 0;
605 svc->nargs = nargs;
606 svc->onrestart.name = "onrestart";
607 list_init(&svc->onrestart.commands);
608 list_add_tail(&service_list, &svc->slist);
609 return svc;
610}
611
612static void parse_line_service(struct parse_state *state, int nargs, char **args)
613{
614 struct service *svc = state->context;
615 struct command *cmd;
616 int i, kw, kw_nargs;
617
618 if (nargs == 0) {
619 return;
620 }
621
622 kw = lookup_keyword(args[0]);
623 switch (kw) {
624 case K_capability:
625 break;
626 case K_class:
627 if (nargs != 2) {
628 parse_error(state, "class option requires a classname\n");
629 } else {
630 svc->classname = args[1];
631 }
632 break;
633 case K_console:
634 svc->flags |= SVC_CONSOLE;
635 break;
636 case K_disabled:
637 svc->flags |= SVC_DISABLED;
638 break;
639 case K_group:
640 if (nargs < 2) {
641 parse_error(state, "group option requires a group id\n");
642 } else if (nargs > NR_SVC_SUPP_GIDS + 2) {
643 parse_error(state, "group option accepts at most %d supp. groups\n",
644 NR_SVC_SUPP_GIDS);
645 } else {
646 int n;
647 svc->gid = decode_uid(args[1]);
648 for (n = 2; n < nargs; n++) {
649 svc->supp_gids[n-2] = decode_uid(args[n]);
650 }
651 svc->nr_supp_gids = n - 2;
652 }
653 break;
654 case K_keycodes:
655 if (nargs < 2) {
656 parse_error(state, "keycodes option requires atleast one keycode\n");
657 } else {
658 svc->keycodes = malloc((nargs - 1) * sizeof(svc->keycodes[0]));
659 if (!svc->keycodes) {
660 parse_error(state, "could not allocate keycodes\n");
661 } else {
662 svc->nkeycodes = nargs - 1;
663 for (i = 1; i < nargs; i++) {
664 svc->keycodes[i - 1] = atoi(args[i]);
665 }
666 }
667 }
668 break;
669 case K_oneshot:
670 svc->flags |= SVC_ONESHOT;
671 break;
672 case K_onrestart:
673 nargs--;
674 args++;
675 kw = lookup_keyword(args[0]);
676 if (!kw_is(kw, COMMAND)) {
677 parse_error(state, "invalid command '%s'\n", args[0]);
678 break;
679 }
680 kw_nargs = kw_nargs(kw);
681 if (nargs < kw_nargs) {
682 parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1,
683 kw_nargs > 2 ? "arguments" : "argument");
684 break;
685 }
686
687 cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);
688 cmd->func = kw_func(kw);
689 cmd->nargs = nargs;
690 memcpy(cmd->args, args, sizeof(char*) * nargs);
691 list_add_tail(&svc->onrestart.commands, &cmd->clist);
692 break;
693 case K_critical:
694 svc->flags |= SVC_CRITICAL;
695 break;
696 case K_setenv: { /* name value */
697 struct svcenvinfo *ei;
698 if (nargs < 2) {
699 parse_error(state, "setenv option requires name and value arguments\n");
700 break;
701 }
702 ei = calloc(1, sizeof(*ei));
703 if (!ei) {
704 parse_error(state, "out of memory\n");
705 break;
706 }
707 ei->name = args[1];
708 ei->value = args[2];
709 ei->next = svc->envvars;
710 svc->envvars = ei;
711 break;
712 }
713 case K_socket: {/* name type perm [ uid gid ] */
714 struct socketinfo *si;
715 if (nargs < 4) {
716 parse_error(state, "socket option requires name, type, perm arguments\n");
717 break;
718 }
719 if (strcmp(args[2],"dgram") && strcmp(args[2],"stream")) {
720 parse_error(state, "socket type must be 'dgram' or 'stream'\n");
721 break;
722 }
723 si = calloc(1, sizeof(*si));
724 if (!si) {
725 parse_error(state, "out of memory\n");
726 break;
727 }
728 si->name = args[1];
729 si->type = args[2];
730 si->perm = strtoul(args[3], 0, 8);
731 if (nargs > 4)
732 si->uid = decode_uid(args[4]);
733 if (nargs > 5)
734 si->gid = decode_uid(args[5]);
735 si->next = svc->sockets;
736 svc->sockets = si;
737 break;
738 }
739 case K_user:
740 if (nargs != 2) {
741 parse_error(state, "user option requires a user id\n");
742 } else {
743 svc->uid = decode_uid(args[1]);
744 }
745 break;
746 default:
747 parse_error(state, "invalid option '%s'\n", args[0]);
748 }
749}
750
751static void *parse_action(struct parse_state *state, int nargs, char **args)
752{
753 struct action *act;
754 if (nargs < 2) {
755 parse_error(state, "actions must have a trigger\n");
756 return 0;
757 }
758 if (nargs > 2) {
759 parse_error(state, "actions may not have extra parameters\n");
760 return 0;
761 }
762 act = calloc(1, sizeof(*act));
763 act->name = args[1];
764 list_init(&act->commands);
765 list_add_tail(&action_list, &act->alist);
766 /* XXX add to hash */
767 return act;
768}
769
770static void parse_line_action(struct parse_state* state, int nargs, char **args)
771{
772 struct command *cmd;
773 struct action *act = state->context;
774 int (*func)(int nargs, char **args);
775 int kw, n;
776
777 if (nargs == 0) {
778 return;
779 }
780
781 kw = lookup_keyword(args[0]);
782 if (!kw_is(kw, COMMAND)) {
783 parse_error(state, "invalid command '%s'\n", args[0]);
784 return;
785 }
786
787 n = kw_nargs(kw);
788 if (nargs < n) {
789 parse_error(state, "%s requires %d %s\n", args[0], n - 1,
790 n > 2 ? "arguments" : "argument");
791 return;
792 }
793 cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);
794 cmd->func = kw_func(kw);
795 cmd->nargs = nargs;
796 memcpy(cmd->args, args, sizeof(char*) * nargs);
797 list_add_tail(&act->commands, &cmd->clist);
798}