root/src/noit_console_state.c

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

oh my word... basic function, refs #49

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