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