root/src/noit_console_state.c

Revision 410716d9cc66847c0f708a31e3c4a72561e17e3b, 20.3 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 3 years ago)

if no arg is specified, show all queues

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