blob: b045216fc62cff3205e60257d3efefa371b23385 [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
Colin Crossebc6ff12010-04-13 19:52:01 -0700565void queue_builtin_action(int (*func)(int nargs, char **args), char *name)
566{
567 struct action *act;
568 struct command *cmd;
569
570 act = calloc(1, sizeof(*act));
571 act->name = name;
572 list_init(&act->commands);
573
574 cmd = calloc(1, sizeof(*cmd));
575 cmd->func = func;
576 cmd->args[0] = name;
577 list_add_tail(&act->commands, &cmd->clist);
578
579 list_add_tail(&action_list, &act->alist);
580 action_add_queue_tail(act);
581}
582
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800583void action_add_queue_tail(struct action *act)
584{
585 list_add_tail(&action_queue, &act->qlist);
586}
587
588struct action *action_remove_queue_head(void)
589{
590 if (list_empty(&action_queue)) {
591 return 0;
592 } else {
593 struct listnode *node = list_head(&action_queue);
594 struct action *act = node_to_item(node, struct action, qlist);
595 list_remove(node);
596 return act;
597 }
598}
599
Colin Crossebc6ff12010-04-13 19:52:01 -0700600int action_queue_empty()
601{
602 return list_empty(&action_queue);
603}
604
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800605static void *parse_service(struct parse_state *state, int nargs, char **args)
606{
607 struct service *svc;
608 if (nargs < 3) {
609 parse_error(state, "services must have a name and a program\n");
610 return 0;
611 }
612 if (!valid_name(args[1])) {
613 parse_error(state, "invalid service name '%s'\n", args[1]);
614 return 0;
615 }
616
617 svc = service_find_by_name(args[1]);
618 if (svc) {
619 parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]);
620 return 0;
621 }
622
623 nargs -= 2;
624 svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs);
625 if (!svc) {
626 parse_error(state, "out of memory\n");
627 return 0;
628 }
629 svc->name = args[1];
630 svc->classname = "default";
631 memcpy(svc->args, args + 2, sizeof(char*) * nargs);
632 svc->args[nargs] = 0;
633 svc->nargs = nargs;
634 svc->onrestart.name = "onrestart";
635 list_init(&svc->onrestart.commands);
636 list_add_tail(&service_list, &svc->slist);
637 return svc;
638}
639
640static void parse_line_service(struct parse_state *state, int nargs, char **args)
641{
642 struct service *svc = state->context;
643 struct command *cmd;
644 int i, kw, kw_nargs;
645
646 if (nargs == 0) {
647 return;
648 }
649
San Mehat4e221f02010-02-25 14:19:50 -0800650 svc->ioprio_class = IoSchedClass_NONE;
651
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800652 kw = lookup_keyword(args[0]);
653 switch (kw) {
654 case K_capability:
655 break;
656 case K_class:
657 if (nargs != 2) {
658 parse_error(state, "class option requires a classname\n");
659 } else {
660 svc->classname = args[1];
661 }
662 break;
663 case K_console:
664 svc->flags |= SVC_CONSOLE;
665 break;
666 case K_disabled:
667 svc->flags |= SVC_DISABLED;
668 break;
San Mehat4e221f02010-02-25 14:19:50 -0800669 case K_ioprio:
670 if (nargs != 3) {
671 parse_error(state, "ioprio optin usage: ioprio <rt|be|idle> <ioprio 0-7>\n");
672 } else {
673 svc->ioprio_pri = strtoul(args[2], 0, 8);
674
675 if (svc->ioprio_pri < 0 || svc->ioprio_pri > 7) {
676 parse_error(state, "priority value must be range 0 - 7\n");
677 break;
678 }
679
680 if (!strcmp(args[1], "rt")) {
681 svc->ioprio_class = IoSchedClass_RT;
682 } else if (!strcmp(args[1], "be")) {
683 svc->ioprio_class = IoSchedClass_BE;
684 } else if (!strcmp(args[1], "idle")) {
685 svc->ioprio_class = IoSchedClass_IDLE;
686 } else {
687 parse_error(state, "ioprio option usage: ioprio <rt|be|idle> <0-7>\n");
688 }
689 }
690 break;
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800691 case K_group:
692 if (nargs < 2) {
693 parse_error(state, "group option requires a group id\n");
694 } else if (nargs > NR_SVC_SUPP_GIDS + 2) {
695 parse_error(state, "group option accepts at most %d supp. groups\n",
696 NR_SVC_SUPP_GIDS);
697 } else {
698 int n;
699 svc->gid = decode_uid(args[1]);
700 for (n = 2; n < nargs; n++) {
701 svc->supp_gids[n-2] = decode_uid(args[n]);
702 }
703 svc->nr_supp_gids = n - 2;
704 }
705 break;
706 case K_keycodes:
707 if (nargs < 2) {
708 parse_error(state, "keycodes option requires atleast one keycode\n");
709 } else {
710 svc->keycodes = malloc((nargs - 1) * sizeof(svc->keycodes[0]));
711 if (!svc->keycodes) {
712 parse_error(state, "could not allocate keycodes\n");
713 } else {
714 svc->nkeycodes = nargs - 1;
715 for (i = 1; i < nargs; i++) {
716 svc->keycodes[i - 1] = atoi(args[i]);
717 }
718 }
719 }
720 break;
721 case K_oneshot:
722 svc->flags |= SVC_ONESHOT;
723 break;
724 case K_onrestart:
725 nargs--;
726 args++;
727 kw = lookup_keyword(args[0]);
728 if (!kw_is(kw, COMMAND)) {
729 parse_error(state, "invalid command '%s'\n", args[0]);
730 break;
731 }
732 kw_nargs = kw_nargs(kw);
733 if (nargs < kw_nargs) {
734 parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1,
735 kw_nargs > 2 ? "arguments" : "argument");
736 break;
737 }
738
739 cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);
740 cmd->func = kw_func(kw);
741 cmd->nargs = nargs;
742 memcpy(cmd->args, args, sizeof(char*) * nargs);
743 list_add_tail(&svc->onrestart.commands, &cmd->clist);
744 break;
745 case K_critical:
746 svc->flags |= SVC_CRITICAL;
747 break;
748 case K_setenv: { /* name value */
749 struct svcenvinfo *ei;
750 if (nargs < 2) {
751 parse_error(state, "setenv option requires name and value arguments\n");
752 break;
753 }
754 ei = calloc(1, sizeof(*ei));
755 if (!ei) {
756 parse_error(state, "out of memory\n");
757 break;
758 }
759 ei->name = args[1];
760 ei->value = args[2];
761 ei->next = svc->envvars;
762 svc->envvars = ei;
763 break;
764 }
765 case K_socket: {/* name type perm [ uid gid ] */
766 struct socketinfo *si;
767 if (nargs < 4) {
768 parse_error(state, "socket option requires name, type, perm arguments\n");
769 break;
770 }
771 if (strcmp(args[2],"dgram") && strcmp(args[2],"stream")) {
772 parse_error(state, "socket type must be 'dgram' or 'stream'\n");
773 break;
774 }
775 si = calloc(1, sizeof(*si));
776 if (!si) {
777 parse_error(state, "out of memory\n");
778 break;
779 }
780 si->name = args[1];
781 si->type = args[2];
782 si->perm = strtoul(args[3], 0, 8);
783 if (nargs > 4)
784 si->uid = decode_uid(args[4]);
785 if (nargs > 5)
786 si->gid = decode_uid(args[5]);
787 si->next = svc->sockets;
788 svc->sockets = si;
789 break;
790 }
791 case K_user:
792 if (nargs != 2) {
793 parse_error(state, "user option requires a user id\n");
794 } else {
795 svc->uid = decode_uid(args[1]);
796 }
797 break;
798 default:
799 parse_error(state, "invalid option '%s'\n", args[0]);
800 }
801}
802
803static void *parse_action(struct parse_state *state, int nargs, char **args)
804{
805 struct action *act;
806 if (nargs < 2) {
807 parse_error(state, "actions must have a trigger\n");
808 return 0;
809 }
810 if (nargs > 2) {
811 parse_error(state, "actions may not have extra parameters\n");
812 return 0;
813 }
814 act = calloc(1, sizeof(*act));
815 act->name = args[1];
816 list_init(&act->commands);
817 list_add_tail(&action_list, &act->alist);
818 /* XXX add to hash */
819 return act;
820}
821
822static void parse_line_action(struct parse_state* state, int nargs, char **args)
823{
824 struct command *cmd;
825 struct action *act = state->context;
826 int (*func)(int nargs, char **args);
827 int kw, n;
828
829 if (nargs == 0) {
830 return;
831 }
832
833 kw = lookup_keyword(args[0]);
834 if (!kw_is(kw, COMMAND)) {
835 parse_error(state, "invalid command '%s'\n", args[0]);
836 return;
837 }
838
839 n = kw_nargs(kw);
840 if (nargs < n) {
841 parse_error(state, "%s requires %d %s\n", args[0], n - 1,
842 n > 2 ? "arguments" : "argument");
843 return;
844 }
845 cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);
846 cmd->func = kw_func(kw);
847 cmd->nargs = nargs;
848 memcpy(cmd->args, args, sizeof(char*) * nargs);
849 list_add_tail(&act->commands, &cmd->clist);
850}