Root/neocon/neocon.c

1/*
2 * neocon.c - An interface for changing tty devices
3 *
4 * Copyright (C) 2007, 2008 by OpenMoko, Inc.
5 * Copyright 2011, 2013 by Werner Almesberger
6 * Written by Werner Almesberger <werner@almesberger.net>
7 * All Rights Reserved
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 */
23
24
25#include <stdlib.h>
26#include <stdio.h>
27#include <stdio.h>
28#include <unistd.h>
29#include <string.h>
30#include <termios.h>
31#include <fcntl.h>
32#include <assert.h>
33#include <sys/time.h>
34#include <sys/select.h>
35
36
37#define MAX_BUF 2048
38
39static char *const *ttys;
40static int num_ttys;
41static int curr_tty = -1; /* start with first tty */
42static speed_t speed = B115200;
43static struct termios console, tty;
44static FILE *log = NULL;
45static int timestamp = 0;
46static char escape = '~';
47
48
49static struct bps {
50    speed_t speed;
51    int bps;
52} bps_tab[] = {
53    { B300, 300 },
54    { B1200, 1200 },
55    { B2400, 2400 },
56    { B9600, 9600 },
57    { B19200, 19200 },
58    { B38400, 38400 },
59    { B57600, 57600 },
60    { B115200, 115200 },
61    { 0, 0 }
62};
63
64
65static speed_t bps_to_speed(int bps)
66{
67    const struct bps *p;
68
69    for (p = bps_tab; p->bps; p++)
70        if (p->bps == bps)
71            return p->speed;
72    fprintf(stderr, "no such speed: %d bps\n", bps);
73    exit(1);
74}
75
76
77static void make_raw(int fd, struct termios *old, int cr, int local_edit)
78{
79    struct termios t;
80    long flags;
81
82    if (tcgetattr(fd, &t) < 0) {
83        perror("tcgetattr");
84        exit(1);
85    }
86    if (old)
87        *old = t;
88    if (local_edit)
89        return;
90    cfmakeraw(&t);
91    if (fd) {
92        t.c_iflag &= ~(IXON | IXOFF);
93        t.c_cflag |= CLOCAL;
94        t.c_cflag &= ~CRTSCTS;
95        if (cfsetispeed(&t, speed) < 0) {
96            perror("cfsetispeed");
97            exit(1);
98        }
99        if (cfsetospeed(&t, speed) < 0) {
100            perror("cfsetospeed");
101            exit(1);
102        }
103    }
104    if (cr)
105        t.c_oflag |= OPOST | ONLCR;
106    if (tcsetattr(fd, TCSANOW, &t) < 0) {
107        perror("tcsetattr");
108        exit(1);
109    }
110    flags = fcntl(fd, F_GETFL);
111    if (flags < 0) {
112        perror("fcntl F_GETFL");
113        exit(1);
114    }
115    if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) < 0) {
116        perror("fcntl F_GETFL");
117        exit(1);
118    }
119}
120
121
122static int open_next_tty(void)
123{
124    int i, fd = -1;
125
126    for (i = 0; i != num_ttys; i++) {
127        curr_tty = (curr_tty+1) % num_ttys;
128        fd = open(ttys[curr_tty], O_RDWR | O_NDELAY);
129        if (fd >= 0)
130            break;
131    }
132    if (fd >= 0)
133        make_raw(fd, &tty, 0, 0);
134        return fd;
135}
136
137
138/*
139 * Return 1 if the user manually forces a device change.
140 */
141
142
143static int scan(const char *s, size_t len)
144{
145    static int state = 0;
146    const char *p;
147    int res = 0;
148
149    for (p = s; p != s+len; p++)
150        switch (state) {
151        case 0:
152            if (*p == escape)
153                state++;
154            else
155                state = 0;
156            break;
157        case 1:
158            if (*p == '.')
159                exit(0);
160            if (*p == 'n')
161                res = 1;
162            state = 0;
163            break;
164        }
165    return res;
166}
167
168
169static int write_log(const char *buf, ssize_t len)
170{
171    size_t wrote;
172
173    wrote = fwrite(buf, 1, len, log);
174    if (wrote == len)
175        return 1;
176    fprintf(stderr, "write failed. closing log file.\n");
177    fclose(log);
178    log = NULL;
179    return 0;
180}
181
182
183static int add_timestamp(void)
184{
185    struct timeval tv;
186    char buf[40]; /* be generous */
187    int len;
188
189    if (gettimeofday(&tv, NULL) < 0) {
190        perror("gettimeofday");
191        exit(1);
192    }
193    len = sprintf(buf, "%lu.%06lu ",
194        (unsigned long) tv.tv_sec, (unsigned long) tv.tv_usec);
195    return write_log(buf, len);
196}
197
198
199static void do_log(const char *buf, ssize_t len)
200{
201    static int nl = 1; /* we're at the beginning of a new line */
202    char tmp[MAX_BUF];
203    const char *from;
204    char *to;
205
206    assert(len <= MAX_BUF);
207    from = buf;
208    to = tmp;
209    while (from != buf+len) {
210        if (*from == '\r') {
211            from++;
212            continue;
213        }
214        if (nl && timestamp)
215            if (!add_timestamp())
216                return;
217        nl = 0;
218        if (*from == '\n') {
219            *to++ = *from++;
220            if (!write_log(tmp, to-tmp))
221                return;
222            to = tmp;
223            nl = 1;
224            continue;
225        }
226        *to++ = *from < ' ' || *from > '~' ? '#' : *from;
227        from++;
228    }
229    write_log(tmp, to-tmp);
230}
231
232
233static int copy(int in, int out, int from_user, int single)
234{
235    char buffer[MAX_BUF];
236    ssize_t got, wrote, pos;
237
238    got = read(in, buffer, single ? 1 : sizeof(buffer));
239    if (got <= 0)
240        return 0;
241    if (from_user) {
242        if (scan(buffer, got))
243            return 0;
244    } else {
245        if (log)
246            do_log(buffer, got);
247    }
248    for (pos = 0; pos != got; pos += wrote) {
249        wrote = write(out, buffer+pos, got-pos);
250        if (wrote < 0)
251            return 0;
252    }
253    return 1;
254}
255
256
257static void write_string(const char *s)
258{
259    int len = strlen(s);
260
261    while (len) {
262        ssize_t wrote;
263
264        wrote = write(1, s, len);
265        if (wrote < 0) {
266            perror("write");
267            exit(1);
268        }
269        s += wrote;
270        len -= wrote;
271    }
272}
273
274
275static void cleanup(void)
276{
277    if (tcsetattr(0, TCSANOW, &console) < 0)
278        perror("tcsetattr");
279    write(1, "\n", 1);
280}
281
282
283static void usage(const char *name)
284{
285    fprintf(stderr,
286"usage: %s [-b bps] [-c] [-e escape] [-l logfile [-a] [-T]] [-L]\n"
287" %*s [-t delay_ms] tty ...\n\n"
288" -a append to the log file if it already exists\n"
289" -b bps set the TTY to the specified bit rate\n"
290" -c add carriage return before newline (on console)\n"
291" -e escape set the escape character (default: ~)\n"
292" -l logfile log all output to the specified file\n"
293" -L enable local line editing\n"
294" -t delay_ms wait the specified amount of time between input characters\n"
295" -T add timestamps to the log file\n"
296        , name, (int) strlen(name), "");
297    exit(1);
298}
299
300
301int main(int argc, char *const *argv)
302{
303    char *end;
304    int c, bps, cr = 0;
305    int local_edit = 0;
306    int fd = -1;
307    int append = 0;
308    const char *logfile = NULL;
309    int throttle_us = 0;
310    int throttle = 0;
311
312    while ((c = getopt(argc, argv, "ab:ce:l:Lt:T")) != EOF)
313        switch (c) {
314        case 'a':
315            append = 1;
316            break;
317        case 'b':
318            bps = strtoul(optarg, &end, 0);
319            if (*end)
320                    usage(*argv);
321            speed = bps_to_speed(bps);
322            break;
323        case 'c':
324            cr = 1;
325            break;
326        case 'e':
327            if (strlen(optarg) != 1)
328                usage(*argv);
329            escape = *optarg;
330            break;
331        case 'l':
332            logfile = optarg;
333            break;
334        case 'L':
335            local_edit = 1;
336            break;
337        case 't':
338            throttle_us = strtoul(optarg, &end, 0)*1000;
339            if (*end)
340                usage(*argv);
341            break;
342        case 'T':
343            timestamp = 1;
344            break;
345        default:
346            usage(*argv);
347    }
348    num_ttys = argc-optind;
349    ttys = argv+optind;
350
351    if (logfile) {
352        log = fopen(logfile, append ? "a" : "w");
353        if (!log) {
354            perror(logfile);
355            exit(1);
356        }
357        setlinebuf(log);
358    }
359
360    make_raw(0, &console, cr, local_edit);
361    atexit(cleanup);
362    while (1) {
363        struct timeval tv;
364        fd_set set;
365        int res;
366
367        if (fd < 0) {
368            fd = open_next_tty();
369            if (fd > 0) {
370                char buf[1024]; /* enough :-) */
371
372                sprintf(buf, "\r\r[Open %s]\r\n",
373                    ttys[curr_tty]);
374                write_string(buf);
375            }
376        }
377        FD_ZERO(&set);
378        if (!throttle)
379            FD_SET(0, &set);
380        if (fd >= 0)
381            FD_SET(fd, &set);
382        tv.tv_sec = 0;
383        tv.tv_usec = throttle ? throttle_us : 100000;
384        res = select(fd < 0 ? 1 : fd+1, &set, NULL, NULL, &tv);
385        if (res < 0) {
386            perror("select");
387            return 1;
388        }
389        if (!res)
390            throttle = 0;
391        if (FD_ISSET(0, &set)) {
392            if (throttle_us)
393                throttle = 1;
394            if (!copy(0, fd, 1, throttle_us != 0))
395                goto failed;
396        }
397        if (fd >= 0 && FD_ISSET(fd, &set))
398            if (!copy(fd, 1, 0, 0))
399                goto failed;
400        continue;
401
402failed:
403        write_string("\r\n[Closed]\r\n");
404        (void) close(fd);
405        fd = -1;
406    }
407    return 0;
408}
409

Archive Download this file

Branches:
master



interactive