1 /* $OpenBSD: ldattach.c,v 1.15 2014/08/10 02:09:35 guenther 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/ttycom.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, "slip")) { 201 bits = 8; /* make sure we use 8 databits */ 202 ldisc = SLIPDISC; 203 } else if (!strcmp(disc, "nmea")) { 204 ldisc = NMEADISC; 205 if (speed == 0) 206 speed = B4800; /* default is 4800 baud for nmea */ 207 } else if (!strcmp(disc, "msts")) { 208 ldisc = MSTSDISC; 209 } else if (!strcmp(disc, "endrun")) { 210 ldisc = ENDRUNDISC; 211 } else { 212 syslog(LOG_ERR, "unknown line discipline %s", disc); 213 goto bail_out; 214 } 215 216 if ((fd = open(dev, O_RDWR)) < 0) { 217 syslog(LOG_ERR, "can't open %s", dev); 218 goto bail_out; 219 } 220 221 /* 222 * Get the current line attributes, modify only values that are 223 * either requested on the command line or that are needed by 224 * the line discipline (e.g. nmea has a default baudrate of 225 * 4800 instead of 9600). 226 */ 227 if (tcgetattr(fd, &tty) < 0) { 228 if (ppid != 1) 229 warnx("tcgetattr"); 230 goto bail_out; 231 } 232 233 234 if (bits == 7) { 235 tty.c_cflag &= ~CS8; 236 tty.c_cflag |= CS7; 237 } else if (bits == 8) { 238 tty.c_cflag &= ~CS7; 239 tty.c_cflag |= CS8; 240 } 241 242 if (parity != 0) 243 tty.c_cflag |= PARENB; 244 if (parity == 'o') 245 tty.c_cflag |= PARODD; 246 else 247 tty.c_cflag &= ~PARODD; 248 249 if (stop == 2) 250 tty.c_cflag |= CSTOPB; 251 else 252 tty.c_cflag &= ~CSTOPB; 253 254 if (flowcl) 255 tty.c_cflag |= CRTSCTS; 256 257 if (hupcl == 0) 258 tty.c_cflag &= ~HUPCL; 259 260 if (speed != 0) 261 cfsetspeed(&tty, speed); 262 263 /* setup common to all line disciplines */ 264 if (ioctl(fd, TIOCSDTR, 0) < 0) 265 warn("TIOCSDTR"); 266 if (ioctl(fd, TIOCSETD, &ldisc) < 0) { 267 syslog(LOG_ERR, "can't attach %s line discipline on %s", disc, 268 dev); 269 goto bail_out; 270 } 271 272 /* line discpline specific setup */ 273 switch (ldisc) { 274 case NMEADISC: 275 case MSTSDISC: 276 case ENDRUNDISC: 277 if (ioctl(fd, TIOCSTSTAMP, &tstamps) < 0) { 278 warnx("TIOCSTSTAMP"); 279 goto bail_out; 280 } 281 tty.c_cflag |= CLOCAL; 282 /* FALLTHROUGH */ 283 case SLIPDISC: 284 tty.c_iflag = 0; 285 tty.c_lflag = 0; 286 tty.c_oflag = 0; 287 tty.c_cc[VMIN] = 1; 288 tty.c_cc[VTIME] = 0; 289 break; 290 } 291 292 /* finally set the line attributes */ 293 if (tcsetattr(fd, TCSADRAIN, &tty) < 0) { 294 if (ppid != 1) 295 warnx("tcsetattr"); 296 goto bail_out; 297 } 298 299 /* 300 * open a pty(4) pair to pass the data if the -p option has been 301 * given on the commandline. 302 */ 303 if (pty) { 304 if (openpty(&master, &slave, ptyn, NULL, NULL)) 305 errx(1, "can't open a pty"); 306 close(slave); 307 printf("%s\n", ptyn); 308 fflush(stdout); 309 } 310 if (nodaemon) 311 openlog("ldattach", LOG_PID | LOG_CONS | LOG_PERROR, 312 LOG_DAEMON); 313 else { 314 openlog("ldattach", LOG_PID | LOG_CONS, LOG_DAEMON); 315 if (daemon(0, 0)) 316 errx(1, "can't daemonize"); 317 } 318 319 syslog(LOG_INFO, "attach %s on %s", disc, dev); 320 signal(SIGHUP, coroner); 321 signal(SIGTERM, coroner); 322 323 if (master != -1) { 324 syslog(LOG_INFO, "passing data to %s", ptyn); 325 relay(fd, master); 326 } else { 327 sigemptyset(&sigset); 328 329 while (!dying) 330 sigsuspend(&sigset); 331 } 332 333 bail_out: 334 if (ppid == 1) 335 sleep(30); /* delay restart when called from init */ 336 337 return 0; 338 } 339 340 /* ARGSUSED */ 341 void 342 coroner(int useless) 343 { 344 dying = 1; 345 } 346