blob: b085da87c03212def2ebd22031082f732bd54541 [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;
207 break;
208 }
209 return K_UNKNOWN;
210}
211
212void parse_line_no_op(struct parse_state *state, int nargs, char **args)
213{
214}
215
216int next_token(struct parse_state *state)
217{
218 char *x = state->ptr;
219 char *s;
220
221 if (state->nexttoken) {
222 int t = state->nexttoken;
223 state->nexttoken = 0;
224 return t;
225 }
226
227 for (;;) {
228 switch (*x) {
229 case 0:
230 state->ptr = x;
231 return T_EOF;
232 case '\n':
233 state->line++;
234 x++;
235 state->ptr = x;
236 return T_NEWLINE;
237 case ' ':
238 case '\t':
239 case '\r':
240 x++;
241 continue;
242 case '#':
243 while (*x && (*x != '\n')) x++;
244 state->line++;
245 state->ptr = x;
246 return T_NEWLINE;
247 default:
248 goto text;
249 }
250 }
251
252textdone:
253 state->ptr = x;
254 *s = 0;
255 return T_TEXT;
256text:
257 state->text = s = x;
258textresume:
259 for (;;) {
260 switch (*x) {
261 case 0:
262 goto textdone;
263 case ' ':
264 case '\t':
265 case '\r':
266 x++;
267 goto textdone;
268 case '\n':
269 state->nexttoken = T_NEWLINE;
270 x++;
271 goto textdone;
272 case '"':
273 x++;
274 for (;;) {
275 switch (*x) {
276 case 0:
277 /* unterminated quoted thing */
278 state->ptr = x;
279 return T_EOF;
280 case '"':
281 x++;
282 goto textresume;
283 default:
284 *s++ = *x++;
285 }
286 }
287 break;
288 case '\\':
289 x++;
290 switch (*x) {
291 case 0:
292 goto textdone;
293 case 'n':
294 *s++ = '\n';
295 break;
296 case 'r':
297 *s++ = '\r';
298 break;
299 case 't':
300 *s++ = '\t';
301 break;
302 case '\\':
303 *s++ = '\\';
304 break;
305 case '\r':
306 /* \ <cr> <lf> -> line continuation */
307 if (x[1] != '\n') {
308 x++;
309 continue;
310 }
311 case '\n':
312 /* \ <lf> -> line continuation */
313 state->line++;
314 x++;
315 /* eat any extra whitespace */
316 while((*x == ' ') || (*x == '\t')) x++;
317 continue;
318 default:
319 /* unknown escape -- just copy */
320 *s++ = *x++;
321 }
322 continue;
323 default:
324 *s++ = *x++;
325 }
326 }
327 return T_EOF;
328}
329
330void parse_line(int nargs, char **args)
331{
332 int n;
333 int id = lookup_keyword(args[0]);
334 printf("%s(%d)", args[0], id);
335 for (n = 1; n < nargs; n++) {
336 printf(" '%s'", args[n]);
337 }
338 printf("\n");
339}
340
341void parse_new_section(struct parse_state *state, int kw,
342 int nargs, char **args)
343{
344 printf("[ %s %s ]\n", args[0],
345 nargs > 1 ? args[1] : "");
346 switch(kw) {
347 case K_service:
348 state->context = parse_service(state, nargs, args);
349 if (state->context) {
350 state->parse_line = parse_line_service;
351 return;
352 }
353 break;
354 case K_on:
355 state->context = parse_action(state, nargs, args);
356 if (state->context) {
357 state->parse_line = parse_line_action;
358 return;
359 }
360 break;
361 }
362 state->parse_line = parse_line_no_op;
363}
364
365static void parse_config(const char *fn, char *s)
366{
367 struct parse_state state;
San Mehatf24e2522009-05-19 13:30:46 -0700368 char *args[SVC_MAXARGS];
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800369 int nargs;
370
371 nargs = 0;
372 state.filename = fn;
373 state.line = 1;
374 state.ptr = s;
375 state.nexttoken = 0;
376 state.parse_line = parse_line_no_op;
377 for (;;) {
378 switch (next_token(&state)) {
379 case T_EOF:
380 state.parse_line(&state, 0, 0);
381 return;
382 case T_NEWLINE:
383 if (nargs) {
384 int kw = lookup_keyword(args[0]);
385 if (kw_is(kw, SECTION)) {
386 state.parse_line(&state, 0, 0);
387 parse_new_section(&state, kw, nargs, args);
388 } else {
389 state.parse_line(&state, nargs, args);
390 }
391 nargs = 0;
392 }
393 break;
394 case T_TEXT:
San Mehatf24e2522009-05-19 13:30:46 -0700395 if (nargs < SVC_MAXARGS) {
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800396 args[nargs++] = state.text;
397 }
398 break;
399 }
400 }
401}
402
403int parse_config_file(const char *fn)
404{
405 char *data;
406 data = read_file(fn, 0);
407 if (!data) return -1;
408
409 parse_config(fn, data);
410 DUMP();
411 return 0;
412}
413
414static int valid_name(const char *name)
415{
416 if (strlen(name) > 16) {
417 return 0;
418 }
419 while (*name) {
420 if (!isalnum(*name) && (*name != '_') && (*name != '-')) {
421 return 0;
422 }
423 name++;
424 }
425 return 1;
426}
427
428struct service *service_find_by_name(const char *name)
429{
430 struct listnode *node;
431 struct service *svc;
432 list_for_each(node, &service_list) {
433 svc = node_to_item(node, struct service, slist);
434 if (!strcmp(svc->name, name)) {
435 return svc;
436 }
437 }
438 return 0;
439}
440
441struct service *service_find_by_pid(pid_t pid)
442{
443 struct listnode *node;
444 struct service *svc;
445 list_for_each(node, &service_list) {
446 svc = node_to_item(node, struct service, slist);
447 if (svc->pid == pid) {
448 return svc;
449 }
450 }
451 return 0;
452}
453
454struct service *service_find_by_keychord(int keychord_id)
455{
456 struct listnode *node;
457 struct service *svc;
458 list_for_each(node, &service_list) {
459 svc = node_to_item(node, struct service, slist);
460 if (svc->keychord_id == keychord_id) {
461 return svc;
462 }
463 }
464 return 0;
465}
466
467void service_for_each(void (*func)(struct service *svc))
468{
469 struct listnode *node;
470 struct service *svc;
471 list_for_each(node, &service_list) {
472 svc = node_to_item(node, struct service, slist);
473 func(svc);
474 }
475}
476
477void service_for_each_class(const char *classname,
478 void (*func)(struct service *svc))
479{
480 struct listnode *node;
481 struct service *svc;
482 list_for_each(node, &service_list) {
483 svc = node_to_item(node, struct service, slist);
484 if (!strcmp(svc->classname, classname)) {
485 func(svc);
486 }
487 }
488}
489
490void service_for_each_flags(unsigned matchflags,
491 void (*func)(struct service *svc))
492{
493 struct listnode *node;
494 struct service *svc;
495 list_for_each(node, &service_list) {
496 svc = node_to_item(node, struct service, slist);
497 if (svc->flags & matchflags) {
498 func(svc);
499 }
500 }
501}
502
503void action_for_each_trigger(const char *trigger,
504 void (*func)(struct action *act))
505{
506 struct listnode *node;
507 struct action *act;
508 list_for_each(node, &action_list) {
509 act = node_to_item(node, struct action, alist);
510 if (!strcmp(act->name, trigger)) {
511 func(act);
512 }
513 }
514}
515
516void queue_property_triggers(const char *name, const char *value)
517{
518 struct listnode *node;
519 struct action *act;
520 list_for_each(node, &action_list) {
521 act = node_to_item(node, struct action, alist);
522 if (!strncmp(act->name, "property:", strlen("property:"))) {
523 const char *test = act->name + strlen("property:");
524 int name_length = strlen(name);
525
526 if (!strncmp(name, test, name_length) &&
527 test[name_length] == '=' &&
528 !strcmp(test + name_length + 1, value)) {
529 action_add_queue_tail(act);
530 }
531 }
532 }
533}
534
535void queue_all_property_triggers()
536{
537 struct listnode *node;
538 struct action *act;
539 list_for_each(node, &action_list) {
540 act = node_to_item(node, struct action, alist);
541 if (!strncmp(act->name, "property:", strlen("property:"))) {
542 /* parse property name and value
543 syntax is property:<name>=<value> */
544 const char* name = act->name + strlen("property:");
545 const char* equals = strchr(name, '=');
546 if (equals) {
Mike Lockwoodb3779552009-05-08 14:27:42 -0400547 char prop_name[PROP_NAME_MAX + 1];
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800548 const char* value;
549 int length = equals - name;
550 if (length > PROP_NAME_MAX) {
551 ERROR("property name too long in trigger %s", act->name);
552 } else {
553 memcpy(prop_name, name, length);
554 prop_name[length] = 0;
555
556 /* does the property exist, and match the trigger value? */
Mike Lockwoodb3779552009-05-08 14:27:42 -0400557 value = property_get(prop_name);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800558 if (value && !strcmp(equals + 1, value)) {
559 action_add_queue_tail(act);
560 }
561 }
562 }
563 }
564 }
565}
566
Colin Crossebc6ff12010-04-13 19:52:01 -0700567void queue_builtin_action(int (*func)(int nargs, char **args), char *name)
568{
569 struct action *act;
570 struct command *cmd;
571
572 act = calloc(1, sizeof(*act));
573 act->name = name;
574 list_init(&act->commands);
575
576 cmd = calloc(1, sizeof(*cmd));
577 cmd->func = func;
578 cmd->args[0] = name;
579 list_add_tail(&act->commands, &cmd->clist);
580
581 list_add_tail(&action_list, &act->alist);
582 action_add_queue_tail(act);
583}
584
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800585void action_add_queue_tail(struct action *act)
586{
587 list_add_tail(&action_queue, &act->qlist);
588}
589
590struct action *action_remove_queue_head(void)
591{
592 if (list_empty(&action_queue)) {
593 return 0;
594 } else {
595 struct listnode *node = list_head(&action_queue);
596 struct action *act = node_to_item(node, struct action, qlist);
597 list_remove(node);
598 return act;
599 }
600}
601
Colin Crossebc6ff12010-04-13 19:52:01 -0700602int action_queue_empty()
603{
604 return list_empty(&action_queue);
605}
606
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800607static void *parse_service(struct parse_state *state, int nargs, char **args)
608{
609 struct service *svc;
610 if (nargs < 3) {
611 parse_error(state, "services must have a name and a program\n");
612 return 0;
613 }
614 if (!valid_name(args[1])) {
615 parse_error(state, "invalid service name '%s'\n", args[1]);
616 return 0;
617 }
618
619 svc = service_find_by_name(args[1]);
620 if (svc) {
621 parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]);
622 return 0;
623 }
624
625 nargs -= 2;
626 svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs);
627 if (!svc) {
628 parse_error(state, "out of memory\n");
629 return 0;
630 }
631 svc->name = args[1];
632 svc->classname = "default";
633 memcpy(svc->args, args + 2, sizeof(char*) * nargs);
634 svc->args[nargs] = 0;
635 svc->nargs = nargs;
636 svc->onrestart.name = "onrestart";
637 list_init(&svc->onrestart.commands);
638 list_add_tail(&service_list, &svc->slist);
639 return svc;
640}
641
642static void parse_line_service(struct parse_state *state, int nargs, char **args)
643{
644 struct service *svc = state->context;
645 struct command *cmd;
646 int i, kw, kw_nargs;
647
648 if (nargs == 0) {
649 return;
650 }
651
San Mehat4e221f02010-02-25 14:19:50 -0800652 svc->ioprio_class = IoSchedClass_NONE;
653
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800654 kw = lookup_keyword(args[0]);
655 switch (kw) {
656 case K_capability:
657 break;
658 case K_class:
659 if (nargs != 2) {
660 parse_error(state, "class option requires a classname\n");
661 } else {
662 svc->classname = args[1];
663 }
664 break;
665 case K_console:
666 svc->flags |= SVC_CONSOLE;
667 break;
668 case K_disabled:
669 svc->flags |= SVC_DISABLED;
670 break;
San Mehat4e221f02010-02-25 14:19:50 -0800671 case K_ioprio:
672 if (nargs != 3) {
673 parse_error(state, "ioprio optin usage: ioprio <rt|be|idle> <ioprio 0-7>\n");
674 } else {
675 svc->ioprio_pri = strtoul(args[2], 0, 8);
676
677 if (svc->ioprio_pri < 0 || svc->ioprio_pri > 7) {
678 parse_error(state, "priority value must be range 0 - 7\n");
679 break;
680 }
681
682 if (!strcmp(args[1], "rt")) {
683 svc->ioprio_class = IoSchedClass_RT;
684 } else if (!strcmp(args[1], "be")) {
685 svc->ioprio_class = IoSchedClass_BE;
686 } else if (!strcmp(args[1], "idle")) {
687 svc->ioprio_class = IoSchedClass_IDLE;
688 } else {
689 parse_error(state, "ioprio option usage: ioprio <rt|be|idle> <0-7>\n");
690 }
691 }
692 break;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800693 case K_group:
694 if (nargs < 2) {
695 parse_error(state, "group option requires a group id\n");
696 } else if (nargs > NR_SVC_SUPP_GIDS + 2) {
697 parse_error(state, "group option accepts at most %d supp. groups\n",
698 NR_SVC_SUPP_GIDS);
699 } else {
700 int n;
701 svc->gid = decode_uid(args[1]);
702 for (n = 2; n < nargs; n++) {
703 svc->supp_gids[n-2] = decode_uid(args[n]);
704 }
705 svc->nr_supp_gids = n - 2;
706 }
707 break;
708 case K_keycodes:
709 if (nargs < 2) {
710 parse_error(state, "keycodes option requires atleast one keycode\n");
711 } else {
712 svc->keycodes = malloc((nargs - 1) * sizeof(svc->keycodes[0]));
713 if (!svc->keycodes) {
714 parse_error(state, "could not allocate keycodes\n");
715 } else {
716 svc->nkeycodes = nargs - 1;
717 for (i = 1; i < nargs; i++) {
718 svc->keycodes[i - 1] = atoi(args[i]);
719 }
720 }
721 }
722 break;
723 case K_oneshot:
724 svc->flags |= SVC_ONESHOT;
725 break;
726 case K_onrestart:
727 nargs--;
728 args++;
729 kw = lookup_keyword(args[0]);
730 if (!kw_is(kw, COMMAND)) {
731 parse_error(state, "invalid command '%s'\n", args[0]);
732 break;
733 }
734 kw_nargs = kw_nargs(kw);
735 if (nargs < kw_nargs) {
736 parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1,
737 kw_nargs > 2 ? "arguments" : "argument");
738 break;
739 }
740
741 cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);
742 cmd->func = kw_func(kw);
743 cmd->nargs = nargs;
744 memcpy(cmd->args, args, sizeof(char*) * nargs);
745 list_add_tail(&svc->onrestart.commands, &cmd->clist);
746 break;
747 case K_critical:
748 svc->flags |= SVC_CRITICAL;
749 break;
750 case K_setenv: { /* name value */
751 struct svcenvinfo *ei;
752 if (nargs < 2) {
753 parse_error(state, "setenv option requires name and value arguments\n");
754 break;
755 }
756 ei = calloc(1, sizeof(*ei));
757 if (!ei) {
758 parse_error(state, "out of memory\n");
759 break;
760 }
761 ei->name = args[1];
762 ei->value = args[2];
763 ei->next = svc->envvars;
764 svc->envvars = ei;
765 break;
766 }
767 case K_socket: {/* name type perm [ uid gid ] */
768 struct socketinfo *si;
769 if (nargs < 4) {
770 parse_error(state, "socket option requires name, type, perm arguments\n");
771 break;
772 }
773 if (strcmp(args[2],"dgram") && strcmp(args[2],"stream")) {
774 parse_error(state, "socket type must be 'dgram' or 'stream'\n");
775 break;
776 }
777 si = calloc(1, sizeof(*si));
778 if (!si) {
779 parse_error(state, "out of memory\n");
780 break;
781 }
782 si->name = args[1];
783 si->type = args[2];
784 si->perm = strtoul(args[3], 0, 8);
785 if (nargs > 4)
786 si->uid = decode_uid(args[4]);
787 if (nargs > 5)
788 si->gid = decode_uid(args[5]);
789 si->next = svc->sockets;
790 svc->sockets = si;
791 break;
792 }
793 case K_user:
794 if (nargs != 2) {
795 parse_error(state, "user option requires a user id\n");
796 } else {
797 svc->uid = decode_uid(args[1]);
798 }
799 break;
800 default:
801 parse_error(state, "invalid option '%s'\n", args[0]);
802 }
803}
804
805static void *parse_action(struct parse_state *state, int nargs, char **args)
806{
807 struct action *act;
808 if (nargs < 2) {
809 parse_error(state, "actions must have a trigger\n");
810 return 0;
811 }
812 if (nargs > 2) {
813 parse_error(state, "actions may not have extra parameters\n");
814 return 0;
815 }
816 act = calloc(1, sizeof(*act));
817 act->name = args[1];
818 list_init(&act->commands);
819 list_add_tail(&action_list, &act->alist);
820 /* XXX add to hash */
821 return act;
822}
823
824static void parse_line_action(struct parse_state* state, int nargs, char **args)
825{
826 struct command *cmd;
827 struct action *act = state->context;
828 int (*func)(int nargs, char **args);
829 int kw, n;
830
831 if (nargs == 0) {
832 return;
833 }
834
835 kw = lookup_keyword(args[0]);
836 if (!kw_is(kw, COMMAND)) {
837 parse_error(state, "invalid command '%s'\n", args[0]);
838 return;
839 }
840
841 n = kw_nargs(kw);
842 if (nargs < n) {
843 parse_error(state, "%s requires %d %s\n", args[0], n - 1,
844 n > 2 ? "arguments" : "argument");
845 return;
846 }
847 cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);
848 cmd->func = kw_func(kw);
849 cmd->nargs = nargs;
850 memcpy(cmd->args, args, sizeof(char*) * nargs);
851 list_add_tail(&act->commands, &cmd->clist);
852}