1 /* $OpenBSD: ldattach.c,v 1.18 2019/06/28 13:32:44 deraadt Exp $ */ 2 3 /* 4 * Copyright (c) 2007, 2008 Marc Balmer <mbalmer@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 USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* 20 * Attach a line disciplines to a tty(4) device either from the commandline 21 * or from init(8) (using entries in /etc/ttys). Optionally pass the data 22 * received on the tty(4) device to the master device of a pty(4) pair. 23 */ 24 25 #include <sys/types.h> 26 #include <sys/ioctl.h> 27 #include <sys/limits.h> 28 #include <sys/socket.h> 29 #include <sys/stat.h> 30 #include <sys/ioctl.h> 31 32 #include <err.h> 33 #include <errno.h> 34 #include <fcntl.h> 35 #include <paths.h> 36 #include <poll.h> 37 #include <signal.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <syslog.h> 42 #include <termios.h> 43 #include <unistd.h> 44 #include <util.h> 45 46 #include "atomicio.h" 47 48 __dead void usage(void); 49 void relay(int, int); 50 void coroner(int); 51 52 volatile sig_atomic_t dying = 0; 53 54 __dead void 55 usage(void) 56 { 57 extern char *__progname; 58 59 fprintf(stderr, "usage: %s [-27dehmop] [-s baudrate] " 60 "[-t cond] discipline device\n", __progname); 61 exit(1); 62 } 63 64 /* relay data between two file descriptors */ 65 void 66 relay(int device, int pty) 67 { 68 struct pollfd pfd[2]; 69 int nfds, n, nread; 70 char buf[128]; 71 72 pfd[0].fd = device; 73 pfd[1].fd = pty; 74 75 while (!dying) { 76 pfd[0].events = POLLRDNORM; 77 pfd[1].events = POLLRDNORM; 78 nfds = poll(pfd, 2, INFTIM); 79 if (nfds == -1) { 80 syslog(LOG_ERR, "polling error"); 81 exit(1); 82 } 83 if (nfds == 0) /* should not happen */ 84 continue; 85 86 if (pfd[1].revents & POLLHUP) { /* slave device not connected */ 87 sleep(1); 88 continue; 89 } 90 91 for (n = 0; n < 2; n++) { 92 if (!(pfd[n].revents & POLLRDNORM)) 93 continue; 94 95 nread = read(pfd[n].fd, buf, sizeof(buf)); 96 if (nread == -1) { 97 syslog(LOG_ERR, "error reading from %s: %m", 98 n ? "pty" : "device"); 99 exit(1); 100 } 101 if (nread == 0) { 102 syslog(LOG_ERR, "eof during read from %s: %m", 103 n ? "pty" : "device"); 104 exit(1); 105 } 106 atomicio(vwrite, pfd[1 - n].fd, buf, nread); 107 } 108 } 109 } 110 111 int 112 main(int argc, char *argv[]) 113 { 114 struct termios tty; 115 struct tstamps tstamps; 116 const char *errstr; 117 sigset_t sigset; 118 pid_t ppid; 119 int ch, fd, master = -1, slave, pty = 0, ldisc, nodaemon = 0; 120 int bits = 0, parity = 0, stop = 0, flowcl = 0, hupcl = 1; 121 speed_t speed = 0; 122 char devn[32], ptyn[32], *dev, *disc; 123 124 tstamps.ts_set = tstamps.ts_clr = 0; 125 126 if ((ppid = getppid()) == 1) 127 nodaemon = 1; 128 129 while ((ch = getopt(argc, argv, "27dehmops:t:")) != -1) { 130 switch (ch) { 131 case '2': 132 stop = 2; 133 break; 134 case '7': 135 bits = 7; 136 break; 137 case 'd': 138 nodaemon = 1; 139 break; 140 case 'e': 141 parity = 'e'; 142 break; 143 case 'h': 144 flowcl = 1; 145 break; 146 case 'm': 147 hupcl = 0; 148 break; 149 case 'o': 150 parity = 'o'; 151 break; 152 case 'p': 153 pty = 1; 154 break; 155 case 's': 156 speed = (speed_t)strtonum(optarg, 0, UINT_MAX, &errstr); 157 if (errstr) { 158 if (ppid != 1) 159 errx(1, "speed is %s: %s", errstr, 160 optarg); 161 else 162 goto bail_out; 163 } 164 break; 165 case 't': 166 if (!strcasecmp(optarg, "dcd")) 167 tstamps.ts_set |= TIOCM_CAR; 168 else if (!strcasecmp(optarg, "!dcd")) 169 tstamps.ts_clr |= TIOCM_CAR; 170 else if (!strcasecmp(optarg, "cts")) 171 tstamps.ts_set |= TIOCM_CTS; 172 else if (!strcasecmp(optarg, "!cts")) 173 tstamps.ts_clr |= TIOCM_CTS; 174 else { 175 if (ppid != 1) 176 errx(1, "'%s' not supported for " 177 "timestamping", optarg); 178 else 179 goto bail_out; 180 } 181 break; 182 default: 183 if (ppid != -1) 184 usage(); 185 } 186 } 187 argc -= optind; 188 argv += optind; 189 190 if (ppid != 1 && argc != 2) 191 usage(); 192 193 disc = *argv++; 194 dev = *argv; 195 if (strncmp(_PATH_DEV, dev, sizeof(_PATH_DEV) - 1)) { 196 (void)snprintf(devn, sizeof(devn), "%s%s", _PATH_DEV, dev); 197 dev = devn; 198 } 199 200 if (!strcmp(disc, "nmea")) { 201 ldisc = NMEADISC; 202 if (speed == 0) 203 speed = B4800; /* default is 4800 baud for nmea */ 204 } else if (!strcmp(disc, "msts")) { 205 ldisc = MSTSDISC; 206 } else if (!strcmp(disc, "endrun")) { 207 ldisc = ENDRUNDISC; 208 } else { 209 syslog(LOG_ERR, "unknown line discipline %s", disc); 210 goto bail_out; 211 } 212 213 if ((fd = open(dev, O_RDWR)) == -1) { 214 syslog(LOG_ERR, "can't open %s", dev); 215 goto bail_out; 216 } 217 218 /* 219 * Get the current line attributes, modify only values that are 220 * either requested on the command line or that are needed by 221 * the line discipline (e.g. nmea has a default baudrate of 222 * 4800 instead of 9600). 223 */ 224 if (tcgetattr(fd, &tty) == -1) { 225 if (ppid != 1) 226 warnx("tcgetattr"); 227 goto bail_out; 228 } 229 230 231 if (bits == 7) { 232 tty.c_cflag &= ~CS8; 233 tty.c_cflag |= CS7; 234 } else if (bits == 8) { 235 tty.c_cflag &= ~CS7; 236 tty.c_cflag |= CS8; 237 } 238 239 if (parity != 0) 240 tty.c_cflag |= PARENB; 241 if (parity == 'o') 242 tty.c_cflag |= PARODD; 243 else 244 tty.c_cflag &= ~PARODD; 245 246 if (stop == 2) 247 tty.c_cflag |= CSTOPB; 248 else 249 tty.c_cflag &= ~CSTOPB; 250 251 if (flowcl) 252 tty.c_cflag |= CRTSCTS; 253 254 if (hupcl == 0) 255 tty.c_cflag &= ~HUPCL; 256 257 if (speed != 0) 258 cfsetspeed(&tty, speed); 259 260 /* setup common to all line disciplines */ 261 if (ioctl(fd, TIOCSDTR, 0) == -1) 262 warn("TIOCSDTR"); 263 if (ioctl(fd, TIOCSETD, &ldisc) == -1) { 264 syslog(LOG_ERR, "can't attach %s line discipline on %s", disc, 265 dev); 266 goto bail_out; 267 } 268 269 /* line discpline specific setup */ 270 switch (ldisc) { 271 case NMEADISC: 272 case MSTSDISC: 273 case ENDRUNDISC: 274 if (ioctl(fd, TIOCSTSTAMP, &tstamps) == -1) { 275 warnx("TIOCSTSTAMP"); 276 goto bail_out; 277 } 278 tty.c_cflag |= CLOCAL; 279 tty.c_iflag = 0; 280 tty.c_lflag = 0; 281 tty.c_oflag = 0; 282 tty.c_cc[VMIN] = 1; 283 tty.c_cc[VTIME] = 0; 284 break; 285 } 286 287 /* finally set the line attributes */ 288 if (tcsetattr(fd, TCSADRAIN, &tty) == -1) { 289 if (ppid != 1) 290 warnx("tcsetattr"); 291 goto bail_out; 292 } 293 294 /* 295 * open a pty(4) pair to pass the data if the -p option has been 296 * given on the commandline. 297 */ 298 if (pty) { 299 if (openpty(&master, &slave, ptyn, NULL, NULL)) 300 errx(1, "can't open a pty"); 301 close(slave); 302 printf("%s\n", ptyn); 303 fflush(stdout); 304 } 305 if (nodaemon) 306 openlog("ldattach", LOG_PID | LOG_CONS | LOG_PERROR, 307 LOG_DAEMON); 308 else { 309 openlog("ldattach", LOG_PID | LOG_CONS, LOG_DAEMON); 310 if (daemon(0, 0)) 311 errx(1, "can't daemonize"); 312 } 313 314 syslog(LOG_INFO, "attach %s on %s", disc, dev); 315 signal(SIGHUP, coroner); 316 signal(SIGTERM, coroner); 317 318 if (master != -1) { 319 syslog(LOG_INFO, "passing data to %s", ptyn); 320 relay(fd, master); 321 } else { 322 sigemptyset(&sigset); 323 324 while (!dying) 325 sigsuspend(&sigset); 326 } 327 328 bail_out: 329 if (ppid == 1) 330 sleep(30); /* delay restart when called from init */ 331 332 return 0; 333 } 334 335 /* ARGSUSED */ 336 void 337 coroner(int useless) 338 { 339 dying = 1; 340 } 341