root/src/noit_console_state.c

Revision 56c7e9dd00981d9018a28e7adf1fb6144821c51e, 14.6 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 5 years ago)

refs #37... lots of progress here

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