root/src/noit_console_state.c

Revision db656f3332551668dbf8656d8dcd05fe917ddcd7, 14.4 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 6 years ago)

pull help and docs into the console, refs #21

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  */
5
6 #include "noit_defines.h"
7
8 #include "eventer/eventer.h"
9 #include "utils/noit_log.h"
10 #include "utils/noit_hash.h"
11 #include "noit_listener.h"
12 #include "noit_console.h"
13 #include "noit_tokenizer.h"
14 #include "noit_module.h"
15
16 #include <netinet/in.h>
17 #include <arpa/inet.h>
18 #include <pcre.h>
19
20 int cmd_info_comparek(const void *akv, const void *bv) {
21   char *ak = (char *)akv;
22   cmd_info_t *b = (cmd_info_t *)bv;
23   return strcasecmp(ak, b->name);
24 }
25 int cmd_info_compare(const void *av, const void *bv) {
26   cmd_info_t *a = (cmd_info_t *)av;
27   cmd_info_t *b = (cmd_info_t *)bv;
28   return strcasecmp(a->name, b->name);
29 }
30
31 cmd_info_t console_command_help = {
32   "help", noit_console_help, NULL, NULL
33 };
34 cmd_info_t console_command_exit = {
35   "exit", noit_console_state_pop, NULL, NULL
36 };
37 cmd_info_t console_command_shutdown = {
38   "shutdown", noit_console_shutdown, NULL, NULL
39 };
40 cmd_info_t console_command_restart = {
41   "restart", noit_console_restart, NULL, NULL
42 };
43
44 void
45 noit_console_add_help(const char *topic, console_cmd_func_t topic_func) {
46   noit_console_state_t *s = console_command_help.dstate;
47   if(!s) {
48     console_command_help.dstate = s = calloc(1, sizeof(*s));
49     noit_skiplist_init(&s->cmds);
50     noit_skiplist_set_compare(&s->cmds, cmd_info_compare, cmd_info_comparek);
51   }
52   noit_console_state_add_cmd(s, NCSCMD(topic, topic_func, NULL, NULL));
53 }
54
55 static char *
56 noit_console_state_prompt(EditLine *el) {
57   static char *tl = "noit# ";
58   return tl;
59 }
60
61 static char *
62 apply_replace(const char *src, const char *name, const char *value) {
63   char *result, *cp;
64   const char *nextpat, *searchstart;
65   char pat[256];
66   int maxlen, patlen, vlen, slen;
67   snprintf(pat, sizeof(pat), "{%s}", name);
68   patlen = strlen(pat);
69   vlen = strlen(value);
70   slen = strlen(src);
71   /* Worst case is just a stream of replacements. */
72   maxlen = (slen / patlen) * MAX(vlen,patlen) + (slen % patlen) + 1;
73   cp = result = malloc(maxlen);
74   searchstart = src;
75   while((nextpat = strstr(searchstart, pat)) != NULL) {
76     memcpy(cp, searchstart, nextpat - searchstart); /* pull the prefix */
77     cp += nextpat - searchstart;                    /* advance destination */
78     memcpy(cp, value, vlen);                        /* copy replacement */
79     cp += vlen;                                     /* advance destination */
80     searchstart = nextpat + patlen;                 /* set new searchstart */
81   }
82   /* Pick up the trailer (plus '\0') */
83   memcpy(cp, searchstart, strlen(searchstart)+1);
84   return result;
85 }
86 static pcre *IP_match = NULL;
87 static pcre *numeric_match = NULL;
88 static int
89 expand_range(const char *range, char ***set, int max_count, const char **err) {
90   int count, erroff, ovector[30], rv;
91   char buff[32]; /* submatches */
92   const char *pcre_err;
93   *err = NULL;
94   if(!IP_match) {
95     IP_match = pcre_compile("^(full:)?(\\d+\\.\\d+\\.\\d+\\.\\d+)/(\\d+)$",
96                             0, &pcre_err, &erroff, NULL);
97     if(!IP_match) {
98       *err = "IP match pattern failed to compile!";
99       noitL(noit_error, "pcre_compiled failed offset %d: %s\n", erroff, pcre_err);
100       return -1;
101     }
102   }
103   if(!numeric_match) {
104     numeric_match = pcre_compile("^(\\d+)(?:,(\\d+))?\\.\\.(\\d+)$",
105                                  0, &pcre_err, &erroff, NULL);
106     if(!numeric_match) {
107       *err = "numeric match pattern failed to compile!";
108       noitL(noit_error, "pcre_compiled failed offset %d: %s\n", erroff, pcre_err);
109       return -1;
110     }
111   }
112   rv = pcre_exec(IP_match, NULL, range, strlen(range), 0, 0, ovector, 30);
113   if(rv >= 0) {
114     int mask, full = 0, i;
115     u_int32_t host_addr;
116     struct in_addr addr;
117     /* 0 is the full monty, 1 is "" or "full:", 2 is the IP, 3 is the mask */
118     pcre_copy_substring(range, ovector, rv, 1, buff, sizeof(buff));
119     full = buff[0] ? 1 : 0;
120     pcre_copy_substring(range, ovector, rv, 3, buff, sizeof(buff));
121     mask = atoi(buff);
122     if(mask == 32) full = 1; /* host implies.. the host */
123     if(mask < 0 || mask > 32) {
124       *err = "invalid netmask";
125       return 0;
126     }
127     count = 1 << (32-mask);
128     pcre_copy_substring(range, ovector, rv, 2, buff, sizeof(buff));
129     if(inet_pton(AF_INET, buff, &addr) != 1) {
130       *err = "could not parse IP address";
131       return 0;
132     }
133     host_addr = ntohl(addr.s_addr);
134     host_addr &= ~((u_int32_t)count - 1);
135
136     if(!full) count -= 2; /* No network or broadcast */
137     if(count > max_count || !count) return -count;
138     if(!full) host_addr++; /* Skip the network address */
139
140     *set = malloc(count * sizeof(**set));
141     for(i=0; i<count; i++)  {
142       addr.s_addr = htonl(host_addr + i);
143       inet_ntop(AF_INET, &addr, buff, sizeof(buff));
144       (*set)[i] = strdup(buff);
145     }
146     return count;
147   }
148   rv = pcre_exec(numeric_match, NULL, range, strlen(range), 0, 0, ovector, 30);
149   if(rv >= 0) {
150     int s, n, e, i;
151     pcre_copy_substring(range, ovector, rv, 1, buff, sizeof(buff));
152     s = atoi(buff);
153     pcre_copy_substring(range, ovector, rv, 3, buff, sizeof(buff));
154     e = atoi(buff);
155     pcre_copy_substring(range, ovector, rv, 2, buff, sizeof(buff));
156     if(buff[0]) n = atoi(buff);
157     else n = (s<e) ? s+1 : s-1;
158
159     /* Ensure that s < n < e */
160     if((s<e && s>n) || (s>e && s<n)) {
161       *err = "mixed up sequence";
162       return 0;
163     }
164     i = n - s; /* Our increment */
165     count = (e - s) / i + 1;
166     *set = malloc(count * sizeof(**set));
167     count = 0;
168     for(; (i>0 && s<=e) || (i<0 && s>=e); s += i) {
169       snprintf(buff, sizeof(buff), "%d", s);
170       (*set)[count] = strdup(buff);
171       count++;
172     }
173     return count;
174   }
175   *err = "cannot understand range";
176   return 0;
177 }
178 int
179 noit_console_generic_apply(noit_console_closure_t ncct,
180                            int argc, char **argv,
181                            noit_console_state_t *dstate,
182                            void *closure) {
183   int i, j, count;
184   char *name, *range;
185   char **nargv, **expanded;
186   const char *err;
187   int problems = 0;
188   if(argc < 3) {
189     nc_printf(ncct, "apply <name> <range> cmd ...\n");
190     return -1;
191   }
192   name = argv[0];
193   range = argv[1];
194   argc -= 2;
195   argv += 2;
196
197   count = expand_range(range, &expanded, 256, &err);
198   if(!count) {
199     nc_printf(ncct, "apply error: '%s' range produced nothing [%s]\n",
200               range, err ? err : "unknown error");
201     return -1;
202   }
203   if(count < 0) {
204     nc_printf(ncct, "apply error: '%s' range would produce %d items.\n",
205               range, count);
206     return -1;
207   }
208   nargv = malloc(argc * sizeof(*nargv));
209   for(i=0; i<count; i++) {
210     for(j=0; j<argc; j++) nargv[j] = apply_replace(argv[j], name, expanded[i]);
211     if(noit_console_state_do(ncct, argc, nargv)) problems = -1;
212     for(j=0; j<argc; j++) free(nargv[j]);
213     free(expanded[i]);
214   }
215   free(nargv);
216   free(expanded);
217   return problems;
218 }
219
220 int
221 noit_console_render_help(noit_console_closure_t ncct,
222                          noit_console_state_t *dstate) {
223   noit_skiplist_node *iter = NULL;
224   if(!dstate) {
225     nc_printf(ncct, "No help available.\n");
226     return -1;
227   }
228   for(iter = noit_skiplist_getlist(&dstate->cmds); iter;
229       noit_skiplist_next(&dstate->cmds,&iter)) {
230     cmd_info_t *cmd = iter->data;
231     if(strcmp(cmd->name, "help")) nc_printf(ncct, "  ==> '%s'\n", cmd->name);
232   }
233   return 0;
234 }
235
236 int
237 noit_console_state_delegate(noit_console_closure_t ncct,
238                             int argc, char **argv,
239                             noit_console_state_t *dstate,
240                             void *closure) {
241   noit_console_state_stack_t tmps = { 0 };
242
243   if(argc == 0) {
244     noit_console_render_help(ncct, dstate);
245     nc_printf(ncct, "incomplete command.\n");
246     return -1;
247   }
248   if(!dstate) {
249     nc_printf(ncct, "internal error: no delegate state\n");
250     return -1;
251   }
252   tmps.state = dstate;
253   return _noit_console_state_do(ncct, &tmps, argc, argv);
254 }
255
256 int
257 _noit_console_state_do(noit_console_closure_t ncct,
258                        noit_console_state_stack_t *stack,
259                        int argc, char **argv) {
260   noit_skiplist_node *next, *amb = NULL;
261   cmd_info_t *cmd;
262
263   if(!argc) {
264     noit_console_render_help(ncct, stack->state);
265     nc_printf(ncct, "arguments expected\n");
266     return -1;
267   }
268   cmd = noit_skiplist_find_neighbors(&stack->state->cmds, argv[0],
269                                      NULL, NULL, &next);
270   if(!cmd) {
271     int ambiguous = 0;
272     if(next) {
273       cmd_info_t *pcmd = NULL;
274       cmd = next->data;
275       amb = next;
276       noit_skiplist_next(&stack->state->cmds, &amb);
277       if(amb) pcmd = amb->data;
278       /* So cmd is the next in line... pcmd is the one after that.
279        * If they both strncasecmp to 0, we're ambiguous,
280        *    neither, then we're not found.
281        *    only cmd, then we've found a partial, unambiguous.
282        */
283       if(strncasecmp(cmd->name, argv[0], strlen(argv[0])) == 0) {
284         if(pcmd && strncasecmp(pcmd->name, argv[0], strlen(argv[0])) == 0) {
285           cmd = NULL;
286           ambiguous = 1;
287         }
288       }
289       else
290         cmd = NULL;
291     }
292     if(!cmd) {
293       if(ambiguous || !strcmp(argv[0], "?")) {
294         char *partial = ambiguous ? argv[0] : "";
295         if(ambiguous) nc_printf(ncct, "Ambiguous command: '%s'\n", argv[0]);
296         amb = ambiguous ? next : noit_skiplist_getlist(&stack->state->cmds);
297         for(; amb; noit_skiplist_next(&stack->state->cmds, &amb)) {
298           cmd = amb->data;
299           if(!strlen(partial) || strncasecmp(cmd->name, partial, strlen(partial)) == 0)
300             nc_printf(ncct, "\t%s\n", cmd->name);
301           else
302             break;
303         }
304       }
305       else
306         nc_printf(ncct, "No such command: '%s'\n", argv[0]);
307       return -1;
308     }
309   }
310   if(ncct->state_stack->name) free(ncct->state_stack->name);
311   ncct->state_stack->name = strdup(cmd->name);
312   return cmd->func(ncct, argc-1, argv+1, cmd->dstate, cmd->closure);
313 }
314 int
315 noit_console_state_do(noit_console_closure_t ncct, int argc, char **argv) {
316   return _noit_console_state_do(ncct, ncct->state_stack, argc, argv);
317 }
318
319 noit_console_state_t *
320 noit_console_state_alloc(void) {
321   noit_console_state_t *s;
322   s = calloc(1, sizeof(*s));
323   noit_skiplist_init(&s->cmds);
324   noit_skiplist_set_compare(&s->cmds, cmd_info_compare, cmd_info_comparek);
325   noit_console_state_add_cmd(s,
326       NCSCMD("apply", noit_console_generic_apply, NULL, NULL));
327   noit_console_state_add_cmd(s, &console_command_help);
328   return s;
329 }
330
331 int
332 noit_console_state_add_cmd(noit_console_state_t *state,
333                            cmd_info_t *cmd) {
334   return (noit_skiplist_insert(&state->cmds, cmd) != NULL);
335 }
336
337 cmd_info_t *
338 noit_console_state_get_cmd(noit_console_state_t *state,
339                            const char *name) {
340   cmd_info_t *cmd;
341   cmd = noit_skiplist_find(&state->cmds, name, NULL);
342   return cmd;
343 }
344
345 noit_console_state_t *
346 noit_console_state_build(console_prompt_func_t promptf, cmd_info_t **clist,
347                          state_free_func_t sfreef) {
348   noit_console_state_t *state;
349   state = noit_console_state_alloc();
350   state->console_prompt_function = promptf;
351   while(*clist) {
352     noit_skiplist_insert(&state->cmds, *clist);
353     clist++;
354   }
355   state->statefree = sfreef;
356   return state;
357 }
358
359 cmd_info_t *NCSCMD(const char *name, console_cmd_func_t func,
360                    noit_console_state_t *dstate, void *closure) {
361   cmd_info_t *cmd;
362   cmd = calloc(1, sizeof(*cmd));
363   cmd->name = strdup(name);
364   cmd->func = func;
365   cmd->dstate = dstate;
366   cmd->closure = closure;
367   return cmd;
368 }
369
370 noit_console_state_t *
371 noit_console_state_initial() {
372   static noit_console_state_t *_top_level_state = NULL;
373   if(!_top_level_state) {
374     static noit_console_state_t *no_state;
375     _top_level_state = noit_console_state_alloc();
376     noit_console_state_add_cmd(_top_level_state, &console_command_exit);
377     noit_console_state_add_cmd(_top_level_state,
378       NCSCMD("show", noit_console_state_delegate,
379              noit_console_state_alloc(), NULL));
380     no_state = noit_console_state_alloc();
381     noit_console_state_add_cmd(_top_level_state,
382       NCSCMD("no", noit_console_state_delegate,
383              no_state, NULL));
384
385     noit_console_state_add_cmd(_top_level_state, &console_command_shutdown);
386     noit_console_state_add_cmd(_top_level_state, &console_command_restart);
387   }
388   return _top_level_state;
389 }
390
391 void
392 noit_console_state_push_state(noit_console_closure_t ncct,
393                               noit_console_state_t *state) {
394   noit_console_state_stack_t *stack;
395   stack = calloc(1, sizeof(*stack));
396   stack->last = ncct->state_stack;
397   stack->state = state;
398   ncct->state_stack = stack;
399 }
400
401 int
402 noit_console_shutdown(noit_console_closure_t ncct, int argc, char **argv,
403                       noit_console_state_t *dstate, void *unused) {
404   exit(2);
405 }
406 int
407 noit_console_restart(noit_console_closure_t ncct, int argc, char **argv,
408                      noit_console_state_t *dstate, void *unused) {
409   exit(1);
410 }
411 int
412 noit_console_help(noit_console_closure_t ncct, int argc, char **argv,
413                   noit_console_state_t *dstate, void *unused) {
414   noit_console_state_stack_t *current;
415   current = ncct->state_stack;
416
417   if(!argc) {
418     noit_console_state_stack_t *i = current;
419     if(!current) {
420       nc_printf(ncct, "no state!\n");
421       return -1;
422     }
423     for(i=current;i;i=i->last) {
424       if(i != current)
425         nc_printf(ncct, " -> '%s'\n", i->name ? i->name : "(null)");
426     }
427     if(dstate) {
428       nc_printf(ncct, "= Topics =\n");
429       noit_console_render_help(ncct, dstate);
430     }
431     if(current->state) {
432       nc_printf(ncct, "\n= Commands =\n");
433       noit_console_render_help(ncct, current->state);
434     }
435     return 0;
436   }
437   else if(argc > 0) {
438     nc_printf(ncct, "Help for '%s':\n", argv[0]);
439     if(noit_console_state_delegate(ncct, argc, argv, dstate, NULL) == 0)
440       return 0;
441   }
442   nc_printf(ncct, "command not understood.\n");
443   return -1;
444 }
445 int
446 noit_console_state_pop(noit_console_closure_t ncct, int argc, char **argv,
447                        noit_console_state_t *dstate, void *unused) {
448   noit_console_state_stack_t *current;
449
450   if(argc) {
451     nc_printf(ncct, "no arguments allowed to this command.\n");
452     return -1;
453   }
454   if(!ncct->state_stack || !ncct->state_stack->last) {
455     ncct->wants_shutdown = 1;
456     return 0;
457   }
458
459   current = ncct->state_stack;
460   ncct->state_stack = current->last;
461   current->last = NULL;
462   if(current->state->statefree) current->state->statefree(current->state);
463   if(current->name) free(current->name);
464   free(current);
465   noit_console_state_init(ncct);
466   return 0;
467 }
468
469 int
470 noit_console_state_init(noit_console_closure_t ncct) {
471   if(ncct->el) {
472     console_prompt_func_t f;
473     f = ncct->state_stack->state->console_prompt_function;
474     el_set(ncct->el, EL_PROMPT, f ? f : noit_console_state_prompt);
475   }
476   return 0;
477 }
Note: See TracBrowser for help on using the browser.