~ubuntu-branches/debian/squeeze/putty/squeeze

« back to all changes in this revision

Viewing changes to unix/uxser.c

  • Committer: Bazaar Package Importer
  • Author(s): Colin Watson
  • Date: 2008-05-28 09:28:32 UTC
  • mfrom: (4.1.4 hardy)
  • Revision ID: james.westby@ubuntu.com-20080528092832-88epkb3d4s1zsw61
Tags: 0.60-3
* Move putty to Applications/Network/Communication menu sub-section.
* Use dh_desktop.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Serial back end (Unix-specific).
 
3
 */
 
4
 
 
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
 
 
20
typedef 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
 */
 
33
static 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
 
 
45
static 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
 
 
57
static tree234 *serial_by_fd = NULL;
 
58
 
 
59
static int serial_select_result(int fd, int event);
 
60
static void serial_uxsel_setup(Serial serial);
 
61
static void serial_try_write(Serial serial);
 
62
 
 
63
static const char *serial_configure(Serial serial, Config *cfg)
 
64
{
 
65
    struct termios options;
 
66
    int bflag, bval;
 
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
     */
 
78
#define SETBAUD(x) (bflag = B ## x, bval = x)
 
79
#define CHECKBAUD(x) do { if (cfg->serspeed >= x) SETBAUD(x); } while (0)
 
80
    SETBAUD(50);
 
81
#ifdef B75
 
82
    CHECKBAUD(75);
 
83
#endif
 
84
#ifdef B110
 
85
    CHECKBAUD(110);
 
86
#endif
 
87
#ifdef B134
 
88
    CHECKBAUD(134);
 
89
#endif
 
90
#ifdef B150
 
91
    CHECKBAUD(150);
 
92
#endif
 
93
#ifdef B200
 
94
    CHECKBAUD(200);
 
95
#endif
 
96
#ifdef B300
 
97
    CHECKBAUD(300);
 
98
#endif
 
99
#ifdef B600
 
100
    CHECKBAUD(600);
 
101
#endif
 
102
#ifdef B1200
 
103
    CHECKBAUD(1200);
 
104
#endif
 
105
#ifdef B1800
 
106
    CHECKBAUD(1800);
 
107
#endif
 
108
#ifdef B2400
 
109
    CHECKBAUD(2400);
 
110
#endif
 
111
#ifdef B4800
 
112
    CHECKBAUD(4800);
 
113
#endif
 
114
#ifdef B9600
 
115
    CHECKBAUD(9600);
 
116
#endif
 
117
#ifdef B19200
 
118
    CHECKBAUD(19200);
 
119
#endif
 
120
#ifdef B38400
 
121
    CHECKBAUD(38400);
 
122
#endif
 
123
#ifdef B57600
 
124
    CHECKBAUD(57600);
 
125
#endif
 
126
#ifdef B76800
 
127
    CHECKBAUD(76800);
 
128
#endif
 
129
#ifdef B115200
 
130
    CHECKBAUD(115200);
 
131
#endif
 
132
#ifdef B230400
 
133
    CHECKBAUD(230400);
 
134
#endif
 
135
#undef CHECKBAUD
 
136
#undef SETBAUD
 
137
    cfsetispeed(&options, bflag);
 
138
    cfsetospeed(&options, bflag);
 
139
    msg = dupprintf("Configuring baud rate %d", bval);
 
140
    logevent(serial->frontend, msg);
 
141
    sfree(msg);
 
142
 
 
143
    options.c_cflag &= ~CSIZE;
 
144
    switch (cfg->serdatabits) {
 
145
      case 5: options.c_cflag |= CS5; break;
 
146
      case 6: options.c_cflag |= CS6; break;
 
147
      case 7: options.c_cflag |= CS7; break;
 
148
      case 8: options.c_cflag |= CS8; break;
 
149
      default: return "Invalid number of data bits (need 5, 6, 7 or 8)";
 
150
    }
 
151
    msg = dupprintf("Configuring %d data bits", cfg->serdatabits);
 
152
    logevent(serial->frontend, msg);
 
153
    sfree(msg);
 
154
 
 
155
    if (cfg->serstopbits >= 4) {
 
156
        options.c_cflag |= CSTOPB;
 
157
    } else {
 
158
        options.c_cflag &= ~CSTOPB;
 
159
    }
 
160
    msg = dupprintf("Configuring %d stop bits",
 
161
                    (options.c_cflag & CSTOPB ? 2 : 1));
 
162
    logevent(serial->frontend, msg);
 
163
    sfree(msg);
 
164
 
 
165
    options.c_iflag &= ~(IXON|IXOFF);
 
166
#ifdef CRTSCTS
 
167
    options.c_cflag &= ~CRTSCTS;
 
168
#endif
 
169
#ifdef CNEW_RTSCTS
 
170
    options.c_cflag &= ~CNEW_RTSCTS;
 
171
#endif
 
172
    if (cfg->serflow == SER_FLOW_XONXOFF) {
 
173
        options.c_iflag |= IXON | IXOFF;
 
174
        str = "XON/XOFF";
 
175
    } else if (cfg->serflow == SER_FLOW_RTSCTS) {
 
176
#ifdef CRTSCTS
 
177
        options.c_cflag |= CRTSCTS;
 
178
#endif
 
179
#ifdef CNEW_RTSCTS
 
180
        options.c_cflag |= CNEW_RTSCTS;
 
181
#endif
 
182
        str = "RTS/CTS";
 
183
    } else
 
184
        str = "no";
 
185
    msg = dupprintf("Configuring %s flow control", str);
 
186
    logevent(serial->frontend, msg);
 
187
    sfree(msg);
 
188
 
 
189
    /* Parity */
 
190
    if (cfg->serparity == SER_PAR_ODD) {
 
191
        options.c_cflag |= PARENB;
 
192
        options.c_cflag |= PARODD;
 
193
        str = "odd";
 
194
    } else if (cfg->serparity == SER_PAR_EVEN) {
 
195
        options.c_cflag |= PARENB;
 
196
        options.c_cflag &= ~PARODD;
 
197
        str = "even";
 
198
    } else {
 
199
        options.c_cflag &= ~PARENB;
 
200
        str = "no";
 
201
    }
 
202
    msg = dupprintf("Configuring %s parity", str);
 
203
    logevent(serial->frontend, msg);
 
204
    sfree(msg);
 
205
 
 
206
    options.c_cflag |= CLOCAL | CREAD;
 
207
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
 
208
    options.c_iflag &= ~(ISTRIP | IGNCR | INLCR | ICRNL
 
209
#ifdef IUCLC
 
210
                         | IUCLC
 
211
#endif
 
212
                         );
 
213
    options.c_oflag &= ~(OPOST
 
214
#ifdef ONLCR
 
215
                         | ONLCR
 
216
#endif
 
217
#ifdef OCRNL
 
218
                         | OCRNL
 
219
#endif
 
220
#ifdef ONOCR
 
221
                         | ONOCR
 
222
#endif
 
223
#ifdef ONLRET
 
224
                         | ONLRET
 
225
#endif
 
226
                         );
 
227
    options.c_cc[VMIN] = 1;
 
228
    options.c_cc[VTIME] = 0;
 
229
 
 
230
    if (tcsetattr(serial->fd, TCSANOW, &options) < 0)
 
231
        return "Unable to configure serial port";
 
232
 
 
233
    return NULL;
 
234
}
 
235
 
 
236
/*
 
237
 * Called to set up the serial connection.
 
238
 * 
 
239
 * Returns an error message, or NULL on success.
 
240
 *
 
241
 * Also places the canonical host name into `realhost'. It must be
 
242
 * freed by the caller.
 
243
 */
 
244
static const char *serial_init(void *frontend_handle, void **backend_handle,
 
245
                               Config *cfg,
 
246
                               char *host, int port, char **realhost, int nodelay,
 
247
                               int keepalive)
 
248
{
 
249
    Serial serial;
 
250
    const char *err;
 
251
 
 
252
    serial = snew(struct serial_backend_data);
 
253
    *backend_handle = serial;
 
254
 
 
255
    serial->frontend = frontend_handle;
 
256
    serial->finished = FALSE;
 
257
    serial->inbufsize = 0;
 
258
    bufchain_init(&serial->output_data);
 
259
 
 
260
    {
 
261
        char *msg = dupprintf("Opening serial device %s", cfg->serline);
 
262
        logevent(serial->frontend, msg);
 
263
    }
 
264
 
 
265
    serial->fd = open(cfg->serline, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);
 
266
    if (serial->fd < 0)
 
267
        return "Unable to open serial port";
 
268
 
 
269
    cloexec(serial->fd);
 
270
 
 
271
    err = serial_configure(serial, cfg);
 
272
    if (err)
 
273
        return err;
 
274
 
 
275
    *realhost = dupstr(cfg->serline);
 
276
 
 
277
    if (!serial_by_fd)
 
278
        serial_by_fd = newtree234(serial_compare_by_fd);
 
279
    add234(serial_by_fd, serial);
 
280
 
 
281
    serial_uxsel_setup(serial);
 
282
 
 
283
    /*
 
284
     * Specials are always available.
 
285
     */
 
286
    update_specials_menu(serial->frontend);
 
287
 
 
288
    return NULL;
 
289
}
 
290
 
 
291
static void serial_close(Serial serial)
 
292
{
 
293
    if (serial->fd >= 0) {
 
294
        close(serial->fd);
 
295
        serial->fd = -1;
 
296
    }
 
297
}
 
298
 
 
299
static void serial_free(void *handle)
 
300
{
 
301
    Serial serial = (Serial) handle;
 
302
 
 
303
    serial_close(serial);
 
304
 
 
305
    bufchain_clear(&serial->output_data);
 
306
 
 
307
    sfree(serial);
 
308
}
 
309
 
 
310
static void serial_reconfig(void *handle, Config *cfg)
 
311
{
 
312
    Serial serial = (Serial) handle;
 
313
    const char *err;
 
314
 
 
315
    err = serial_configure(serial, cfg);
 
316
 
 
317
    /*
 
318
     * FIXME: what should we do if err returns something?
 
319
     */
 
320
}
 
321
 
 
322
static int serial_select_result(int fd, int event)
 
323
{
 
324
    Serial serial;
 
325
    char buf[4096];
 
326
    int ret;
 
327
    int finished = FALSE;
 
328
 
 
329
    serial = find234(serial_by_fd, &fd, serial_find_by_fd);
 
330
 
 
331
    if (!serial)
 
332
        return 1;                      /* spurious event; keep going */
 
333
 
 
334
    if (event == 1) {
 
335
        ret = read(serial->fd, buf, sizeof(buf));
 
336
 
 
337
        if (ret == 0) {
 
338
            /*
 
339
             * Shouldn't happen on a real serial port, but I'm open
 
340
             * to the idea that there might be two-way devices we
 
341
             * can treat _like_ serial ports which can return EOF.
 
342
             */
 
343
            finished = TRUE;
 
344
        } else if (ret < 0) {
 
345
            perror("read serial port");
 
346
            exit(1);
 
347
        } else if (ret > 0) {
 
348
            serial->inbufsize = from_backend(serial->frontend, 0, buf, ret);
 
349
            serial_uxsel_setup(serial); /* might acquire backlog and freeze */
 
350
        }
 
351
    } else if (event == 2) {
 
352
        /*
 
353
         * Attempt to send data down the pty.
 
354
         */
 
355
        serial_try_write(serial);
 
356
    }
 
357
 
 
358
    if (finished) {
 
359
        serial_close(serial);
 
360
 
 
361
        serial->finished = TRUE;
 
362
 
 
363
        notify_remote_exit(serial->frontend);
 
364
    }
 
365
 
 
366
    return !finished;
 
367
}
 
368
 
 
369
static void serial_uxsel_setup(Serial serial)
 
370
{
 
371
    int rwx = 0;
 
372
 
 
373
    if (serial->inbufsize <= SERIAL_MAX_BACKLOG)
 
374
        rwx |= 1;
 
375
    if (bufchain_size(&serial->output_data))
 
376
        rwx |= 2;                      /* might also want to write to it */
 
377
    uxsel_set(serial->fd, rwx, serial_select_result);
 
378
}
 
379
 
 
380
static void serial_try_write(Serial serial)
 
381
{
 
382
    void *data;
 
383
    int len, ret;
 
384
 
 
385
    assert(serial->fd >= 0);
 
386
 
 
387
    while (bufchain_size(&serial->output_data) > 0) {
 
388
        bufchain_prefix(&serial->output_data, &data, &len);
 
389
        ret = write(serial->fd, data, len);
 
390
 
 
391
        if (ret < 0 && (errno == EWOULDBLOCK)) {
 
392
            /*
 
393
             * We've sent all we can for the moment.
 
394
             */
 
395
            break;
 
396
        }
 
397
        if (ret < 0) {
 
398
            perror("write serial port");
 
399
            exit(1);
 
400
        }
 
401
        bufchain_consume(&serial->output_data, ret);
 
402
    }
 
403
 
 
404
    serial_uxsel_setup(serial);
 
405
}
 
406
 
 
407
/*
 
408
 * Called to send data down the serial connection.
 
409
 */
 
410
static int serial_send(void *handle, char *buf, int len)
 
411
{
 
412
    Serial serial = (Serial) handle;
 
413
 
 
414
    if (serial->fd < 0)
 
415
        return 0;
 
416
 
 
417
    bufchain_add(&serial->output_data, buf, len);
 
418
    serial_try_write(serial);
 
419
 
 
420
    return bufchain_size(&serial->output_data);
 
421
}
 
422
 
 
423
/*
 
424
 * Called to query the current sendability status.
 
425
 */
 
426
static int serial_sendbuffer(void *handle)
 
427
{
 
428
    Serial serial = (Serial) handle;
 
429
    return bufchain_size(&serial->output_data);
 
430
}
 
431
 
 
432
/*
 
433
 * Called to set the size of the window
 
434
 */
 
435
static void serial_size(void *handle, int width, int height)
 
436
{
 
437
    /* Do nothing! */
 
438
    return;
 
439
}
 
440
 
 
441
/*
 
442
 * Send serial special codes.
 
443
 */
 
444
static void serial_special(void *handle, Telnet_Special code)
 
445
{
 
446
    Serial serial = (Serial) handle;
 
447
 
 
448
    if (serial->fd >= 0 && code == TS_BRK) {
 
449
        tcsendbreak(serial->fd, 0);
 
450
        logevent(serial->frontend, "Sending serial break at user request");
 
451
    }
 
452
 
 
453
    return;
 
454
}
 
455
 
 
456
/*
 
457
 * Return a list of the special codes that make sense in this
 
458
 * protocol.
 
459
 */
 
460
static const struct telnet_special *serial_get_specials(void *handle)
 
461
{
 
462
    static const struct telnet_special specials[] = {
 
463
        {"Break", TS_BRK},
 
464
        {NULL, TS_EXITMENU}
 
465
    };
 
466
    return specials;
 
467
}
 
468
 
 
469
static int serial_connected(void *handle)
 
470
{
 
471
    return 1;                          /* always connected */
 
472
}
 
473
 
 
474
static int serial_sendok(void *handle)
 
475
{
 
476
    return 1;
 
477
}
 
478
 
 
479
static void serial_unthrottle(void *handle, int backlog)
 
480
{
 
481
    Serial serial = (Serial) handle;
 
482
    serial->inbufsize = backlog;
 
483
    serial_uxsel_setup(serial);
 
484
}
 
485
 
 
486
static int serial_ldisc(void *handle, int option)
 
487
{
 
488
    /*
 
489
     * Local editing and local echo are off by default.
 
490
     */
 
491
    return 0;
 
492
}
 
493
 
 
494
static void serial_provide_ldisc(void *handle, void *ldisc)
 
495
{
 
496
    /* This is a stub. */
 
497
}
 
498
 
 
499
static void serial_provide_logctx(void *handle, void *logctx)
 
500
{
 
501
    /* This is a stub. */
 
502
}
 
503
 
 
504
static int serial_exitcode(void *handle)
 
505
{
 
506
    Serial serial = (Serial) handle;
 
507
    if (serial->fd >= 0)
 
508
        return -1;                     /* still connected */
 
509
    else
 
510
        /* Exit codes are a meaningless concept with serial ports */
 
511
        return INT_MAX;
 
512
}
 
513
 
 
514
/*
 
515
 * cfg_info for Serial does nothing at all.
 
516
 */
 
517
static int serial_cfg_info(void *handle)
 
518
{
 
519
    return 0;
 
520
}
 
521
 
 
522
Backend serial_backend = {
 
523
    serial_init,
 
524
    serial_free,
 
525
    serial_reconfig,
 
526
    serial_send,
 
527
    serial_sendbuffer,
 
528
    serial_size,
 
529
    serial_special,
 
530
    serial_get_specials,
 
531
    serial_connected,
 
532
    serial_exitcode,
 
533
    serial_sendok,
 
534
    serial_ldisc,
 
535
    serial_provide_ldisc,
 
536
    serial_provide_logctx,
 
537
    serial_unthrottle,
 
538
    serial_cfg_info,
 
539
    1
 
540
};