root/src/noit_console.c

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

compiles on linux -- still no eventer, refs #12

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