• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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