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