Annotation of src/usr.bin/cu/cu.c, Revision 1.1
1.1 ! nicm 1: /* $OpenBSD$ */
! 2:
! 3: /*
! 4: * Copyright (c) 2012 Nicholas Marriott <nicm@openbsd.org>
! 5: *
! 6: * Permission to use, copy, modify, and distribute this software for any
! 7: * purpose with or without fee is hereby granted, provided that the above
! 8: * copyright notice and this permission notice appear in all copies.
! 9: *
! 10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 14: * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
! 15: * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
! 16: * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 17: */
! 18:
! 19: #include <sys/param.h>
! 20: #include <sys/ioctl.h>
! 21:
! 22: #include <err.h>
! 23: #include <event.h>
! 24: #include <fcntl.h>
! 25: #include <getopt.h>
! 26: #include <paths.h>
! 27: #include <pwd.h>
! 28: #include <signal.h>
! 29: #include <stdio.h>
! 30: #include <stdlib.h>
! 31: #include <string.h>
! 32: #include <termios.h>
! 33: #include <unistd.h>
! 34:
! 35: #include "cu.h"
! 36:
! 37: extern char *__progname;
! 38:
! 39: struct termios saved_tio;
! 40: struct bufferevent *input_ev;
! 41: struct bufferevent *output_ev;
! 42: int line_fd;
! 43: struct bufferevent *line_ev;
! 44: struct event sigterm_ev;
! 45: enum {
! 46: STATE_NONE,
! 47: STATE_NEWLINE,
! 48: STATE_TILDE
! 49: } last_state = STATE_NEWLINE;
! 50:
! 51: __dead void usage(void);
! 52: void signal_event(int, short, void *);
! 53: void stream_read(struct bufferevent *, void *);
! 54: void stream_error(struct bufferevent *, short, void *);
! 55: void line_read(struct bufferevent *, void *);
! 56: void line_error(struct bufferevent *, short, void *);
! 57:
! 58: __dead void
! 59: usage(void)
! 60: {
! 61: fprintf(stderr, "usage: %s [-l line] [-s speed]\n", __progname);
! 62: exit(1);
! 63: }
! 64:
! 65: int
! 66: main(int argc, char **argv)
! 67: {
! 68: const char *line, *errstr;
! 69: char *tmp;
! 70: int opt, speed, i, ch;
! 71: struct termios tio;
! 72: static char sbuf[12];
! 73:
! 74: line = "/dev/cua00";
! 75: speed = 9600;
! 76:
! 77: /*
! 78: * Convert obsolescent -### speed to modern -s### syntax which getopt()
! 79: * can handle.
! 80: */
! 81: for (i = 1; i < argc; i++) {
! 82: if (argv[i][0] == '-') {
! 83: switch (argv[i][1]) {
! 84: case '0': case '1': case '2': case '3': case '4':
! 85: case '5': case '6': case '7': case '8': case '9':
! 86: ch = snprintf(sbuf, sizeof(sbuf), "-s%s",
! 87: &argv[i][1]);
! 88: if (ch <= 0 || ch >= (int)sizeof(sbuf)) {
! 89: errx(1, "invalid speed: %s",
! 90: &argv[i][1]);
! 91: }
! 92: argv[i] = sbuf;
! 93: break;
! 94: case '-':
! 95: /* if we get "--" stop processing args */
! 96: if (argv[i][2] == '\0')
! 97: goto getopt;
! 98: break;
! 99: }
! 100: }
! 101: }
! 102:
! 103: getopt:
! 104: while ((opt = getopt(argc, argv, "l:s:")) != -1) {
! 105: switch (opt) {
! 106: case 'l':
! 107: line = optarg;
! 108: break;
! 109: case 's':
! 110: speed = strtonum(optarg, 0, UINT_MAX, &errstr);
! 111: if (errstr != NULL)
! 112: errx(1, "speed is %s: %s", errstr, optarg);
! 113: break;
! 114: default:
! 115: usage();
! 116: }
! 117: }
! 118: argc -= optind;
! 119: argv += optind;
! 120: if (argc != 0)
! 121: usage();
! 122:
! 123: if (strchr(line, '/') == NULL) {
! 124: if (asprintf(&tmp, "%s%s", _PATH_DEV, line) == -1)
! 125: err(1, "asprintf");
! 126: line = tmp;
! 127: }
! 128:
! 129: line_fd = open(line, O_RDWR);
! 130: if (line_fd < 0)
! 131: err(1, "open(\"%s\")", line);
! 132: if (ioctl(line_fd, TIOCEXCL) != 0)
! 133: err(1, "ioctl(TIOCEXCL)");
! 134:
! 135: cfmakeraw(&tio);
! 136: tio.c_iflag = 0;
! 137: tio.c_oflag = 0;
! 138: tio.c_lflag = 0;
! 139: tio.c_cflag = CREAD|CS8;
! 140: tio.c_cc[VMIN] = 1;
! 141: tio.c_cc[VTIME] = 0;
! 142: cfsetspeed(&tio, speed);
! 143: if (tcsetattr(line_fd, TCSAFLUSH, &tio) != 0)
! 144: err(1, "tcsetattr");
! 145:
! 146: if (isatty(STDIN_FILENO) && tcgetattr(STDIN_FILENO, &saved_tio) != 0)
! 147: err(1, "tcgetattr");
! 148:
! 149: event_init();
! 150: signal_set(&sigterm_ev, SIGTERM, signal_event, NULL);
! 151: signal_add(&sigterm_ev, NULL);
! 152:
! 153: set_termios();
! 154:
! 155: /* stdin and stdout get separate events */
! 156: input_ev = bufferevent_new(STDIN_FILENO, stream_read, NULL,
! 157: stream_error, NULL);
! 158: bufferevent_enable(input_ev, EV_READ);
! 159: output_ev = bufferevent_new(STDOUT_FILENO, NULL, NULL, stream_error,
! 160: NULL);
! 161: bufferevent_enable(output_ev, EV_WRITE);
! 162:
! 163: line_ev = bufferevent_new(line_fd, line_read, NULL, line_error,
! 164: NULL);
! 165: bufferevent_enable(line_ev, EV_READ|EV_WRITE);
! 166:
! 167: printf("Connected (speed %u)\r\n", speed);
! 168: event_dispatch();
! 169:
! 170: if (isatty(STDIN_FILENO))
! 171: tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_tio);
! 172: printf("\r\n[EOT]\n");
! 173:
! 174: exit(0);
! 175: }
! 176:
! 177: void
! 178: signal_event(int fd, short events, void *data)
! 179: {
! 180: if (isatty(STDIN_FILENO))
! 181: tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_tio);
! 182: printf("\r\n[SIG%s]\n", sys_signame[fd]);
! 183:
! 184: exit(0);
! 185: }
! 186:
! 187: void
! 188: set_termios(void)
! 189: {
! 190: struct termios tio;
! 191:
! 192: if (!isatty(STDIN_FILENO))
! 193: return;
! 194:
! 195: memcpy(&tio, &saved_tio, sizeof(tio));
! 196: tio.c_lflag &= ~(ICANON|IEXTEN|ECHO);
! 197: tio.c_iflag &= ~(INPCK|ICRNL);
! 198: tio.c_oflag &= ~OPOST;
! 199: tio.c_cc[VMIN] = 1;
! 200: tio.c_cc[VTIME] = 0;
! 201: tio.c_cc[VDISCARD] = _POSIX_VDISABLE;
! 202: tio.c_cc[VDSUSP] = _POSIX_VDISABLE;
! 203: tio.c_cc[VINTR] = _POSIX_VDISABLE;
! 204: tio.c_cc[VLNEXT] = _POSIX_VDISABLE;
! 205: tio.c_cc[VQUIT] = _POSIX_VDISABLE;
! 206: tio.c_cc[VSUSP] = _POSIX_VDISABLE;
! 207: if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio) != 0)
! 208: err(1, "tcsetattr");
! 209: }
! 210:
! 211: void
! 212: restore_termios(void)
! 213: {
! 214: if (!isatty(STDIN_FILENO))
! 215: return;
! 216:
! 217: if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_tio) != 0)
! 218: err(1, "tcsetattr");
! 219: }
! 220:
! 221: void
! 222: stream_read(struct bufferevent *bufev, void *data)
! 223: {
! 224: char *new_data, *ptr;
! 225: size_t new_size;
! 226: int state_change;
! 227:
! 228: new_data = EVBUFFER_DATA(input_ev->input);
! 229: new_size = EVBUFFER_LENGTH(input_ev->input);
! 230: if (new_size == 0)
! 231: return;
! 232:
! 233: state_change = isatty(STDIN_FILENO);
! 234: for (ptr = new_data; ptr < new_data + new_size; ptr++) {
! 235: switch (last_state) {
! 236: case STATE_NONE:
! 237: if (state_change && *ptr == '\r')
! 238: last_state = STATE_NEWLINE;
! 239: break;
! 240: case STATE_NEWLINE:
! 241: if (state_change && *ptr == '~') {
! 242: last_state = STATE_TILDE;
! 243: continue;
! 244: }
! 245: if (*ptr != '\r')
! 246: last_state = STATE_NONE;
! 247: break;
! 248: case STATE_TILDE:
! 249: do_command(*ptr);
! 250: last_state = STATE_NEWLINE;
! 251: continue;
! 252: }
! 253:
! 254: bufferevent_write(line_ev, ptr, 1);
! 255: }
! 256:
! 257: evbuffer_drain(input_ev->input, new_size);
! 258: }
! 259:
! 260: void
! 261: stream_error(struct bufferevent *bufev, short what, void *data)
! 262: {
! 263: event_loopexit(NULL);
! 264: }
! 265:
! 266: void
! 267: line_read(struct bufferevent *bufev, void *data)
! 268: {
! 269: char *new_data;
! 270: size_t new_size;
! 271:
! 272: new_data = EVBUFFER_DATA(line_ev->input);
! 273: new_size = EVBUFFER_LENGTH(line_ev->input);
! 274: if (new_size == 0)
! 275: return;
! 276:
! 277: bufferevent_write(output_ev, new_data, new_size);
! 278:
! 279: evbuffer_drain(line_ev->input, new_size);
! 280: }
! 281:
! 282: void
! 283: line_error(struct bufferevent *bufev, short what, void *data)
! 284: {
! 285: event_loopexit(NULL);
! 286: }
! 287:
! 288: /* Expands tildes in the file name. Based on code from ssh/misc.c. */
! 289: char *
! 290: tilde_expand(const char *filename1)
! 291: {
! 292: const char *filename, *path;
! 293: char user[128], ret[MAXPATHLEN], *out;
! 294: struct passwd *pw;
! 295: u_int len, slash;
! 296:
! 297: if (*filename1 != '~')
! 298: goto no_change;
! 299: filename = filename1 + 1;
! 300:
! 301: path = strchr(filename, '/');
! 302: if (path != NULL && path > filename) { /* ~user/path */
! 303: slash = path - filename;
! 304: if (slash > sizeof(user) - 1)
! 305: goto no_change;
! 306: memcpy(user, filename, slash);
! 307: user[slash] = '\0';
! 308: if ((pw = getpwnam(user)) == NULL)
! 309: goto no_change;
! 310: } else if ((pw = getpwuid(getuid())) == NULL) /* ~/path */
! 311: goto no_change;
! 312:
! 313: if (strlcpy(ret, pw->pw_dir, sizeof(ret)) >= sizeof(ret))
! 314: goto no_change;
! 315:
! 316: /* Make sure directory has a trailing '/' */
! 317: len = strlen(pw->pw_dir);
! 318: if ((len == 0 || pw->pw_dir[len - 1] != '/') &&
! 319: strlcat(ret, "/", sizeof(ret)) >= sizeof(ret))
! 320: goto no_change;
! 321:
! 322: /* Skip leading '/' from specified path */
! 323: if (path != NULL)
! 324: filename = path + 1;
! 325: if (strlcat(ret, filename, sizeof(ret)) >= sizeof(ret))
! 326: goto no_change;
! 327:
! 328: out = strdup(ret);
! 329: if (out == NULL)
! 330: err(1, "strdup");
! 331: return (out);
! 332:
! 333: no_change:
! 334: out = strdup(filename1);
! 335: if (out == NULL)
! 336: err(1, "strdup");
! 337: return (out);
! 338: }