root/src/noit_console_state.c

Revision 31d42e564259174c10ca8df5d8b206096a29c957, 18.1 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 8 years ago)

This pulls the timer stuff into the shared base and consolidates a lot
of repeated code across the different scheduler implementations.

times and fdevents are API exposed now and the console exposes them via:

show eventer debug timers
show eventer debug sockets

(the console stuff need to be cleaned up to support autocomplete)

refs #221

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