Fix another giant batch of resource leaks. (Mostly memory, but there's
[u/mdw/putty] / unix / uxser.c
CommitLineData
aef05b78 1/*
2 * Serial back end (Unix-specific).
3 */
4
aef05b78 5#include <stdio.h>
6#include <stdlib.h>
7#include <assert.h>
8#include <limits.h>
9
10#include <errno.h>
11#include <unistd.h>
12#include <fcntl.h>
13#include <termios.h>
14
15#include "putty.h"
16#include "tree234.h"
17
18#define SERIAL_MAX_BACKLOG 4096
19
20typedef struct serial_backend_data {
21 void *frontend;
22 int fd;
23 int finished;
24 int inbufsize;
25 bufchain output_data;
26} *Serial;
27
28/*
29 * We store our serial backends in a tree sorted by fd, so that
30 * when we get an uxsel notification we know which backend instance
31 * is the owner of the serial port that caused it.
32 */
33static int serial_compare_by_fd(void *av, void *bv)
34{
35 Serial a = (Serial)av;
36 Serial b = (Serial)bv;
37
38 if (a->fd < b->fd)
39 return -1;
40 else if (a->fd > b->fd)
41 return +1;
42 return 0;
43}
44
45static int serial_find_by_fd(void *av, void *bv)
46{
47 int a = *(int *)av;
48 Serial b = (Serial)bv;
49
50 if (a < b->fd)
51 return -1;
52 else if (a > b->fd)
53 return +1;
54 return 0;
55}
56
57static tree234 *serial_by_fd = NULL;
58
59static int serial_select_result(int fd, int event);
60static void serial_uxsel_setup(Serial serial);
61static void serial_try_write(Serial serial);
62
4a693cfc 63static const char *serial_configure(Serial serial, Conf *conf)
aef05b78 64{
65 struct termios options;
4a693cfc 66 int bflag, bval, speed, flow, parity;
aef05b78 67 const char *str;
68 char *msg;
69
70 if (serial->fd < 0)
71 return "Unable to reconfigure already-closed serial connection";
72
73 tcgetattr(serial->fd, &options);
74
75 /*
76 * Find the appropriate baud rate flag.
77 */
4a693cfc 78 speed = conf_get_int(conf, CONF_serspeed);
aef05b78 79#define SETBAUD(x) (bflag = B ## x, bval = x)
4a693cfc 80#define CHECKBAUD(x) do { if (speed >= x) SETBAUD(x); } while (0)
aef05b78 81 SETBAUD(50);
82#ifdef B75
83 CHECKBAUD(75);
84#endif
85#ifdef B110
86 CHECKBAUD(110);
87#endif
88#ifdef B134
89 CHECKBAUD(134);
90#endif
91#ifdef B150
92 CHECKBAUD(150);
93#endif
94#ifdef B200
95 CHECKBAUD(200);
96#endif
97#ifdef B300
98 CHECKBAUD(300);
99#endif
100#ifdef B600
101 CHECKBAUD(600);
102#endif
103#ifdef B1200
104 CHECKBAUD(1200);
105#endif
106#ifdef B1800
107 CHECKBAUD(1800);
108#endif
109#ifdef B2400
110 CHECKBAUD(2400);
111#endif
112#ifdef B4800
113 CHECKBAUD(4800);
114#endif
115#ifdef B9600
116 CHECKBAUD(9600);
117#endif
118#ifdef B19200
119 CHECKBAUD(19200);
120#endif
121#ifdef B38400
122 CHECKBAUD(38400);
123#endif
124#ifdef B57600
125 CHECKBAUD(57600);
126#endif
127#ifdef B76800
128 CHECKBAUD(76800);
129#endif
130#ifdef B115200
131 CHECKBAUD(115200);
132#endif
25d0ab1e 133#ifdef B153600
134 CHECKBAUD(153600);
135#endif
aef05b78 136#ifdef B230400
137 CHECKBAUD(230400);
138#endif
25d0ab1e 139#ifdef B307200
140 CHECKBAUD(307200);
141#endif
142#ifdef B460800
143 CHECKBAUD(460800);
144#endif
145#ifdef B500000
146 CHECKBAUD(500000);
147#endif
148#ifdef B576000
149 CHECKBAUD(576000);
150#endif
151#ifdef B921600
152 CHECKBAUD(921600);
153#endif
154#ifdef B1000000
155 CHECKBAUD(1000000);
156#endif
157#ifdef B1152000
158 CHECKBAUD(1152000);
159#endif
160#ifdef B1500000
161 CHECKBAUD(1500000);
162#endif
163#ifdef B2000000
164 CHECKBAUD(2000000);
165#endif
166#ifdef B2500000
167 CHECKBAUD(2500000);
168#endif
169#ifdef B3000000
170 CHECKBAUD(3000000);
171#endif
172#ifdef B3500000
173 CHECKBAUD(3500000);
174#endif
175#ifdef B4000000
176 CHECKBAUD(4000000);
177#endif
aef05b78 178#undef CHECKBAUD
179#undef SETBAUD
180 cfsetispeed(&options, bflag);
181 cfsetospeed(&options, bflag);
182 msg = dupprintf("Configuring baud rate %d", bval);
183 logevent(serial->frontend, msg);
184 sfree(msg);
185
186 options.c_cflag &= ~CSIZE;
4a693cfc 187 switch (conf_get_int(conf, CONF_serdatabits)) {
aef05b78 188 case 5: options.c_cflag |= CS5; break;
189 case 6: options.c_cflag |= CS6; break;
190 case 7: options.c_cflag |= CS7; break;
191 case 8: options.c_cflag |= CS8; break;
192 default: return "Invalid number of data bits (need 5, 6, 7 or 8)";
193 }
4a693cfc 194 msg = dupprintf("Configuring %d data bits",
195 conf_get_int(conf, CONF_serdatabits));
aef05b78 196 logevent(serial->frontend, msg);
197 sfree(msg);
198
4a693cfc 199 if (conf_get_int(conf, CONF_serstopbits) >= 4) {
aef05b78 200 options.c_cflag |= CSTOPB;
201 } else {
202 options.c_cflag &= ~CSTOPB;
203 }
204 msg = dupprintf("Configuring %d stop bits",
205 (options.c_cflag & CSTOPB ? 2 : 1));
206 logevent(serial->frontend, msg);
207 sfree(msg);
208
e26f8aa1 209 options.c_iflag &= ~(IXON|IXOFF);
210#ifdef CRTSCTS
211 options.c_cflag &= ~CRTSCTS;
212#endif
213#ifdef CNEW_RTSCTS
214 options.c_cflag &= ~CNEW_RTSCTS;
215#endif
4a693cfc 216 flow = conf_get_int(conf, CONF_serflow);
217 if (flow == SER_FLOW_XONXOFF) {
e26f8aa1 218 options.c_iflag |= IXON | IXOFF;
aef05b78 219 str = "XON/XOFF";
4a693cfc 220 } else if (flow == SER_FLOW_RTSCTS) {
aef05b78 221#ifdef CRTSCTS
222 options.c_cflag |= CRTSCTS;
223#endif
224#ifdef CNEW_RTSCTS
225 options.c_cflag |= CNEW_RTSCTS;
226#endif
227 str = "RTS/CTS";
228 } else
229 str = "no";
230 msg = dupprintf("Configuring %s flow control", str);
231 logevent(serial->frontend, msg);
232 sfree(msg);
233
234 /* Parity */
4a693cfc 235 parity = conf_get_int(conf, CONF_serparity);
236 if (parity == SER_PAR_ODD) {
aef05b78 237 options.c_cflag |= PARENB;
238 options.c_cflag |= PARODD;
239 str = "odd";
4a693cfc 240 } else if (parity == SER_PAR_EVEN) {
aef05b78 241 options.c_cflag |= PARENB;
242 options.c_cflag &= ~PARODD;
243 str = "even";
244 } else {
245 options.c_cflag &= ~PARENB;
246 str = "no";
247 }
248 msg = dupprintf("Configuring %s parity", str);
249 logevent(serial->frontend, msg);
250 sfree(msg);
251
252 options.c_cflag |= CLOCAL | CREAD;
253 options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
9a41f8d3 254 options.c_iflag &= ~(ISTRIP | IGNCR | INLCR | ICRNL
255#ifdef IUCLC
256 | IUCLC
257#endif
258 );
259 options.c_oflag &= ~(OPOST
260#ifdef ONLCR
261 | ONLCR
262#endif
e8c03e4c 263#ifdef OCRNL
264 | OCRNL
265#endif
266#ifdef ONOCR
267 | ONOCR
268#endif
269#ifdef ONLRET
270 | ONLRET
271#endif
272 );
aef05b78 273 options.c_cc[VMIN] = 1;
274 options.c_cc[VTIME] = 0;
275
276 if (tcsetattr(serial->fd, TCSANOW, &options) < 0)
277 return "Unable to configure serial port";
278
279 return NULL;
280}
281
282/*
283 * Called to set up the serial connection.
284 *
285 * Returns an error message, or NULL on success.
286 *
287 * Also places the canonical host name into `realhost'. It must be
288 * freed by the caller.
289 */
290static const char *serial_init(void *frontend_handle, void **backend_handle,
4a693cfc 291 Conf *conf,
aef05b78 292 char *host, int port, char **realhost, int nodelay,
293 int keepalive)
294{
295 Serial serial;
296 const char *err;
4a693cfc 297 char *line;
aef05b78 298
299 serial = snew(struct serial_backend_data);
300 *backend_handle = serial;
301
302 serial->frontend = frontend_handle;
303 serial->finished = FALSE;
304 serial->inbufsize = 0;
305 bufchain_init(&serial->output_data);
306
4a693cfc 307 line = conf_get_str(conf, CONF_serline);
aef05b78 308 {
4a693cfc 309 char *msg = dupprintf("Opening serial device %s", line);
aef05b78 310 logevent(serial->frontend, msg);
038ec85e 311 sfree(msg);
aef05b78 312 }
313
4a693cfc 314 serial->fd = open(line, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);
aef05b78 315 if (serial->fd < 0)
316 return "Unable to open serial port";
317
db9b7dce 318 cloexec(serial->fd);
89e97516 319
4a693cfc 320 err = serial_configure(serial, conf);
aef05b78 321 if (err)
322 return err;
323
4a693cfc 324 *realhost = dupstr(line);
aef05b78 325
326 if (!serial_by_fd)
327 serial_by_fd = newtree234(serial_compare_by_fd);
328 add234(serial_by_fd, serial);
329
330 serial_uxsel_setup(serial);
331
45ef7ce3 332 /*
333 * Specials are always available.
334 */
335 update_specials_menu(serial->frontend);
336
aef05b78 337 return NULL;
338}
339
340static void serial_close(Serial serial)
341{
342 if (serial->fd >= 0) {
343 close(serial->fd);
344 serial->fd = -1;
345 }
346}
347
348static void serial_free(void *handle)
349{
350 Serial serial = (Serial) handle;
351
352 serial_close(serial);
353
354 bufchain_clear(&serial->output_data);
355
356 sfree(serial);
357}
358
4a693cfc 359static void serial_reconfig(void *handle, Conf *conf)
aef05b78 360{
361 Serial serial = (Serial) handle;
aef05b78 362
363 /*
3ab79841 364 * FIXME: what should we do if this returns an error?
aef05b78 365 */
4a693cfc 366 serial_configure(serial, conf);
aef05b78 367}
368
369static int serial_select_result(int fd, int event)
370{
371 Serial serial;
372 char buf[4096];
373 int ret;
374 int finished = FALSE;
375
376 serial = find234(serial_by_fd, &fd, serial_find_by_fd);
377
378 if (!serial)
379 return 1; /* spurious event; keep going */
380
381 if (event == 1) {
382 ret = read(serial->fd, buf, sizeof(buf));
383
384 if (ret == 0) {
385 /*
386 * Shouldn't happen on a real serial port, but I'm open
387 * to the idea that there might be two-way devices we
388 * can treat _like_ serial ports which can return EOF.
389 */
390 finished = TRUE;
391 } else if (ret < 0) {
0620c88a 392#ifdef EAGAIN
393 if (errno == EAGAIN)
394 return 1; /* spurious */
395#endif
396#ifdef EWOULDBLOCK
397 if (errno == EWOULDBLOCK)
398 return 1; /* spurious */
399#endif
aef05b78 400 perror("read serial port");
401 exit(1);
402 } else if (ret > 0) {
403 serial->inbufsize = from_backend(serial->frontend, 0, buf, ret);
404 serial_uxsel_setup(serial); /* might acquire backlog and freeze */
405 }
406 } else if (event == 2) {
407 /*
408 * Attempt to send data down the pty.
409 */
410 serial_try_write(serial);
411 }
412
413 if (finished) {
414 serial_close(serial);
415
416 serial->finished = TRUE;
417
418 notify_remote_exit(serial->frontend);
419 }
420
421 return !finished;
422}
423
424static void serial_uxsel_setup(Serial serial)
425{
426 int rwx = 0;
427
428 if (serial->inbufsize <= SERIAL_MAX_BACKLOG)
429 rwx |= 1;
430 if (bufchain_size(&serial->output_data))
431 rwx |= 2; /* might also want to write to it */
432 uxsel_set(serial->fd, rwx, serial_select_result);
433}
434
435static void serial_try_write(Serial serial)
436{
437 void *data;
438 int len, ret;
439
440 assert(serial->fd >= 0);
441
442 while (bufchain_size(&serial->output_data) > 0) {
443 bufchain_prefix(&serial->output_data, &data, &len);
444 ret = write(serial->fd, data, len);
445
446 if (ret < 0 && (errno == EWOULDBLOCK)) {
447 /*
448 * We've sent all we can for the moment.
449 */
450 break;
451 }
452 if (ret < 0) {
453 perror("write serial port");
454 exit(1);
455 }
456 bufchain_consume(&serial->output_data, ret);
457 }
458
459 serial_uxsel_setup(serial);
460}
461
462/*
463 * Called to send data down the serial connection.
464 */
465static int serial_send(void *handle, char *buf, int len)
466{
467 Serial serial = (Serial) handle;
468
469 if (serial->fd < 0)
470 return 0;
471
472 bufchain_add(&serial->output_data, buf, len);
473 serial_try_write(serial);
474
475 return bufchain_size(&serial->output_data);
476}
477
478/*
479 * Called to query the current sendability status.
480 */
481static int serial_sendbuffer(void *handle)
482{
483 Serial serial = (Serial) handle;
484 return bufchain_size(&serial->output_data);
485}
486
487/*
488 * Called to set the size of the window
489 */
490static void serial_size(void *handle, int width, int height)
491{
492 /* Do nothing! */
493 return;
494}
495
496/*
497 * Send serial special codes.
498 */
499static void serial_special(void *handle, Telnet_Special code)
500{
45ef7ce3 501 Serial serial = (Serial) handle;
502
503 if (serial->fd >= 0 && code == TS_BRK) {
504 tcsendbreak(serial->fd, 0);
505 logevent(serial->frontend, "Sending serial break at user request");
506 }
507
aef05b78 508 return;
509}
510
511/*
512 * Return a list of the special codes that make sense in this
513 * protocol.
514 */
515static const struct telnet_special *serial_get_specials(void *handle)
516{
45ef7ce3 517 static const struct telnet_special specials[] = {
518 {"Break", TS_BRK},
519 {NULL, TS_EXITMENU}
520 };
521 return specials;
aef05b78 522}
523
524static int serial_connected(void *handle)
525{
526 return 1; /* always connected */
527}
528
529static int serial_sendok(void *handle)
530{
531 return 1;
532}
533
534static void serial_unthrottle(void *handle, int backlog)
535{
536 Serial serial = (Serial) handle;
537 serial->inbufsize = backlog;
538 serial_uxsel_setup(serial);
539}
540
541static int serial_ldisc(void *handle, int option)
542{
543 /*
544 * Local editing and local echo are off by default.
545 */
546 return 0;
547}
548
549static void serial_provide_ldisc(void *handle, void *ldisc)
550{
551 /* This is a stub. */
552}
553
554static void serial_provide_logctx(void *handle, void *logctx)
555{
556 /* This is a stub. */
557}
558
559static int serial_exitcode(void *handle)
560{
561 Serial serial = (Serial) handle;
562 if (serial->fd >= 0)
563 return -1; /* still connected */
564 else
565 /* Exit codes are a meaningless concept with serial ports */
566 return INT_MAX;
567}
568
569/*
570 * cfg_info for Serial does nothing at all.
571 */
572static int serial_cfg_info(void *handle)
573{
574 return 0;
575}
576
577Backend serial_backend = {
578 serial_init,
579 serial_free,
580 serial_reconfig,
581 serial_send,
582 serial_sendbuffer,
583 serial_size,
584 serial_special,
585 serial_get_specials,
586 serial_connected,
587 serial_exitcode,
588 serial_sendok,
589 serial_ldisc,
590 serial_provide_ldisc,
591 serial_provide_logctx,
592 serial_unthrottle,
593 serial_cfg_info,
9e164d82 594 "serial",
595 PROT_SERIAL,
a4451dd1 596 0
aef05b78 597};