root/src/noit_console.c

Revision dfec9683c6674557f3639bb6485d369ebd6a347b, 16.0 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 2 years ago)

Make sure we compile with clang (trunk) on Ubuntu 11.10

This is a good milestone, so let's do an intermediate commit:
At this point we are able to compile with Clang (trunk, no less;)
on Ubuntu 11.10. I was careful with the introduced changes not to
introduce any regressions:

* bsd/libutil.h is prefered, otherwise we fallback to libutil.h
* replaced isinteger()'s guts with a call to strtod()

  • 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
35 #include <assert.h>
36 #include <stdio.h>
37 #include <unistd.h>
38 #ifdef HAVE_ALLOCA_H
39 #include <alloca.h>
40 #endif
41 #include <errno.h>
42 #ifdef HAVE_SYS_IOCTL_H
43 #include <sys/ioctl.h>
44 #endif
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <fcntl.h>
48 #ifdef HAVE_STROPTS_H
49 #include <stropts.h>
50 #endif
51 #ifdef HAVE_SYS_STREAM_H
52 #include <sys/stream.h>
53 #endif
54 #ifdef HAVE_TERMIOS_H
55 #include <termios.h>
56 #endif
57 #ifdef HAVE_PTY_H
58 #include <pty.h>
59 #endif
60 #ifdef HAVE_UTIL_H
61 #include <util.h>
62 #endif
63 #ifdef HAVE_BSD_LIBUTIL_H
64 #include <bsd/libutil.h>
65 #elif HAVE_LIBUTIL_H
66 #include <libutil.h>
67 #endif
68 #include <arpa/telnet.h>
69 #include <signal.h>
70
71 #include "eventer/eventer.h"
72 #include "utils/noit_log.h"
73 #include "noit_listener.h"
74 #include "noit_console.h"
75 #include "noit_tokenizer.h"
76
77 #include "noitedit/sys.h"
78 #include "noitedit/el.h"
79 #include "noitedit/fcns.h"
80 #include "noitedit/map.h"
81
82 static void
83 nc_telnet_cooker(noit_console_closure_t ncct) {
84   char *tmpbuf, *p, *n;
85   int r;
86
87   tmpbuf = ncct->outbuf;
88   if(ncct->outbuf_len == 0) return;
89
90   p = ncct->outbuf + ncct->outbuf_completed;
91   r = ncct->outbuf_len - ncct->outbuf_completed;
92   n = memchr(p, '\n', r);
93   /* No '\n'? Nothin' to do */
94   if(!n) {
95     ncct->outbuf_cooked = ncct->outbuf_len;
96     return;
97   }
98
99   /* Forget the outbuf -- it is now tmpbuf */
100   ncct->outbuf = NULL;
101   ncct->outbuf_allocd = 0;
102   ncct->outbuf_len = 0;
103   ncct->outbuf_completed = 0;
104   ncct->outbuf_cooked = 0;
105   do {
106     nc_write(ncct, p, n-p);   r -= n-p;
107     if(n == tmpbuf || *(n-1) != '\r')
108       nc_write(ncct, "\r", 1);
109     p = n;
110     n = memchr(p+1, '\n', r-1);
111   } while(n);
112   nc_write(ncct, p, r);
113   ncct->outbuf_cooked = ncct->outbuf_len;
114   free(tmpbuf);
115 }
116 int
117 nc_printf(noit_console_closure_t ncct, const char *fmt, ...) {
118   int len;
119   va_list arg;
120   va_start(arg, fmt);
121   len = nc_vprintf(ncct, fmt, arg);
122   va_end(arg);
123   return len;
124 }
125 int
126 nc_vprintf(noit_console_closure_t ncct, const char *fmt, va_list arg) {
127 #ifdef va_copy
128   va_list copy;
129 #endif
130   int lenwanted;
131  
132   if(!ncct->outbuf_allocd) {
133     ncct->outbuf = malloc(4096);
134     if(!ncct->outbuf) return 0;
135     ncct->outbuf_allocd = 4096;
136   }
137   while(1) {
138     char *newbuf;
139 #ifdef va_copy
140     va_copy(copy, arg);
141     lenwanted = vsnprintf(ncct->outbuf + ncct->outbuf_len,
142                           ncct->outbuf_allocd - ncct->outbuf_len,
143                           fmt, copy);
144     va_end(copy);
145 #else
146     lenwanted = vsnprintf(ncct->outbuf + ncct->outbuf_len,
147                           ncct->outbuf_allocd - ncct->outbuf_len,
148                           fmt, arg);
149 #endif
150     if(ncct->outbuf_len + lenwanted < ncct->outbuf_allocd) {
151       /* All went well, things are as we want them. */
152       ncct->outbuf_len += lenwanted;
153       return lenwanted;
154     }
155
156     /* We need to enlarge the buffer */
157     lenwanted += ncct->outbuf_len;
158     lenwanted /= 4096;
159     lenwanted += 1;
160     lenwanted *= 4096;
161     newbuf = realloc(ncct->outbuf, lenwanted);
162     if(!newbuf) {
163       return 0;
164     }
165     ncct->outbuf = newbuf;
166     ncct->outbuf_allocd = lenwanted;
167   }
168   /* NOTREACHED */
169 }
170 int
171 nc_write(noit_console_closure_t ncct, const void *buf, int len) {
172   if(!ncct->outbuf_allocd) {
173     ncct->outbuf = malloc(len);
174     if(!ncct->outbuf) return 0;
175     ncct->outbuf_allocd = len;
176   }
177   else if(ncct->outbuf_allocd < ncct->outbuf_len + len) {
178     char *newbuf;
179     newbuf = realloc(ncct->outbuf, ncct->outbuf_len + len);
180     if(!newbuf) return 0;
181     ncct->outbuf = newbuf;
182   }
183   memcpy(ncct->outbuf + ncct->outbuf_len, buf, len);
184   ncct->outbuf_len += len;
185   return len;
186 }
187
188 static void
189 noit_console_userdata_free(void *data) {
190   noit_console_userdata_t *userdata = data;
191   if(userdata) {
192     if(userdata->name) free(userdata->name);
193     if(userdata->freefunc)
194       userdata->freefunc(userdata->data);
195     free(userdata);
196   }
197 }
198 void
199 noit_console_closure_free(noit_console_closure_t ncct) {
200   noit_log_stream_t lf;
201   if(ncct->el) el_end(ncct->el);
202   if(ncct->hist) history_end(ncct->hist);
203   if(ncct->pty_master >= 0) close(ncct->pty_master);
204   if(ncct->pty_slave >= 0) close(ncct->pty_slave);
205   if(ncct->outbuf) free(ncct->outbuf);
206   if(ncct->telnet) noit_console_telnet_free(ncct->telnet);
207   noit_hash_destroy(&ncct->userdata, NULL, noit_console_userdata_free);
208   while(ncct->state_stack) {
209     noit_console_state_stack_t *tmp;
210     tmp = ncct->state_stack;
211     ncct->state_stack = tmp->last;
212     free(tmp);
213   }
214   lf = noit_log_stream_find(ncct->feed_path);
215   noit_log_stream_remove(ncct->feed_path);
216   if(lf) {
217     noit_log_stream_free(lf);
218   }
219   free(ncct);
220 }
221
222 noit_console_closure_t
223 noit_console_closure_alloc() {
224   noit_console_closure_t new_ncct;
225   new_ncct = calloc(1, sizeof(*new_ncct));
226   noit_hash_init(&new_ncct->userdata);
227   noit_console_state_push_state(new_ncct, noit_console_state_initial());
228   new_ncct->pty_master = -1;
229   new_ncct->pty_slave = -1;
230   return new_ncct;
231 }
232
233 void
234 noit_console_userdata_set(struct __noit_console_closure *ncct,
235                           const char *name, void *data,
236                           state_userdata_free_func_t freefunc) {
237   noit_console_userdata_t *item;
238   item = calloc(1, sizeof(*item));
239   item->name = strdup(name);
240   item->data = data;
241   item->freefunc = freefunc;
242   noit_hash_replace(&ncct->userdata, item->name, strlen(item->name),
243                     item, NULL, noit_console_userdata_free);
244 }
245  
246 void *
247 noit_console_userdata_get(struct __noit_console_closure *ncct,
248                           const char *name) {
249   void *vitem;
250   if(noit_hash_retrieve(&ncct->userdata, name, strlen(name),
251                         &vitem))
252     return ((noit_console_userdata_t *)vitem)->data;
253   return NULL;
254 }
255
256
257 int
258 noit_console_continue_sending(noit_console_closure_t ncct,
259                               int *mask) {
260   int len;
261   eventer_t e = ncct->e;
262   if(!ncct->outbuf_len) return 0;
263   if(ncct->output_cooker) ncct->output_cooker(ncct);
264   while(ncct->outbuf_len > ncct->outbuf_completed) {
265     len = e->opset->write(e->fd, ncct->outbuf + ncct->outbuf_completed,
266                           ncct->outbuf_len - ncct->outbuf_completed,
267                           mask, e);
268     if(len < 0) {
269       if(errno == EAGAIN) return -1;
270       /* Do something else here? */
271       return -1;
272     }
273     ncct->outbuf_completed += len;
274   }
275   len = ncct->outbuf_len;
276   free(ncct->outbuf);
277   ncct->outbuf = NULL;
278   ncct->outbuf_allocd = ncct->outbuf_len =
279     ncct->outbuf_completed = ncct->outbuf_cooked = 0;
280   return len;
281 }
282
283 void
284 noit_console_dispatch(eventer_t e, const char *buffer,
285                       noit_console_closure_t ncct) {
286   char **cmds;
287   HistEvent ev;
288   int i, cnt = 32;
289
290   cmds = alloca(32 * sizeof(*cmds));
291   i = noit_tokenize(buffer, cmds, &cnt);
292
293   /* < 0 is an error, that's fine.  We want it in the history to "fix" */
294   /* > 0 means we had arguments, so let's put it in the history */
295   /* 0 means nothing -- and that isn't worthy of history inclusion */
296   if(i) history(ncct->hist, &ev, H_ENTER, buffer);
297
298   if(i>cnt) nc_printf(ncct, "Command length too long.\n");
299   else if(i<0) nc_printf(ncct, "Error at offset: %d\n", 0-i);
300   else noit_console_state_do(ncct, cnt, cmds);
301 }
302
303 void
304 noit_console_motd(eventer_t e, acceptor_closure_t *ac,
305                   noit_console_closure_t ncct) {
306   int ssl;
307   ssl = eventer_get_eventer_ssl_ctx(e) ? 1 : 0;
308   nc_printf(ncct, "noitd%s: %s\n",
309             ssl ? "(secure)" : "",
310             ac->remote_cn ? ac->remote_cn : "(no auth)");
311 }
312
313 int
314 allocate_pty(int *master, int *slave) {
315 #if defined(HAVE_OPENPTY) || (defined(HAVE_DECL_OPENPTY) && HAVE_DECL_OPENPTY != 0)
316   if(openpty(master, slave, NULL, NULL, NULL)) return -1;
317 #else
318   /* STREAMS... sigh */
319   char   *slavename;
320   extern char *ptsname();
321
322   *master = open("/dev/ptmx", O_RDWR);  /* open master */
323   if(*master < 0) return -1;
324   grantpt(*master);                     /* change permission of   slave */
325   unlockpt(*master);                    /* unlock slave */
326   slavename = ptsname(*master);         /* get name of slave */
327   *slave = open(slavename, O_RDWR);    /* open slave */
328   if(*slave < 0) {
329     close(*master);
330     *master = -1;
331     return -1;
332   }
333   /* This is a bit backwards as we using the PTY backwards.
334    * We want to make the master a tty instead of the slave... odd, I know.
335    */
336   ioctl(*master, I_PUSH, "ptem");       /* push ptem */
337   ioctl(*master, I_PUSH, "ldterm");     /* push ldterm*/
338 #endif
339   if(eventer_set_fd_nonblocking(*master)) return -1;
340   noitL(noit_debug, "allocate_pty -> %d,%d\n", *master, *slave);
341   return 0;
342 }
343
344 int
345 noit_console_handler(eventer_t e, int mask, void *closure,
346                      struct timeval *now) {
347   int newmask = EVENTER_READ | EVENTER_EXCEPTION;
348   int keep_going;
349   acceptor_closure_t *ac = closure;
350   noit_console_closure_t ncct = ac->service_ctx;
351
352   if(mask & EVENTER_EXCEPTION || (ncct && ncct->wants_shutdown)) {
353 socket_error:
354     /* Exceptions cause us to simply snip the connection */
355
356     /* This removes the log feed which is important to do before calling close */
357     eventer_remove_fd(e->fd);
358     if(ncct) noit_console_closure_free(ncct);
359     if(ac) acceptor_closure_free(ac);
360     e->opset->close(e->fd, &newmask, e);
361     return 0;
362   }
363
364   if(!ac->service_ctx) {
365     ncct = ac->service_ctx = noit_console_closure_alloc();
366   }
367   if(!ncct->initialized) {
368     ncct->e = e;
369     if(allocate_pty(&ncct->pty_master, &ncct->pty_slave)) {
370       nc_printf(ncct, "Failed to open pty: %s\n", strerror(errno));
371       ncct->wants_shutdown = 1;
372       goto socket_error;
373     }
374     else {
375       int i;
376       const char *line_protocol;
377       HistEvent ev;
378
379       ncct->hist = history_init();
380       history(ncct->hist, &ev, H_SETSIZE, 500);
381       ncct->el = el_init("noitd", ncct->pty_master, NULL,
382                          e->fd, e, e->fd, e);
383       if(!ncct->el) goto socket_error;
384       if(el_set(ncct->el, EL_USERDATA, ncct)) {
385         noitL(noit_error, "Cannot set userdata on noitedit session\n");
386         goto socket_error;
387       }
388       if(el_set(ncct->el, EL_EDITOR, "emacs"))
389         noitL(noit_error, "Cannot set emacs mode on console\n");
390       if(el_set(ncct->el, EL_HIST, history, ncct->hist))
391         noitL(noit_error, "Cannot set history on console\n");
392       el_set(ncct->el, EL_ADDFN, "noit_complete",
393              "auto completion functions for noit", noit_edit_complete);
394       el_set(ncct->el, EL_BIND, "^I", "noit_complete", NULL);
395       for(i=EL_NUM_FCNS; i < ncct->el->el_map.nfunc; i++) {
396         if(ncct->el->el_map.func[i] == noit_edit_complete) {
397           ncct->noit_edit_complete_cmdnum = i;
398           break;
399         }
400       }
401
402       if(!noit_hash_retr_str(ac->config,
403                              "line_protocol", strlen("line_protocol"),
404                              &line_protocol)) {
405         line_protocol = NULL;
406       }
407       if(line_protocol && !strcasecmp(line_protocol, "telnet")) {
408         ncct->telnet = noit_console_telnet_alloc(ncct);
409         ncct->output_cooker = nc_telnet_cooker;
410       }
411       noit_console_state_init(ncct);
412     }
413     snprintf(ncct->feed_path, sizeof(ncct->feed_path), "console/%d", e->fd);
414     noit_log_stream_new(ncct->feed_path, "noit_console", ncct->feed_path,
415                         ncct, NULL);
416     noit_console_motd(e, ac, ncct);
417     ncct->initialized = 1;
418   }
419
420   /* If we still have data to send back to the client, this will take
421    * care of that
422    */
423   if(noit_console_continue_sending(ncct, &newmask) < 0) {
424     if(ncct->wants_shutdown || errno != EAGAIN) goto socket_error;
425     return newmask | EVENTER_EXCEPTION;
426   }
427
428   for(keep_going=1 ; keep_going ; ) {
429     int len, plen;
430     char sbuf[4096];
431     const char *buffer;
432
433     keep_going = 0;
434
435     buffer = el_gets(ncct->el, &plen);
436     if(!el_eagain(ncct->el)) {
437       if(!buffer) {
438         buffer = "exit";
439         plen = 4;
440         nc_write(ncct, "\n", 1);
441       }
442       keep_going++;
443     }
444
445     len = e->opset->read(e->fd, sbuf, sizeof(sbuf)-1, &newmask, e);
446     if(len == 0 || (len < 0 && errno != EAGAIN)) {
447       eventer_remove_fd(e->fd);
448       if(ncct) noit_console_closure_free(ncct);
449       if(ac) acceptor_closure_free(ac);
450       e->opset->close(e->fd, &newmask, e);
451       return 0;
452     }
453     if(len > 0) {
454       keep_going++;
455       sbuf[len] = '\0';
456       if(ncct->telnet) {
457         noit_console_telnet_telrcv(ncct, sbuf, len);
458         ptyflush(ncct);
459       }
460       else {
461         int written;
462         written = write(ncct->pty_slave, sbuf, len);
463         if(written <= 0) goto socket_error;
464         assert(written == len);
465       }
466     }
467     if(buffer) {
468       char *cmd_buffer;
469       cmd_buffer = malloc(plen+1);
470       memcpy(cmd_buffer, buffer, plen);
471       /* chomp */
472       cmd_buffer[plen] = '\0';
473       if(cmd_buffer[plen-1] == '\n') cmd_buffer[plen-1] = '\0';
474       noitL(noit_debug, "IN[%d]: '%s'\n", plen, cmd_buffer);
475       noit_console_dispatch(e, cmd_buffer, ncct);
476       free(cmd_buffer);
477     }
478     if(noit_console_continue_sending(ncct, &newmask) == -1) {
479       if(ncct->wants_shutdown || errno != EAGAIN) goto socket_error;
480       return newmask | EVENTER_EXCEPTION;
481     }
482     if(ncct->wants_shutdown) goto socket_error;
483   }
484   return newmask | EVENTER_EXCEPTION;
485 }
486
487 static int
488 noit_console_logio_open(noit_log_stream_t ls) {
489   return 0;
490 }
491 static int
492 noit_console_logio_reopen(noit_log_stream_t ls) {
493   /* no op */
494   return 0;
495 }
496 static int
497 noit_console_logio_write(noit_log_stream_t ls, const void *buf, size_t len) {
498   noit_console_closure_t ncct;
499   ncct = noit_log_stream_get_ctx(ls);
500   int rv, rlen, mask;
501   if(!ncct) return 0;
502   rlen = nc_write(ncct, buf, len);
503   while((rv = noit_console_continue_sending(ncct, &mask)) == -1 && errno == EINTR);
504   if(rv == -1 && errno == EAGAIN) {
505     eventer_update(ncct->e, mask | EVENTER_EXCEPTION);
506   }
507   return rlen;
508 }
509 static int
510 noit_console_logio_close(noit_log_stream_t ls) {
511   noit_console_closure_t ncct;
512   ncct = noit_log_stream_get_ctx(ls);
513   if(!ncct) return 0;
514   ncct->e = NULL;
515   noit_log_stream_set_ctx(ls, NULL);
516   return 0;
517 }
518 static logops_t noit_console_logio_ops = {
519   noit_console_logio_open,
520   noit_console_logio_reopen,
521   noit_console_logio_write,
522   NULL,
523   noit_console_logio_close,
524   NULL,
525   NULL
526 };
527
528 int
529 noit_console_write_xml(void *vncct, const char *buffer, int len) {
530   noit_console_closure_t ncct = vncct;
531   return nc_write(ncct, buffer, len);
532 }
533
534 int
535 noit_console_close_xml(void *vncct) {
536   return 0;
537 }
538
539 void
540 noit_console_init(const char *progname) {
541   if(progname) {
542     char buff[32];
543     snprintf(buff, sizeof(buff), "%s# ", progname);
544     noit_console_set_default_prompt(buff);
545   }
546   el_multi_init();
547   signal(SIGTTOU, SIG_IGN);
548   noit_register_logops("noit_console", &noit_console_logio_ops);
549   eventer_name_callback("noit_console", noit_console_handler);
550 }
551
Note: See TracBrowser for help on using the browser.