root/src/noit_console_state.c

Revision 0da4c1a673ca6585bae7e7403509d1fb4ce2ac23, 23.7 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 8 months ago)

support a crash command (for debugging) -- no more harmful than the restart

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