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