root/src/noit_console_state.c

Revision 5491270242210913e348d069064085eb206601bf, 18.7 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 4 years ago)

refs #34

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  *       notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  *       copyright notice, this list of conditions and the following
13  *       disclaimer in the documentation and/or other materials provided
14  *       with the distribution.
15  *     * Neither the name OmniTI Computer Consulting, Inc. nor the names
16  *       of its contributors may be used to endorse or promote products
17  *       derived from this software without specific prior written
18  *       permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "noit_defines.h"
34
35 #include "eventer/eventer.h"
36 #include "utils/noit_log.h"
37 #include "utils/noit_hash.h"
38 #include "noit_listener.h"
39 #include "noit_console.h"
40 #include "noit_tokenizer.h"
41 #include "noit_module.h"
42
43 #include <netinet/in.h>
44 #include <arpa/inet.h>
45 #include <pcre.h>
46
47 int cmd_info_comparek(const void *akv, const void *bv) {
48   char *ak = (char *)akv;
49   cmd_info_t *b = (cmd_info_t *)bv;
50   return strcasecmp(ak, b->name);
51 }
52 int cmd_info_compare(const void *av, const void *bv) {
53   cmd_info_t *a = (cmd_info_t *)av;
54   cmd_info_t *b = (cmd_info_t *)bv;
55   return strcasecmp(a->name, b->name);
56 }
57 static void
58 noit_console_spit_event(eventer_t e, void *c) {
59   struct timeval now, diff;
60   noit_console_closure_t ncct = c;
61   char fdstr[12];
62   char wfn[42];
63   char funcptr[20];
64   const char *cname;
65
66   cname = eventer_name_for_callback(e->callback);
67   snprintf(fdstr, sizeof(fdstr), " fd: %d", e->fd);
68   gettimeofday(&now, NULL);
69   sub_timeval(e->whence, now, &diff);
70   snprintf(wfn, sizeof(wfn), " fires: %lld.%06ds", (long long)diff.tv_sec, (int)diff.tv_usec);
71   snprintf(funcptr, sizeof(funcptr), "%p", e->callback);
72   nc_printf(ncct, "  [%p]%s%s [%c%c%c%c] -> %s(%p)\n",
73             e,
74             e->mask & (EVENTER_READ | EVENTER_WRITE | EVENTER_EXCEPTION) ? fdstr : "",
75             e->mask & (EVENTER_TIMER) ?  wfn : "",
76             e->mask & EVENTER_READ ? 'r' : '-',
77             e->mask & EVENTER_WRITE ? 'w' : '-',
78             e->mask & EVENTER_EXCEPTION ? 'e' : '-',
79             e->mask & EVENTER_TIMER ? 't' : '-',
80             cname ? cname : funcptr, e->closure);
81 }
82 static int
83 noit_console_eventer_timers(noit_console_closure_t ncct, int argc, char **argv,
84                             noit_console_state_t *dstate, void *unused) {
85   if(argc != 0) return -1;
86   eventer_foreach_timedevent(noit_console_spit_event, ncct);
87   return 0;
88 }
89 static int
90 noit_console_eventer_sockets(noit_console_closure_t ncct, int argc, char **argv,
91                              noit_console_state_t *dstate, void *unused) {
92   if(argc != 0) return -1;
93   eventer_foreach_fdevent(noit_console_spit_event, ncct);
94   return 0;
95 }
96
97 cmd_info_t console_command_help = {
98   "help", noit_console_help, noit_console_opt_delegate, NULL, NULL
99 };
100 cmd_info_t console_command_exit = {
101   "exit", noit_console_state_pop, NULL, NULL, NULL
102 };
103 cmd_info_t console_command_shutdown = {
104   "shutdown", noit_console_shutdown, NULL, NULL, NULL
105 };
106 cmd_info_t console_command_restart = {
107   "restart", noit_console_restart, NULL, NULL, NULL
108 };
109 cmd_info_t console_command_eventer_timers = {
110   "timers", noit_console_eventer_timers, NULL, NULL, NULL
111 };
112 cmd_info_t console_command_eventer_sockets = {
113   "sockets", noit_console_eventer_sockets, NULL, NULL, NULL
114 };
115
116 void
117 noit_console_add_help(const char *topic, console_cmd_func_t topic_func,
118                       console_opt_func_t ac) {
119   noit_console_state_t *s = console_command_help.dstate;
120   if(!s) {
121     console_command_help.dstate = s = calloc(1, sizeof(*s));
122     noit_skiplist_init(&s->cmds);
123     noit_skiplist_set_compare(&s->cmds, cmd_info_compare, cmd_info_comparek);
124   }
125   noit_console_state_add_cmd(s, NCSCMD(topic, topic_func, ac, NULL, NULL));
126 }
127
128 static char *default_prompt = NULL;
129
130 void
131 noit_console_set_default_prompt(const char *prompt) {
132   char *tofree = default_prompt;
133   default_prompt = strdup(prompt);
134   if(tofree) free(tofree);
135 }
136 static char *
137 noit_console_state_prompt(EditLine *el) {
138   static char *tl = "noit# ";
139   if(default_prompt) return default_prompt;
140   return tl;
141 }
142
143 static char *
144 apply_replace(const char *src, const char *name, const char *value) {
145   char *result, *cp;
146   const char *nextpat, *searchstart;
147   char pat[256];
148   int maxlen, patlen, vlen, slen;
149   snprintf(pat, sizeof(pat), "{%s}", name);
150   patlen = strlen(pat);
151   vlen = strlen(value);
152   slen = strlen(src);
153   /* Worst case is just a stream of replacements. */
154   maxlen = (slen / patlen) * MAX(vlen,patlen) + (slen % patlen) + 1;
155   cp = result = malloc(maxlen);
156   searchstart = src;
157   while((nextpat = strstr(searchstart, pat)) != NULL) {
158     memcpy(cp, searchstart, nextpat - searchstart); /* pull the prefix */
159     cp += nextpat - searchstart;                    /* advance destination */
160     memcpy(cp, value, vlen);                        /* copy replacement */
161     cp += vlen;                                     /* advance destination */
162     searchstart = nextpat + patlen;                 /* set new searchstart */
163   }
164   /* Pick up the trailer (plus '\0') */
165   memcpy(cp, searchstart, strlen(searchstart)+1);
166   return result;
167 }
168 static pcre *IP_match = NULL;
169 static pcre *numeric_match = NULL;
170 static int
171 expand_range(const char *range, char ***set, int max_count, const char **err) {
172   int count, erroff, ovector[30], rv;
173   char buff[32]; /* submatches */
174   const char *pcre_err;
175   *err = NULL;
176   if(!IP_match) {
177     IP_match = pcre_compile("^(full:)?(\\d+\\.\\d+\\.\\d+\\.\\d+)/(\\d+)$",
178                             0, &pcre_err, &erroff, NULL);
179     if(!IP_match) {
180       *err = "IP match pattern failed to compile!";
181       noitL(noit_error, "pcre_compiled failed offset %d: %s\n", erroff, pcre_err);
182       return -1;
183     }
184   }
185   if(!numeric_match) {
186     numeric_match = pcre_compile("^(\\d+)(?:,(\\d+))?\\.\\.(\\d+)$",
187                                  0, &pcre_err, &erroff, NULL);
188     if(!numeric_match) {
189       *err = "numeric match pattern failed to compile!";
190       noitL(noit_error, "pcre_compiled failed offset %d: %s\n", erroff, pcre_err);
191       return -1;
192     }
193   }
194   rv = pcre_exec(IP_match, NULL, range, strlen(range), 0, 0, ovector, 30);
195   if(rv >= 0) {
196     int mask, full = 0, i;
197     u_int32_t host_addr;
198     struct in_addr addr;
199     /* 0 is the full monty, 1 is "" or "full:", 2 is the IP, 3 is the mask */
200     pcre_copy_substring(range, ovector, rv, 1, buff, sizeof(buff));
201     full = buff[0] ? 1 : 0;
202     pcre_copy_substring(range, ovector, rv, 3, buff, sizeof(buff));
203     mask = atoi(buff);
204     if(mask == 32) full = 1; /* host implies.. the host */
205     if(mask < 0 || mask > 32) {
206       *err = "invalid netmask";
207       return 0;
208     }
209     count = 1 << (32-mask);
210     pcre_copy_substring(range, ovector, rv, 2, buff, sizeof(buff));
211     if(inet_pton(AF_INET, buff, &addr) != 1) {
212       *err = "could not parse IP address";
213       return 0;
214     }
215     host_addr = ntohl(addr.s_addr);
216     host_addr &= ~((u_int32_t)count - 1);
217
218     if(!full) count -= 2; /* No network or broadcast */
219     if(count > max_count || !count) return -count;
220     if(!full) host_addr++; /* Skip the network address */
221
222     *set = malloc(count * sizeof(**set));
223     for(i=0; i<count; i++)  {
224       addr.s_addr = htonl(host_addr + i);
225       inet_ntop(AF_INET, &addr, buff, sizeof(buff));
226       (*set)[i] = strdup(buff);
227     }
228     return count;
229   }
230   rv = pcre_exec(numeric_match, NULL, range, strlen(range), 0, 0, ovector, 30);
231   if(rv >= 0) {
232     int s, n, e, i;
233     pcre_copy_substring(range, ovector, rv, 1, buff, sizeof(buff));
234     s = atoi(buff);
235     pcre_copy_substring(range, ovector, rv, 3, buff, sizeof(buff));
236     e = atoi(buff);
237     pcre_copy_substring(range, ovector, rv, 2, buff, sizeof(buff));
238     if(buff[0]) n = atoi(buff);
239     else n = (s<e) ? s+1 : s-1;
240
241     /* Ensure that s < n < e */
242     if((s<e && s>n) || (s>e && s<n)) {
243       *err = "mixed up sequence";
244       return 0;
245     }
246     i = n - s; /* Our increment */
247     count = (e - s) / i + 1;
248     *set = malloc(count * sizeof(**set));
249     count = 0;
250     for(; (i>0 && s<=e) || (i<0 && s>=e); s += i) {
251       snprintf(buff, sizeof(buff), "%d", s);
252       (*set)[count] = strdup(buff);
253       count++;
254     }
255     return count;
256   }
257   *err = "cannot understand range";
258   return 0;
259 }
260 int
261 noit_console_generic_apply(noit_console_closure_t ncct,
262                            int argc, char **argv,
263                            noit_console_state_t *dstate,
264                            void *closure) {
265   int i, j, count;
266   char *name, *range;
267   char **nargv, **expanded = NULL;
268   const char *err;
269   int problems = 0;
270   if(argc < 3) {
271     nc_printf(ncct, "apply <name> <range> cmd ...\n");
272     return -1;
273   }
274   name = argv[0];
275   range = argv[1];
276   argc -= 2;
277   argv += 2;
278
279   count = expand_range(range, &expanded, 256, &err);
280   if(!count) {
281     nc_printf(ncct, "apply error: '%s' range produced nothing [%s]\n",
282               range, err ? err : "unknown error");
283     return -1;
284   }
285   if(count < 0) {
286     nc_printf(ncct, "apply error: '%s' range would produce %d items.\n",
287               range, count);
288     return -1;
289   }
290   nargv = malloc(argc * sizeof(*nargv));
291   for(i=0; i<count; i++) {
292     for(j=0; j<argc; j++) nargv[j] = apply_replace(argv[j], name, expanded[i]);
293     if(noit_console_state_do(ncct, argc, nargv)) problems = -1;
294     for(j=0; j<argc; j++) free(nargv[j]);
295     free(expanded[i]);
296   }
297   free(nargv);
298   free(expanded);
299   return problems;
300 }
301
302 int
303 noit_console_render_help(noit_console_closure_t ncct,
304                          noit_console_state_t *dstate) {
305   noit_skiplist_node *iter = NULL;
306   if(!dstate) {
307     nc_printf(ncct, "No help available.\n");
308     return -1;
309   }
310   for(iter = noit_skiplist_getlist(&dstate->cmds); iter;
311       noit_skiplist_next(&dstate->cmds,&iter)) {
312     cmd_info_t *cmd = iter->data;
313     if(strcmp(cmd->name, "help")) nc_printf(ncct, "  ==> '%s'\n", cmd->name);
314   }
315   return 0;
316 }
317
318 int
319 noit_console_state_delegate(noit_console_closure_t ncct,
320                             int argc, char **argv,
321                             noit_console_state_t *dstate,
322                             void *closure) {
323   noit_console_state_stack_t tmps = { 0 };
324
325   if(argc == 0) {
326     noit_console_render_help(ncct, dstate);
327     nc_printf(ncct, "incomplete command.\n");
328     return -1;
329   }
330   if(!dstate) {
331     nc_printf(ncct, "internal error: no delegate state\n");
332     return -1;
333   }
334   tmps.state = dstate;
335   return _noit_console_state_do(ncct, &tmps, argc, argv);
336 }
337
338 int
339 _noit_console_state_do(noit_console_closure_t ncct,
340                        noit_console_state_stack_t *stack,
341                        int argc, char **argv) {
342   noit_skiplist_node *next, *amb = NULL;
343   cmd_info_t *cmd;
344
345   if(!argc) {
346     noit_console_render_help(ncct, stack->state);
347     nc_printf(ncct, "arguments expected\n");
348     return -1;
349   }
350   cmd = noit_skiplist_find_neighbors(&stack->state->cmds, argv[0],
351                                      NULL, NULL, &next);
352   if(!cmd) {
353     int ambiguous = 0;
354     if(next) {
355       cmd_info_t *pcmd = NULL;
356       cmd = next->data;
357       amb = next;
358       noit_skiplist_next(&stack->state->cmds, &amb);
359       if(amb) pcmd = amb->data;
360       /* So cmd is the next in line... pcmd is the one after that.
361        * If they both strncasecmp to 0, we're ambiguous,
362        *    neither, then we're not found.
363        *    only cmd, then we've found a partial, unambiguous.
364        */
365       if(strncasecmp(cmd->name, argv[0], strlen(argv[0])) == 0) {
366         if(pcmd && strncasecmp(pcmd->name, argv[0], strlen(argv[0])) == 0) {
367           cmd = NULL;
368           ambiguous = 1;
369         }
370       }
371       else
372         cmd = NULL;
373     }
374     if(!cmd) {
375       if(ambiguous || !strcmp(argv[0], "?")) {
376         char *partial = ambiguous ? argv[0] : "";
377         if(ambiguous) nc_printf(ncct, "Ambiguous command: '%s'\n", argv[0]);
378         amb = ambiguous ? next : noit_skiplist_getlist(&stack->state->cmds);
379         for(; amb; noit_skiplist_next(&stack->state->cmds, &amb)) {
380           cmd = amb->data;
381           if(!strlen(partial) || strncasecmp(cmd->name, partial, strlen(partial)) == 0)
382             nc_printf(ncct, "\t%s\n", cmd->name);
383           else
384             break;
385         }
386       }
387       else
388         nc_printf(ncct, "No such command: '%s'\n", argv[0]);
389       return -1;
390     }
391   }
392   if(ncct->state_stack->name) free(ncct->state_stack->name);
393   ncct->state_stack->name = strdup(cmd->name);
394   return cmd->func(ncct, argc-1, argv+1, cmd->dstate, cmd->closure);
395 }
396 int
397 noit_console_state_do(noit_console_closure_t ncct, int argc, char **argv) {
398   return _noit_console_state_do(ncct, ncct->state_stack, argc, argv);
399 }
400
401 noit_console_state_t *
402 noit_console_state_alloc(void) {
403   noit_console_state_t *s;
404   s = calloc(1, sizeof(*s));
405   noit_skiplist_init(&s->cmds);
406   noit_skiplist_set_compare(&s->cmds, cmd_info_compare, cmd_info_comparek);
407   noit_console_state_add_cmd(s,
408       NCSCMD("apply", noit_console_generic_apply, NULL, NULL, NULL));
409   noit_console_state_add_cmd(s, &console_command_help);
410   return s;
411 }
412
413 int
414 noit_console_state_add_cmd(noit_console_state_t *state,
415                            cmd_info_t *cmd) {
416   return (noit_skiplist_insert(&state->cmds, cmd) != NULL);
417 }
418
419 cmd_info_t *
420 noit_console_state_get_cmd(noit_console_state_t *state,
421                            const char *name) {
422   cmd_info_t *cmd;
423   cmd = noit_skiplist_find(&state->cmds, name, NULL);
424   return cmd;
425 }
426
427 noit_console_state_t *
428 noit_console_state_build(console_prompt_func_t promptf, cmd_info_t **clist,
429                          state_free_func_t sfreef) {
430   noit_console_state_t *state;
431   state = noit_console_state_alloc();
432   state->console_prompt_function = promptf;
433   while(*clist) {
434     noit_skiplist_insert(&state->cmds, *clist);
435     clist++;
436   }
437   state->statefree = sfreef;
438   return state;
439 }
440
441 cmd_info_t *NCSCMD(const char *name, console_cmd_func_t func,
442                    console_opt_func_t ac,
443                    noit_console_state_t *dstate, void *closure) {
444   cmd_info_t *cmd;
445   cmd = calloc(1, sizeof(*cmd));
446   cmd->name = strdup(name);
447   cmd->func = func;
448   cmd->autocomplete = ac;
449   cmd->dstate = dstate;
450   cmd->closure = closure;
451   return cmd;
452 }
453
454 noit_console_state_t *
455 noit_console_mksubdelegate(noit_console_state_t *parent, const char *cmd) {
456   noit_console_state_t *child;
457   cmd_info_t *existing;
458   existing = noit_console_state_get_cmd(parent, cmd);
459   if(existing) return existing->dstate;
460   child = noit_console_state_alloc();
461   noit_console_state_add_cmd(parent,
462                               NCSCMD(cmd, noit_console_state_delegate,
463                                      noit_console_opt_delegate, child, NULL));
464   return child;
465 }
466
467 noit_console_state_t *
468 noit_console_state_initial() {
469   static noit_console_state_t *_top_level_state = NULL;
470   if(!_top_level_state) {
471     static noit_console_state_t *no_state, *show_state, *evdeb;
472     _top_level_state = noit_console_state_alloc();
473     noit_console_state_add_cmd(_top_level_state, &console_command_exit);
474     show_state = noit_console_mksubdelegate(_top_level_state, "show");
475     no_state = noit_console_mksubdelegate(_top_level_state, "no");
476
477     noit_console_state_add_cmd(_top_level_state, &console_command_shutdown);
478     noit_console_state_add_cmd(_top_level_state, &console_command_restart);
479
480     evdeb = noit_console_mksubdelegate(
481               noit_console_mksubdelegate(show_state,
482                                          "eventer"),
483                                        "debug");
484     noit_console_state_add_cmd(evdeb, &console_command_eventer_timers);
485     noit_console_state_add_cmd(evdeb, &console_command_eventer_sockets);
486   }
487   return _top_level_state;
488 }
489
490 void
491 noit_console_state_push_state(noit_console_closure_t ncct,
492                               noit_console_state_t *state) {
493   noit_console_state_stack_t *stack;
494   stack = calloc(1, sizeof(*stack));
495   stack->last = ncct->state_stack;
496   stack->state = state;
497   ncct->state_stack = stack;
498 }
499
500 int
501 noit_console_shutdown(noit_console_closure_t ncct, int argc, char **argv,
502                       noit_console_state_t *dstate, void *unused) {
503   exit(2);
504 }
505 int
506 noit_console_restart(noit_console_closure_t ncct, int argc, char **argv,
507                      noit_console_state_t *dstate, void *unused) {
508   exit(1);
509 }
510 int
511 noit_console_help(noit_console_closure_t ncct, int argc, char **argv,
512                   noit_console_state_t *dstate, void *unused) {
513   noit_console_state_stack_t *current;
514   current = ncct->state_stack;
515
516   if(!argc) {
517     noit_console_state_stack_t *i;
518     if(!current) {
519       nc_printf(ncct, "no state!\n");
520       return -1;
521     }
522     for(i=current;i;i=i->last) {
523       if(i != current)
524         nc_printf(ncct, " -> '%s'\n", i->name ? i->name : "(null)");
525     }
526     if(dstate) {
527       nc_printf(ncct, "= Topics =\n");
528       noit_console_render_help(ncct, dstate);
529     }
530     if(current->state) {
531       nc_printf(ncct, "\n= Commands =\n");
532       noit_console_render_help(ncct, current->state);
533     }
534     return 0;
535   }
536   else if(argc > 0) {
537     nc_printf(ncct, "Help for '%s':\n", argv[0]);
538     if(noit_console_state_delegate(ncct, argc, argv, dstate, NULL) == 0)
539       return 0;
540   }
541   nc_printf(ncct, "command not understood.\n");
542   return -1;
543 }
544 int
545 noit_console_state_pop(noit_console_closure_t ncct, int argc, char **argv,
546                        noit_console_state_t *dstate, void *unused) {
547   noit_console_state_stack_t *current;
548
549   if(argc) {
550     nc_printf(ncct, "no arguments allowed to this command.\n");
551     return -1;
552   }
553   if(!ncct->state_stack || !ncct->state_stack->last) {
554     ncct->wants_shutdown = 1;
555     return 0;
556   }
557
558   current = ncct->state_stack;
559   ncct->state_stack = current->last;
560   current->last = NULL;
561   if(current->state->statefree) current->state->statefree(current->state);
562   if(current->name) free(current->name);
563   free(current);
564   noit_console_state_init(ncct);
565   return 0;
566 }
567
568 int
569 noit_console_state_init(noit_console_closure_t ncct) {
570   if(ncct->el) {
571     console_prompt_func_t f;
572     f = ncct->state_stack->state->console_prompt_function;
573     el_set(ncct->el, EL_PROMPT, f ? f : noit_console_state_prompt);
574   }
575   return 0;
576 }
Note: See TracBrowser for help on using the browser.