root/src/noit_console_state.c

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

actually fixes #20

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