root/src/noit_console_state.c

Revision 9c4cb1291e2aae54211e19c626e1575d0658a153, 23.6 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 9 months ago)

Merge branch 'master' into MTEVerest

  • 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 #include "noit_version.h"
35 #include "eventer/eventer.h"
36 #include "eventer/eventer_jobq.h"
37 #include "utils/noit_log.h"
38 #include "utils/noit_hash.h"
39 #include "noit_listener.h"
40 #include "noit_console.h"
41 #include "noit_tokenizer.h"
42 #include "noit_module.h"
43
44 #include <netinet/in.h>
45 #include <arpa/inet.h>
46 #include <pcre.h>
47 #include <errno.h>
48 #include <sys/utsname.h>
49 #include <assert.h>
50
51 int cmd_info_comparek(const void *akv, const void *bv) {
52   char *ak = (char *)akv;
53   cmd_info_t *b = (cmd_info_t *)bv;
54   return strcasecmp(ak, b->name);
55 }
56 int cmd_info_compare(const void *av, const void *bv) {
57   cmd_info_t *a = (cmd_info_t *)av;
58   cmd_info_t *b = (cmd_info_t *)bv;
59   return strcasecmp(a->name, b->name);
60 }
61 static void
62 noit_console_spit_event(eventer_t e, void *c) {
63   struct timeval now, diff;
64   noit_console_closure_t ncct = c;
65   char fdstr[12];
66   char wfn[42];
67   char funcptr[20];
68   const char *cname;
69
70   cname = eventer_name_for_callback_e(e->callback, e);
71   snprintf(fdstr, sizeof(fdstr), " fd: %d", e->fd);
72   gettimeofday(&now, NULL);
73   sub_timeval(e->whence, now, &diff);
74   snprintf(wfn, sizeof(wfn), " fires: %lld.%06ds", (long long)diff.tv_sec, (int)diff.tv_usec);
75   snprintf(funcptr, sizeof(funcptr), "%p", e->callback);
76   nc_printf(ncct, "  [%p]%s%s t@%x [%c%c%c%c] -> %s(%p)\n",
77             e,
78             e->mask & (EVENTER_READ | EVENTER_WRITE | EVENTER_EXCEPTION) ? fdstr : "",
79             e->mask & (EVENTER_TIMER) ?  wfn : "",
80             e->thr_owner,
81             e->mask & EVENTER_READ ? 'r' : '-',
82             e->mask & EVENTER_WRITE ? 'w' : '-',
83             e->mask & EVENTER_EXCEPTION ? 'e' : '-',
84             e->mask & EVENTER_TIMER ? 't' : '-',
85             cname ? cname : funcptr, e->closure);
86 }
87 static void
88 noit_console_spit_jobq(eventer_jobq_t *jobq, void *c) {
89   noit_console_closure_t ncct = c;
90   int qlen = 0;
91   nc_printf(ncct, "=== %s ===\n", jobq->queue_name);
92   nc_printf(ncct, " concurrency: %d/%d\n", jobq->concurrency, jobq->desired_concurrency);
93   sem_getvalue(&jobq->semaphore, &qlen);
94   nc_printf(ncct, " total jobs: %lld\n", (long long int)jobq->total_jobs);
95   nc_printf(ncct, " backlog: %d\n", jobq->backlog);
96   nc_printf(ncct, " inflight: %d\n", jobq->inflight);
97   nc_printf(ncct, " timeouts: %lld\n", (long long int)jobq->timeouts);
98   nc_printf(ncct, " avg_wait_ms: %f\n", (double)jobq->avg_wait_ns/1000000.0);
99   nc_printf(ncct, " avg_run_ms: %f\n", (double)jobq->avg_run_ns/1000000.0);
100 }
101 static int
102 noit_console_eventer_timers(noit_console_closure_t ncct, int argc, char **argv,
103                             noit_console_state_t *dstate, void *unused) {
104   if(argc != 0) return -1;
105   eventer_foreach_timedevent(noit_console_spit_event, ncct);
106   return 0;
107 }
108 static int
109 noit_console_eventer_sockets(noit_console_closure_t ncct, int argc, char **argv,
110                              noit_console_state_t *dstate, void *unused) {
111   if(argc != 0) return -1;
112   eventer_foreach_fdevent(noit_console_spit_event, ncct);
113   return 0;
114 }
115 static int
116 noit_console_eventer_jobq(noit_console_closure_t ncct, int argc, char **argv,
117                              noit_console_state_t *dstate, void *unused) {
118   eventer_jobq_t *jobq;
119   if(argc != 1) {
120     eventer_jobq_process_each(noit_console_spit_jobq, (void *)ncct);
121     return 0;
122   }
123   jobq = eventer_jobq_retrieve(argv[0]);
124   if(!jobq) {
125     nc_printf(ncct, "no jobq found for '%s'\n", argv[0] ? argv[0] : "");
126     return 0;
127   }
128   noit_console_spit_jobq(jobq, ncct);
129   return 0;
130 }
131
132 cmd_info_t console_command_help = {
133   "help", noit_console_help, noit_console_opt_delegate, NULL, NULL
134 };
135 cmd_info_t console_command_exit = {
136   "exit", noit_console_state_pop, NULL, NULL, NULL
137 };
138 cmd_info_t console_command_crash = {
139   "crash", noit_console_crash, NULL, NULL, NULL
140 };
141 cmd_info_t console_command_shutdown = {
142   "shutdown", noit_console_shutdown, NULL, NULL, NULL
143 };
144 cmd_info_t console_command_restart = {
145   "restart", noit_console_restart, NULL, NULL, NULL
146 };
147 cmd_info_t console_command_eventer_timers = {
148   "timers", noit_console_eventer_timers, NULL, NULL, NULL
149 };
150 cmd_info_t console_command_eventer_sockets = {
151   "sockets", noit_console_eventer_sockets, NULL, NULL, NULL
152 };
153 cmd_info_t console_command_eventer_jobq = {
154   "jobq", noit_console_eventer_jobq, NULL, NULL, NULL
155 };
156
157 static int
158 noit_console_version(noit_console_closure_t ncct, int argc, char **argv,
159                      noit_console_state_t *dstate, void *unused) {
160   char buff[256];
161   struct utsname utsn;
162   nc_printf(ncct,   "build sysname:\t%s\nbuild nodename:\t%s\n"
163                     "build release:\t%s\nbuild version:\t%s\n"
164                     "build machine:\t%s\n",
165             UNAME_S, UNAME_N, UNAME_R, UNAME_V, UNAME_M);
166   if(uname(&utsn) < 0)
167     nc_printf(ncct, "run:\terror; %s\n", strerror(errno));
168   else
169     nc_printf(ncct, "run sysname:\t%s\nrun nodename:\t%s\n"
170                     "run release:\t%s\nrun version:\t%s\n"
171                     "run machine:\t%s\n",
172               utsn.sysname, utsn.nodename, utsn.release, utsn.version, utsn.machine);
173   nc_printf(ncct, "bitwidth:\t%dbit\n", (int)sizeof(void *)*8);
174   noit_build_version(buff, sizeof(buff));
175   nc_printf(ncct, "version:\t%s\n", buff);
176   return 0;
177 }
178 cmd_info_t console_command_version = {
179   "version", noit_console_version, NULL, NULL, NULL
180 };
181
182 static int
183 noit_console_log_a_line(u_int64_t idx, const struct timeval *whence,
184                         const char *line, size_t len, void *cl) {
185   noit_console_closure_t ncct = cl;
186   (void)whence;
187   nc_printf(ncct, "[%llu] %.*s", (unsigned long long)idx, (int)len, line);
188   return 0;
189 }
190 static int
191 noit_console_log_lines(noit_console_closure_t ncct, int argc, char **argv,
192                        noit_console_state_t *dstate, void *unused) {
193   noit_log_stream_t ls;
194   int log_lines = 23;
195   if(argc < 1 || argc > 2) return -1;
196   if(argc == 2) log_lines = atoi(argv[1]);
197   ls = noit_log_stream_find(argv[0]);
198   if(!ls || strcmp(noit_log_stream_get_type(ls),"memory")) {
199     nc_printf(ncct, "No memory log '%s'\n", argv[0]);
200     return 0;
201   }
202   noit_log_memory_lines(ls, log_lines, noit_console_log_a_line, ncct);
203   return 0;
204 }
205 static char *
206 noit_console_memory_log_opts(noit_console_closure_t ncct,
207                              noit_console_state_stack_t *stack,
208                              noit_console_state_t *dstate,
209                              int argc, char **argv, int idx) {
210   noit_log_stream_t *loggers;
211   int cnt, i, offset = 0;
212
213   if(argc == 1) {
214     cnt = noit_log_list(NULL, 0);
215     if(cnt < 0 ) {
216       cnt = 0 - cnt;
217       loggers = alloca(sizeof(*loggers) * cnt);
218       cnt = noit_log_list(loggers, cnt);
219       if(cnt > 0) {
220         for(i=0;i<cnt;i++) {
221           const char *name = noit_log_stream_get_name(loggers[i]);
222           const char *type = noit_log_stream_get_type(loggers[i]);
223
224           if(type && !strcmp(type, "memory")) {
225             if(name && !strncmp(name, argv[0], strlen(argv[0]))) {
226               if(offset == idx) return strdup(name);
227               offset++;
228             }
229           }
230         }
231       }
232     }
233   }
234   return NULL;
235 }
236
237 cmd_info_t console_command_log_lines = {
238   "log", noit_console_log_lines, noit_console_memory_log_opts, NULL, NULL
239 };
240
241 void
242 noit_console_add_help(const char *topic, console_cmd_func_t topic_func,
243                       console_opt_func_t ac) {
244   noit_console_state_t *s = console_command_help.dstate;
245   if(!s) {
246     console_command_help.dstate = s = calloc(1, sizeof(*s));
247     noit_skiplist_init(&s->cmds);
248     noit_skiplist_set_compare(&s->cmds, cmd_info_compare, cmd_info_comparek);
249   }
250   noit_console_state_add_cmd(s, NCSCMD(topic, topic_func, ac, NULL, NULL));
251 }
252
253 static char *default_prompt = NULL;
254
255 void
256 noit_console_set_default_prompt(const char *prompt) {
257   char *tofree = default_prompt;
258   default_prompt = strdup(prompt);
259   if(tofree) free(tofree);
260 }
261 static char *
262 noit_console_state_prompt(EditLine *el) {
263   static char *tl = "noit# ";
264   if(default_prompt) return default_prompt;
265   return tl;
266 }
267
268 static char *
269 apply_replace(const char *src, const char *name, const char *value) {
270   char *result, *cp;
271   const char *nextpat, *searchstart;
272   char pat[256];
273   int maxlen, patlen, vlen, slen;
274   snprintf(pat, sizeof(pat), "{%s}", name);
275   patlen = strlen(pat);
276   vlen = strlen(value);
277   slen = strlen(src);
278   /* Worst case is just a stream of replacements. */
279   maxlen = (slen / patlen) * MAX(vlen,patlen) + (slen % patlen) + 1;
280   cp = result = malloc(maxlen);
281   searchstart = src;
282   while((nextpat = strstr(searchstart, pat)) != NULL) {
283     memcpy(cp, searchstart, nextpat - searchstart); /* pull the prefix */
284     cp += nextpat - searchstart;                    /* advance destination */
285     memcpy(cp, value, vlen);                        /* copy replacement */
286     cp += vlen;                                     /* advance destination */
287     searchstart = nextpat + patlen;                 /* set new searchstart */
288   }
289   /* Pick up the trailer (plus '\0') */
290   memcpy(cp, searchstart, strlen(searchstart)+1);
291   return result;
292 }
293 static pcre *IP_match = NULL;
294 static pcre *numeric_match = NULL;
295 static int
296 expand_range(const char *range, char ***set, int max_count, const char **err) {
297   int count, erroff, ovector[30], rv;
298   char buff[32]; /* submatches */
299   const char *pcre_err;
300   *err = NULL;
301   if(!IP_match) {
302     IP_match = pcre_compile("^(full:)?(\\d+\\.\\d+\\.\\d+\\.\\d+)/(\\d+)$",
303                             0, &pcre_err, &erroff, NULL);
304     if(!IP_match) {
305       *err = "IP match pattern failed to compile!";
306       noitL(noit_error, "pcre_compiled failed offset %d: %s\n", erroff, pcre_err);
307       return -1;
308     }
309   }
310   if(!numeric_match) {
311     numeric_match = pcre_compile("^(\\d+)(?:,(\\d+))?\\.\\.(\\d+)$",
312                                  0, &pcre_err, &erroff, NULL);
313     if(!numeric_match) {
314       *err = "numeric match pattern failed to compile!";
315       noitL(noit_error, "pcre_compiled failed offset %d: %s\n", erroff, pcre_err);
316       return -1;
317     }
318   }
319   rv = pcre_exec(IP_match, NULL, range, strlen(range), 0, 0, ovector, 30);
320   if(rv >= 0) {
321     int mask, full = 0, i;
322     u_int32_t host_addr;
323     struct in_addr addr;
324     /* 0 is the full monty, 1 is "" or "full:", 2 is the IP, 3 is the mask */
325     pcre_copy_substring(range, ovector, rv, 1, buff, sizeof(buff));
326     full = buff[0] ? 1 : 0;
327     pcre_copy_substring(range, ovector, rv, 3, buff, sizeof(buff));
328     mask = atoi(buff);
329     if(mask == 32) full = 1; /* host implies.. the host */
330     if(mask < 0 || mask > 32) {
331       *err = "invalid netmask";
332       return 0;
333     }
334     count = 1 << (32-mask);
335     pcre_copy_substring(range, ovector, rv, 2, buff, sizeof(buff));
336     if(inet_pton(AF_INET, buff, &addr) != 1) {
337       *err = "could not parse IP address";
338       return 0;
339     }
340     host_addr = ntohl(addr.s_addr);
341     host_addr &= ~((u_int32_t)count - 1);
342
343     if(!full) count -= 2; /* No network or broadcast */
344     if(count > max_count || !count) return -count;
345     if(!full) host_addr++; /* Skip the network address */
346
347     *set = malloc(count * sizeof(**set));
348     for(i=0; i<count; i++)  {
349       addr.s_addr = htonl(host_addr + i);
350       inet_ntop(AF_INET, &addr, buff, sizeof(buff));
351       (*set)[i] = strdup(buff);
352     }
353     return count;
354   }
355   rv = pcre_exec(numeric_match, NULL, range, strlen(range), 0, 0, ovector, 30);
356   if(rv >= 0) {
357     int s, n, e, i;
358     pcre_copy_substring(range, ovector, rv, 1, buff, sizeof(buff));
359     s = atoi(buff);
360     pcre_copy_substring(range, ovector, rv, 3, buff, sizeof(buff));
361     e = atoi(buff);
362     pcre_copy_substring(range, ovector, rv, 2, buff, sizeof(buff));
363     if(buff[0]) n = atoi(buff);
364     else n = (s<e) ? s+1 : s-1;
365
366     /* Ensure that s < n < e */
367     if((s<e && s>n) || (s>e && s<n)) {
368       *err = "mixed up sequence";
369       return 0;
370     }
371     i = n - s; /* Our increment */
372     if(i == 0) return 0;
373     count = (e - s) / i + 1;
374     *set = malloc(count * sizeof(**set));
375     count = 0;
376     for(; (i>0 && s<=e) || (i<0 && s>=e); s += i) {
377       snprintf(buff, sizeof(buff), "%d", s);
378       (*set)[count] = strdup(buff);
379       count++;
380     }
381     return count;
382   }
383   *err = "cannot understand range";
384   return 0;
385 }
386 int
387 noit_console_generic_apply(noit_console_closure_t ncct,
388                            int argc, char **argv,
389                            noit_console_state_t *dstate,
390                            void *closure) {
391   int i, j, count;
392   char *name, *range;
393   char **nargv, **expanded = NULL;
394   const char *err;
395   int problems = 0;
396   if(argc < 3) {
397     nc_printf(ncct, "apply <name> <range> cmd ...\n");
398     return -1;
399   }
400   name = argv[0];
401   range = argv[1];
402   argc -= 2;
403   argv += 2;
404
405   count = expand_range(range, &expanded, 256, &err);
406   if(!count) {
407     nc_printf(ncct, "apply error: '%s' range produced nothing [%s]\n",
408               range, err ? err : "unknown error");
409     assert(expanded == NULL);
410     return -1;
411   }
412   if(count < 0) {
413     nc_printf(ncct, "apply error: '%s' range would produce %d items.\n",
414               range, count);
415     return -1;
416   }
417   nargv = malloc(argc * sizeof(*nargv));
418   for(i=0; i<count; i++) {
419     for(j=0; j<argc; j++) nargv[j] = apply_replace(argv[j], name, expanded[i]);
420     if(noit_console_state_do(ncct, argc, nargv)) problems = -1;
421     for(j=0; j<argc; j++) free(nargv[j]);
422     free(expanded[i]);
423   }
424   free(nargv);
425   free(expanded);
426   return problems;
427 }
428
429 int
430 noit_console_render_help(noit_console_closure_t ncct,
431                          noit_console_state_t *dstate) {
432   noit_skiplist_node *iter = NULL;
433   if(!dstate) {
434     nc_printf(ncct, "No help available.\n");
435     return -1;
436   }
437   for(iter = noit_skiplist_getlist(&dstate->cmds); iter;
438       noit_skiplist_next(&dstate->cmds,&iter)) {
439     cmd_info_t *cmd = iter->data;
440     if(strcmp(cmd->name, "help")) nc_printf(ncct, "  ==> '%s'\n", cmd->name);
441   }
442   return 0;
443 }
444
445 int
446 noit_console_state_delegate(noit_console_closure_t ncct,
447                             int argc, char **argv,
448                             noit_console_state_t *dstate,
449                             void *closure) {
450   noit_console_state_stack_t tmps = { 0 };
451
452   if(argc == 0) {
453     noit_console_render_help(ncct, dstate);
454     nc_printf(ncct, "incomplete command.\n");
455     return -1;
456   }
457   if(!dstate) {
458     nc_printf(ncct, "internal error: no delegate state\n");
459     return -1;
460   }
461   tmps.state = dstate;
462   return _noit_console_state_do(ncct, &tmps, argc, argv);
463 }
464
465 int
466 _noit_console_state_do(noit_console_closure_t ncct,
467                        noit_console_state_stack_t *stack,
468                        int argc, char **argv) {
469   noit_skiplist_node *next, *amb = NULL;
470   cmd_info_t *cmd;
471
472   if(!argc) {
473     noit_console_render_help(ncct, stack->state);
474     nc_printf(ncct, "arguments expected\n");
475     return -1;
476   }
477   cmd = noit_skiplist_find_neighbors(&stack->state->cmds, argv[0],
478                                      NULL, NULL, &next);
479   if(!cmd) {
480     int ambiguous = 0;
481     if(next) {
482       cmd_info_t *pcmd = NULL;
483       cmd = next->data;
484       amb = next;
485       noit_skiplist_next(&stack->state->cmds, &amb);
486       if(amb) pcmd = amb->data;
487       /* So cmd is the next in line... pcmd is the one after that.
488        * If they both strncasecmp to 0, we're ambiguous,
489        *    neither, then we're not found.
490        *    only cmd, then we've found a partial, unambiguous.
491        */
492       if(strncasecmp(cmd->name, argv[0], strlen(argv[0])) == 0) {
493         if(pcmd && strncasecmp(pcmd->name, argv[0], strlen(argv[0])) == 0) {
494           cmd = NULL;
495           ambiguous = 1;
496         }
497       }
498       else
499         cmd = NULL;
500     }
501     if(!cmd) {
502       if(ambiguous || !strcmp(argv[0], "?")) {
503         char *partial = ambiguous ? argv[0] : "";
504         if(ambiguous) nc_printf(ncct, "Ambiguous command: '%s'\n", argv[0]);
505         amb = ambiguous ? next : noit_skiplist_getlist(&stack->state->cmds);
506         for(; amb; noit_skiplist_next(&stack->state->cmds, &amb)) {
507           cmd = amb->data;
508           if(!strlen(partial) || strncasecmp(cmd->name, partial, strlen(partial)) == 0)
509             nc_printf(ncct, "\t%s\n", cmd->name);
510           else
511             break;
512         }
513       }
514       else
515         nc_printf(ncct, "No such command: '%s'\n", argv[0]);
516       return -1;
517     }
518   }
519   if(ncct->state_stack->name) free(ncct->state_stack->name);
520   ncct->state_stack->name = strdup(cmd->name);
521   return cmd->func(ncct, argc-1, argv+1, cmd->dstate, cmd->closure);
522 }
523 int
524 noit_console_state_do(noit_console_closure_t ncct, int argc, char **argv) {
525   return _noit_console_state_do(ncct, ncct->state_stack, argc, argv);
526 }
527
528 noit_console_state_t *
529 noit_console_state_alloc(void) {
530   noit_console_state_t *s;
531   s = calloc(1, sizeof(*s));
532   noit_skiplist_init(&s->cmds);
533   noit_skiplist_set_compare(&s->cmds, cmd_info_compare, cmd_info_comparek);
534   noit_console_state_add_cmd(s,
535       NCSCMD("apply", noit_console_generic_apply, NULL, NULL, NULL));
536   noit_console_state_add_cmd(s, &console_command_help);
537   return s;
538 }
539
540 int
541 noit_console_state_add_cmd(noit_console_state_t *state,
542                            cmd_info_t *cmd) {
543   return (noit_skiplist_insert(&state->cmds, cmd) != NULL);
544 }
545
546 cmd_info_t *
547 noit_console_state_get_cmd(noit_console_state_t *state,
548                            const char *name) {
549   cmd_info_t *cmd;
550   cmd = noit_skiplist_find(&state->cmds, name, NULL);
551   return cmd;
552 }
553
554 noit_console_state_t *
555 noit_console_state_build(console_prompt_func_t promptf, cmd_info_t **clist,
556                          state_free_func_t sfreef) {
557   noit_console_state_t *state;
558   state = noit_console_state_alloc();
559   state->console_prompt_function = promptf;
560   while(*clist) {
561     noit_skiplist_insert(&state->cmds, *clist);
562     clist++;
563   }
564   state->statefree = sfreef;
565   return state;
566 }
567
568 cmd_info_t *NCSCMD(const char *name, console_cmd_func_t func,
569                    console_opt_func_t ac,
570                    noit_console_state_t *dstate, void *closure) {
571   cmd_info_t *cmd;
572   cmd = calloc(1, sizeof(*cmd));
573   cmd->name = strdup(name);
574   cmd->func = func;
575   cmd->autocomplete = ac;
576   cmd->dstate = dstate;
577   cmd->closure = closure;
578   return cmd;
579 }
580
581 noit_console_state_t *
582 noit_console_mksubdelegate(noit_console_state_t *parent, const char *cmd) {
583   noit_console_state_t *child;
584   cmd_info_t *existing;
585   existing = noit_console_state_get_cmd(parent, cmd);
586   if(existing) return existing->dstate;
587   child = noit_console_state_alloc();
588   noit_console_state_add_cmd(parent,
589                               NCSCMD(cmd, noit_console_state_delegate,
590                                      noit_console_opt_delegate, child, NULL));
591   return child;
592 }
593
594 noit_console_state_t *
595 noit_console_state_initial() {
596   static noit_console_state_t *_top_level_state = NULL;
597   if(!_top_level_state) {
598     static noit_console_state_t *no_state, *show_state, *evdeb;
599     _top_level_state = noit_console_state_alloc();
600     noit_console_state_add_cmd(_top_level_state, &console_command_exit);
601     show_state = noit_console_mksubdelegate(_top_level_state, "show");
602     no_state = noit_console_mksubdelegate(_top_level_state, "no");
603
604     noit_console_state_add_cmd(_top_level_state, &console_command_crash);
605     noit_console_state_add_cmd(_top_level_state, &console_command_shutdown);
606     noit_console_state_add_cmd(_top_level_state, &console_command_restart);
607     noit_console_state_add_cmd(show_state, &console_command_version);
608     noit_console_state_add_cmd(show_state, &console_command_log_lines);
609     (void)no_state;
610
611     evdeb = noit_console_mksubdelegate(
612               noit_console_mksubdelegate(show_state,
613                                          "eventer"),
614                                        "debug");
615     noit_console_state_add_cmd(evdeb, &console_command_eventer_timers);
616     noit_console_state_add_cmd(evdeb, &console_command_eventer_sockets);
617     noit_console_state_add_cmd(evdeb, &console_command_eventer_jobq);
618   }
619   return _top_level_state;
620 }
621
622 void
623 noit_console_state_push_state(noit_console_closure_t ncct,
624                               noit_console_state_t *state) {
625   noit_console_state_stack_t *stack;
626   stack = calloc(1, sizeof(*stack));
627   stack->last = ncct->state_stack;
628   stack->state = state;
629   ncct->state_stack = stack;
630 }
631
632 int
633 noit_console_crash(noit_console_closure_t ncct, int argc, char **argv,
634                    noit_console_state_t *dstate, void *unused) {
635   *((volatile int *)0) = 0;
636   return 0;
637 }
638 int
639 noit_console_shutdown(noit_console_closure_t ncct, int argc, char **argv,
640                       noit_console_state_t *dstate, void *unused) {
641   exit(2);
642 }
643 int
644 noit_console_restart(noit_console_closure_t ncct, int argc, char **argv,
645                      noit_console_state_t *dstate, void *unused) {
646   exit(1);
647 }
648 int
649 noit_console_help(noit_console_closure_t ncct, int argc, char **argv,
650                   noit_console_state_t *dstate, void *unused) {
651   noit_console_state_stack_t *current;
652   current = ncct->state_stack;
653
654   if(!argc) {
655     noit_console_state_stack_t *i;
656     if(!current) {
657       nc_printf(ncct, "no state!\n");
658       return -1;
659     }
660     for(i=current;i;i=i->last) {
661       if(i != current)
662         nc_printf(ncct, " -> '%s'\n", i->name ? i->name : "(null)");
663     }
664     if(dstate) {
665       nc_printf(ncct, "= Topics =\n");
666       noit_console_render_help(ncct, dstate);
667     }
668     if(current->state) {
669       nc_printf(ncct, "\n= Commands =\n");
670       noit_console_render_help(ncct, current->state);
671     }
672     return 0;
673   }
674   else if(argc > 0) {
675     nc_printf(ncct, "Help for '%s':\n", argv[0]);
676     if(noit_console_state_delegate(ncct, argc, argv, dstate, NULL) == 0)
677       return 0;
678   }
679   nc_printf(ncct, "command not understood.\n");
680   return -1;
681 }
682 int
683 noit_console_state_pop(noit_console_closure_t ncct, int argc, char **argv,
684                        noit_console_state_t *dstate, void *unused) {
685   noit_console_state_stack_t *current;
686
687   if(argc) {
688     nc_printf(ncct, "no arguments allowed to this command.\n");
689     return -1;
690   }
691   if(!ncct->state_stack || !ncct->state_stack->last) {
692     ncct->wants_shutdown = 1;
693     return 0;
694   }
695
696   current = ncct->state_stack;
697   ncct->state_stack = current->last;
698   current->last = NULL;
699   if(current->state->statefree) current->state->statefree(current->state);
700   if(current->name) free(current->name);
701   free(current);
702   noit_console_state_init(ncct);
703   return 0;
704 }
705
706 int
707 noit_console_state_init(noit_console_closure_t ncct) {
708   if(ncct->el) {
709     console_prompt_func_t f;
710     f = ncct->state_stack->state->console_prompt_function;
711     el_set(ncct->el, EL_PROMPT, f ? f : noit_console_state_prompt);
712   }
713   return 0;
714 }
Note: See TracBrowser for help on using the browser.