root/src/noit_console_state.c

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

addresses a lot of warnings. gcc still complains about stuff in xpath stuff, doesn't look like our code though, refs #34

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