root/src/noit_console.c

Revision c4546c715aee6db46888e7ab0bb87fd2ce5357c6, 11.2 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 7 years ago)

I sure hope this doesn't break other platforms too badly. Solaris support sans the eventer... untested, of course. refs #32.

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