root/src/noit_console_state.c

Revision cfdca2d1e21224c8436232bfb3cca7cfd9cb8c5c, 21.0 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 3 weeks ago)

uname success is simply non-negative

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