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