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