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