root/src/noit_console.c

Revision 1fe89bbf2c59cbd0895409389eb9df047e96d692, 9.3 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 6 years ago)

userdata for states, track a path and prompt

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  */
5
6 #include "noit_defines.h"
7
8 #include <stdio.h>
9 #include <unistd.h>
10 #include <errno.h>
11 #include <sys/ioctl.h>
12 #ifdef HAVE_TERMIOS_H
13 #include <termios.h>
14 #endif
15 #ifdef HAVE_UTIL_H
16 #include <util.h>
17 #endif
18 #include <arpa/telnet.h>
19
20 #include "eventer/eventer.h"
21 #include "utils/noit_log.h"
22 #include "noit_listener.h"
23 #include "noit_console.h"
24 #include "noit_tokenizer.h"
25
26 static void
27 nc_telnet_cooker(noit_console_closure_t ncct) {
28   char *tmpbuf, *p, *n;
29   int r;
30
31   tmpbuf = ncct->outbuf;
32   if(ncct->outbuf_len == 0) return;
33
34   p = ncct->outbuf + ncct->outbuf_completed;
35   r = ncct->outbuf_len - ncct->outbuf_completed;
36   n = memchr(p, '\n', r);
37   /* No '\n'? Nothin' to do */
38   if(!n) {
39     ncct->outbuf_cooked = ncct->outbuf_len;
40     return;
41   }
42
43   /* Forget the outbuf -- it is now tmpbuf */
44   ncct->outbuf = NULL;
45   ncct->outbuf_allocd = 0;
46   ncct->outbuf_len = 0;
47   ncct->outbuf_completed = 0;
48   ncct->outbuf_cooked = 0;
49   do {
50     if(n == tmpbuf || *(n-1) != '\r') {
51       nc_write(ncct, p, n-p);   r -= n-p;
52       nc_write(ncct, "\r", 1);
53       p = n;
54     }
55     n = memchr(p+1, '\n', r-1);
56   } while(n);
57   nc_write(ncct, p, r);
58   ncct->outbuf_cooked = ncct->outbuf_len;
59   free(tmpbuf);
60 }
61 int
62 nc_printf(noit_console_closure_t ncct, const char *fmt, ...) {
63   int len;
64   va_list arg;
65   va_start(arg, fmt);
66   len = nc_vprintf(ncct, fmt, arg);
67   va_end(arg);
68   return len;
69 }
70 int
71 nc_vprintf(noit_console_closure_t ncct, const char *fmt, va_list arg) {
72 #ifdef va_copy
73   va_list copy;
74 #endif
75   int lenwanted;
76  
77   if(!ncct->outbuf_allocd) {
78     ncct->outbuf = malloc(4096);
79     if(!ncct->outbuf) return 0;
80     ncct->outbuf_allocd = 4096;
81   }
82   while(1) {
83     char *newbuf;
84 #ifdef va_copy
85     va_copy(copy, arg);
86     lenwanted = vsnprintf(ncct->outbuf + ncct->outbuf_len,
87                           ncct->outbuf_allocd - ncct->outbuf_len,
88                           fmt, copy);
89     va_end(copy);
90 #else
91     lenwanted = vsnprintf(ncct->outbuf + ncct->outbuf_len,
92                           ncct->outbuf_allocd - ncct->outbuf_len,
93                           fmt, arg);
94 #endif
95     if(ncct->outbuf_len + lenwanted < ncct->outbuf_allocd) {
96       /* All went well, things are as we want them. */
97       ncct->outbuf_len += lenwanted;
98       return lenwanted;
99     }
100
101     /* We need to enlarge the buffer */
102     lenwanted += ncct->outbuf_len;
103     lenwanted /= 4096;
104     lenwanted += 1;
105     lenwanted *= 4096;
106     newbuf = realloc(ncct->outbuf, lenwanted);
107     if(!newbuf) {
108       return 0;
109     }
110     ncct->outbuf = newbuf;
111     ncct->outbuf_allocd = lenwanted;
112   }
113   return -1;
114 }
115 int
116 nc_write(noit_console_closure_t ncct, void *buf, int len) {
117   if(!ncct->outbuf_allocd) {
118     ncct->outbuf = malloc(len);
119     if(!ncct->outbuf) return 0;
120     ncct->outbuf_allocd = len;
121   }
122   else if(ncct->outbuf_allocd < ncct->outbuf_len + len) {
123     char *newbuf;
124     newbuf = realloc(ncct->outbuf, ncct->outbuf_len + len);
125     if(!newbuf) return 0;
126     ncct->outbuf = newbuf;
127   }
128   memcpy(ncct->outbuf + ncct->outbuf_len, buf, len);
129   ncct->outbuf_len += len;
130   return len;
131 }
132
133 static void
134 noit_console_userdata_free(void *data) {
135   noit_console_userdata_t *userdata = data;
136   if(userdata) {
137     if(userdata->name) free(userdata->name);
138     if(userdata->freefunc)
139       userdata->freefunc(userdata->data);
140     free(userdata);
141   }
142 }
143 void
144 noit_console_closure_free(noit_console_closure_t ncct) {
145   if(ncct->el) el_end(ncct->el);
146   if(ncct->hist) history_end(ncct->hist);
147   if(ncct->pty_master >= 0) close(ncct->pty_master);
148   if(ncct->pty_slave >= 0) close(ncct->pty_slave);
149   if(ncct->outbuf) free(ncct->outbuf);
150   if(ncct->telnet) noit_console_telnet_free(ncct->telnet);
151   noit_hash_destroy(&ncct->userdata, NULL, noit_console_userdata_free);
152   while(ncct->state_stack) {
153     noit_console_state_stack_t *tmp;
154     tmp = ncct->state_stack;
155     ncct->state_stack = tmp->last;
156     free(tmp);
157   }
158   free(ncct);
159 }
160
161 noit_console_closure_t
162 noit_console_closure_alloc() {
163   noit_console_closure_t new_ncct;
164   new_ncct = calloc(1, sizeof(*new_ncct));
165   noit_hash_init(&new_ncct->userdata);
166   noit_console_state_push_state(new_ncct, noit_console_state_initial());
167   new_ncct->pty_master = -1;
168   new_ncct->pty_slave = -1;
169   return new_ncct;
170 }
171
172 void
173 noit_console_userdata_set(struct __noit_console_closure *ncct,
174                           const char *name, void *data,
175                           state_userdata_free_func_t freefunc) {
176   noit_console_userdata_t *item;
177   item = calloc(1, sizeof(*item));
178   item->name = strdup(name);
179   item->data = data;
180   item->freefunc = freefunc;
181   noit_hash_replace(&ncct->userdata, item->name, strlen(item->name),
182                     item, free, noit_console_userdata_free);
183 }
184  
185 void *
186 noit_console_userdata_get(struct __noit_console_closure *ncct,
187                           const char *name) {
188   noit_console_userdata_t *item;
189   if(noit_hash_retrieve(&ncct->userdata, name, strlen(name),
190                         (void **)&item))
191     return item->data;
192   return NULL;
193 }
194
195
196 int
197 noit_console_continue_sending(noit_console_closure_t ncct,
198                               int *mask) {
199   int len;
200   eventer_t e = ncct->e;
201   if(!ncct->outbuf_len) return 0;
202   if(ncct->output_cooker) ncct->output_cooker(ncct);
203   while(ncct->outbuf_len > ncct->outbuf_completed) {
204     len = e->opset->write(e->fd, ncct->outbuf + ncct->outbuf_completed,
205                           ncct->outbuf_len - ncct->outbuf_completed,
206                           mask, e);
207     if(len < 0) {
208       if(errno == EAGAIN) return -1;
209       /* Do something else here? */
210       return -1;
211     }
212     ncct->outbuf_completed += len;
213   }
214   len = ncct->outbuf_len;
215   free(ncct->outbuf);
216   ncct->outbuf = NULL;
217   ncct->outbuf_allocd = ncct->outbuf_len =
218     ncct->outbuf_completed = ncct->outbuf_cooked = 0;
219   return len;
220 }
221
222 void
223 noit_console_init() {
224   el_multi_init();
225   signal(SIGTTOU, SIG_IGN);
226   eventer_name_callback("noit_console", noit_console_handler);
227 }
228
229 void
230 noit_console_dispatch(eventer_t e, const char *buffer,
231                       noit_console_closure_t ncct) {
232   char **cmds;
233   int i, cnt = 32;
234   cmds = alloca(32 * sizeof(*cmds));
235   i = noit_tokenize(buffer, cmds, &cnt);
236   if(i>cnt) nc_printf(ncct, "Command length too long.\n");
237   else if(i<0) nc_printf(ncct, "Error at offset: %d\n", 0-i);
238   else noit_console_state_do(ncct, cnt, cmds);
239   for(i=0;i<cnt;i++) {
240     nc_printf(ncct, "[%d] '%s'\n", i, cmds[i]);
241     free(cmds[i]);
242   }
243 }
244
245 int
246 noit_console_handler(eventer_t e, int mask, void *closure,
247                      struct timeval *now) {
248   int newmask = EVENTER_READ | EVENTER_EXCEPTION;
249   int keep_going;
250   noit_console_closure_t ncct = closure;
251
252   if(mask & EVENTER_EXCEPTION || (ncct && ncct->wants_shutdown)) {
253 socket_error:
254     /* Exceptions cause us to simply snip the connection */
255     eventer_remove_fd(e->fd);
256     e->opset->close(e->fd, &newmask, e);
257     if(ncct) noit_console_closure_free(ncct);
258     return 0;
259   }
260
261   if(!ncct) {
262     int on = 1;
263     ncct = closure = e->closure = noit_console_closure_alloc();
264     ncct->e = e;
265     if(openpty(&ncct->pty_master, &ncct->pty_slave, NULL, NULL, NULL) ||
266        ioctl(ncct->pty_master, FIONBIO, &on)) {
267       nc_printf(ncct, "Failed to open pty: %s\n", strerror(errno));
268       ncct->wants_shutdown = 1;
269     }
270     else {
271       HistEvent ev;
272       ncct->hist = history_init();
273       history(ncct->hist, &ev, H_SETSIZE, 500);
274       ncct->el = el_init("noitd", ncct->pty_master, e->fd, e->fd);
275       el_set(ncct->el, EL_USERDATA, ncct);
276       el_set(ncct->el, EL_EDITOR, "emacs");
277       el_set(ncct->el, EL_HIST, history, ncct->hist);
278       ncct->telnet = noit_console_telnet_alloc(ncct);
279       ncct->output_cooker = nc_telnet_cooker;
280       noit_console_state_init(ncct);
281     }
282   }
283
284   /* If we still have data to send back to the client, this will take
285    * care of that
286    */
287   if(noit_console_continue_sending(ncct, &newmask) < 0) {
288     if(ncct->wants_shutdown || errno != EAGAIN) goto socket_error;
289     return newmask | EVENTER_EXCEPTION;
290   }
291
292   for(keep_going=1 ; keep_going ; ) {
293     int len, plen;
294     char sbuf[4096];
295     const char *buffer;
296
297     keep_going = 0;
298
299     buffer = el_gets(ncct->el, &plen);
300     if(!el_eagain(ncct->el)) {
301       if(!buffer) {
302         buffer = "exit";
303         plen = 4;
304         nc_write(ncct, "\n", 1);
305       }
306       keep_going++;
307     }
308
309     len = e->opset->read(e->fd, sbuf, sizeof(sbuf)-1, &newmask, e);
310     if(len == 0 || (len < 0 && errno != EAGAIN)) {
311       eventer_remove_fd(e->fd);
312       close(e->fd);
313       return 0;
314     }
315     if(len > 0) {
316       keep_going++;
317       sbuf[len] = '\0';
318       if(ncct->telnet) {
319         noit_console_telnet_telrcv(ncct, sbuf, len);
320         ptyflush(ncct);
321       }
322       else {
323         write(ncct->pty_slave, sbuf, len);
324       }
325     }
326     if(buffer) {
327       char *cmd_buffer;
328       HistEvent ev;
329       cmd_buffer = malloc(plen+1);
330       memcpy(cmd_buffer, buffer, plen);
331       /* chomp */
332       cmd_buffer[plen] = '\0';
333       if(cmd_buffer[plen-1] == '\n') cmd_buffer[plen-1] = '\0';
334       noitL(noit_debug, "IN: '%s'\n", cmd_buffer);
335       history(ncct->hist, &ev, H_ENTER, cmd_buffer);
336       noit_console_dispatch(e, cmd_buffer, ncct);
337       free(cmd_buffer);
338     }
339     if(noit_console_continue_sending(ncct, &newmask) == -1) {
340       if(ncct->wants_shutdown || errno != EAGAIN) goto socket_error;
341       return newmask | EVENTER_EXCEPTION;
342     }
343     if(ncct->wants_shutdown) goto socket_error;
344   }
345   return newmask | EVENTER_EXCEPTION;
346 }
347
Note: See TracBrowser for help on using the browser.