root/src/noit_console.c

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

libedit actually works now?

  • 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 #include <util.h>
13 #include <arpa/telnet.h>
14
15 #include "eventer/eventer.h"
16 #include "utils/noit_log.h"
17 #include "noit_listener.h"
18 #include "noit_console.h"
19 #include "noit_tokenizer.h"
20
21 int
22 nc_printf(noit_console_closure_t ncct, const char *fmt, ...) {
23   int len;
24   va_list arg;
25   va_start(arg, fmt);
26   len = nc_vprintf(ncct, fmt, arg);
27   va_end(arg);
28   return len;
29 }
30 int
31 nc_vprintf(noit_console_closure_t ncct, const char *fmt, va_list arg) {
32 #ifdef va_copy
33   va_list copy;
34 #endif
35   int lenwanted;
36  
37   if(!ncct->outbuf_allocd) {
38     ncct->outbuf = malloc(4096);
39     if(!ncct->outbuf) return 0;
40     ncct->outbuf_allocd = 4096;
41   }
42   while(1) {
43     char *newbuf;
44 #ifdef va_copy
45     va_copy(copy, arg);
46     lenwanted = vsnprintf(ncct->outbuf + ncct->outbuf_len,
47                           ncct->outbuf_allocd - ncct->outbuf_len,
48                           fmt, copy);
49     va_end(copy);
50 #else
51     lenwanted = vsnprintf(ncct->outbuf + ncct->outbuf_len,
52                           ncct->outbuf_allocd - ncct->outbuf_len,
53                           fmt, arg);
54 #endif
55     if(ncct->outbuf_len + lenwanted < ncct->outbuf_allocd) {
56       /* All went well, things are as we want them. */
57       ncct->outbuf_len += lenwanted;
58       return lenwanted;
59     }
60
61     /* We need to enlarge the buffer */
62     lenwanted += ncct->outbuf_len;
63     lenwanted /= 4096;
64     lenwanted += 1;
65     lenwanted *= 4096;
66     newbuf = realloc(ncct->outbuf, lenwanted);
67     if(!newbuf) {
68       return 0;
69     }
70     ncct->outbuf = newbuf;
71     ncct->outbuf_allocd = lenwanted;
72   }
73   return -1;
74 }
75 int
76 nc_write(noit_console_closure_t ncct, void *buf, int len) {
77   if(!ncct->outbuf_allocd) {
78     ncct->outbuf = malloc(len);
79     if(!ncct->outbuf) return 0;
80     ncct->outbuf_allocd = len;
81   }
82   else if(ncct->outbuf_allocd < ncct->outbuf_len + len) {
83     char *newbuf;
84     newbuf = realloc(ncct->outbuf, ncct->outbuf_len + len);
85     if(!newbuf) return 0;
86     ncct->outbuf = newbuf;
87   }
88   memcpy(ncct->outbuf + ncct->outbuf_len, buf, len);
89   ncct->outbuf_len += len;
90   return len;
91 }
92
93 void
94 noit_console_closure_free(noit_console_closure_t ncct) {
95   if(ncct->el) el_end(ncct->el);
96   if(ncct->hist) history_end(ncct->hist);
97   if(ncct->pty_master >= 0) close(ncct->pty_master);
98   if(ncct->pty_slave >= 0) close(ncct->pty_slave);
99   if(ncct->outbuf) free(ncct->outbuf);
100   if(ncct->telnet) noit_console_telnet_free(ncct->telnet);
101   free(ncct);
102 }
103
104 noit_console_closure_t
105 noit_console_closure_alloc() {
106   noit_console_closure_t new_ncct;
107   new_ncct = calloc(1, sizeof(*new_ncct));
108   new_ncct->pty_master = -1;
109   new_ncct->pty_slave = -1;
110   return new_ncct;
111 }
112
113 int
114 noit_console_continue_sending(noit_console_closure_t ncct,
115                               int *mask) {
116   int len;
117   eventer_t e = ncct->e;
118   if(!ncct->outbuf_len) return 0;
119   while(ncct->outbuf_len > ncct->outbuf_completed) {
120     len = e->opset->write(e->fd, ncct->outbuf + ncct->outbuf_completed,
121                           ncct->outbuf_len - ncct->outbuf_completed,
122                           mask, e);
123     if(len < 0) {
124       if(errno == EAGAIN) return -1;
125       /* Do something else here? */
126       return -1;
127     }
128     ncct->outbuf_completed += len;
129   }
130   len = ncct->outbuf_len;
131   free(ncct->outbuf);
132   ncct->outbuf = NULL;
133   ncct->outbuf_allocd = 0;
134   ncct->outbuf_len = ncct->outbuf_completed = 0;
135   return len;
136 }
137
138 void
139 noit_console_init() {
140   el_multi_init();
141   signal(SIGTTOU, SIG_IGN);
142   eventer_name_callback("noit_console", noit_console_handler);
143 }
144
145 void
146 noit_console_dispatch(eventer_t e, const char *buffer,
147                       noit_console_closure_t ncct) {
148   char *cmds[32];
149   int i, cnt = 32;
150   nc_printf(ncct, "You said: %s\r\n", buffer);
151   i = noit_tokenize(buffer, cmds, &cnt);
152   if(i>cnt) nc_printf(ncct, "Command length too long.\n");
153   if(i<0) nc_printf(ncct, "Error at offset: %d\n", 0-i);
154   for(i=0;i<cnt;i++) {
155     nc_printf(ncct, "[%d] '%s'\r\n", i, cmds[i]);
156     free(cmds[i]);
157   }
158 }
159
160 int
161 noit_console_handler(eventer_t e, int mask, void *closure,
162                      struct timeval *now) {
163   int newmask = EVENTER_READ | EVENTER_EXCEPTION;
164   int keep_going;
165   noit_console_closure_t ncct = closure;
166
167   if(mask & EVENTER_EXCEPTION || (ncct && ncct->wants_shutdown)) {
168 socket_error:
169     /* Exceptions cause us to simply snip the connection */
170     eventer_remove_fd(e->fd);
171     e->opset->close(e->fd, &newmask, e);
172     if(ncct) noit_console_closure_free(ncct);
173     return 0;
174   }
175
176   if(!ncct) {
177     int on = 1;
178     ncct = closure = e->closure = noit_console_closure_alloc();
179     ncct->e = e;
180     if(openpty(&ncct->pty_master, &ncct->pty_slave, NULL, NULL, NULL) ||
181        ioctl(ncct->pty_master, FIONBIO, &on)) {
182       nc_printf(ncct, "Failed to open pty: %s\n", strerror(errno));
183       ncct->wants_shutdown = 1;
184     }
185     else {
186       HistEvent ev;
187       ncct->hist = history_init();
188       history(ncct->hist, &ev, H_SETSIZE, 500);
189       ncct->el = el_init("noitd", ncct->pty_master, e->fd, e->fd);
190       el_set(ncct->el, EL_EDITOR, "emacs");
191       el_set(ncct->el, EL_HIST, history, ncct->hist);
192       ncct->telnet = noit_console_telnet_alloc(ncct);
193     }
194   }
195
196   /* If we still have data to send back to the client, this will take
197    * care of that
198    */
199   if(noit_console_continue_sending(ncct, &newmask) == -1) {
200     if(errno != EAGAIN) goto socket_error;
201     return newmask | EVENTER_EXCEPTION;
202   }
203
204   for(keep_going=1 ; keep_going ; ) {
205     int len, plen;
206     char sbuf[4096];
207     const char *buffer;
208
209     keep_going = 0;
210
211     buffer = el_gets(ncct->el, &plen);
212     if(!el_eagain(ncct->el)) keep_going++;
213
214     len = e->opset->read(e->fd, sbuf, sizeof(sbuf)-1, &newmask, e);
215 noitL(noit_stderr, "opset->read => %d bytes\n", len);
216     if(len == 0 || (len < 0 && errno != EAGAIN)) {
217       eventer_remove_fd(e->fd);
218       close(e->fd);
219       return 0;
220     }
221     if(len > 0) {
222       keep_going++;
223       sbuf[len] = '\0';
224       if(ncct->telnet) {
225         noit_console_telnet_telrcv(ncct, sbuf, len);
226         ptyflush(ncct);
227       }
228       else {
229         write(ncct->pty_slave, sbuf, len);
230       }
231     }
232     if(buffer) {
233       char *cmd_buffer;
234       HistEvent ev;
235       cmd_buffer = malloc(plen+1);
236       memcpy(cmd_buffer, buffer, plen);
237       /* chomp */
238       cmd_buffer[plen] = '\0';
239       if(cmd_buffer[plen-1] == '\n') cmd_buffer[plen-1] = '\0';
240       noitL(noit_debug, "IN: '%s'\n", cmd_buffer);
241       history(ncct->hist, &ev, H_ENTER, cmd_buffer);
242       noit_console_dispatch(e, cmd_buffer, ncct);
243       free(cmd_buffer);
244       if(noit_console_continue_sending(ncct, &newmask) == -1) {
245         if(errno != EAGAIN) goto socket_error;
246         return newmask | EVENTER_EXCEPTION;
247       }
248     }
249   }
250   return newmask | EVENTER_EXCEPTION;
251 }
252
Note: See TracBrowser for help on using the browser.