1 /* $OpenBSD: ldattach.c,v 1.11 2008/06/12 03:52:48 mbalmer 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[0].events = POLLRDNORM; 74 pfd[1].fd = pty; 75 pfd[1].events = POLLRDNORM; 76 77 while (!dying) { 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 { 210 syslog(LOG_ERR, "unknown line discipline %s", disc); 211 goto bail_out; 212 } 213 214 if ((fd = open(dev, O_RDWR)) < 0) { 215 syslog(LOG_ERR, "can't open %s", dev); 216 goto bail_out; 217 } 218 219 /* 220 * Get the current line attributes, modify only values that are 221 * either requested on the command line or that are needed by 222 * the line discipline (e.g. nmea has a default baudrate of 223 * 4800 instead of 9600). 224 */ 225 if (tcgetattr(fd, &tty) < 0) { 226 if (ppid != 1) 227 warnx("tcgetattr"); 228 goto bail_out; 229 } 230 231 232 if (bits == 7) { 233 tty.c_cflag &= ~CS8; 234 tty.c_cflag |= CS7; 235 } else if (bits == 8) { 236 tty.c_cflag &= ~CS7; 237 tty.c_cflag |= CS8; 238 } 239 240 if (parity != 0) 241 tty.c_cflag |= PARENB; 242 if (parity == 'o') 243 tty.c_cflag |= PARODD; 244 else 245 tty.c_cflag &= ~PARODD; 246 247 if (stop == 2) 248 tty.c_cflag |= CSTOPB; 249 else 250 tty.c_cflag &= ~CSTOPB; 251 252 if (flowcl) 253 tty.c_cflag |= CRTSCTS; 254 255 if (hupcl == 0) 256 tty.c_cflag &= ~HUPCL; 257 258 if (speed != 0) 259 cfsetspeed(&tty, speed); 260 261 /* setup common to all line disciplines */ 262 if (ioctl(fd, TIOCSDTR, 0) < 0) 263 warn("TIOCSDTR"); 264 if (ioctl(fd, TIOCSETD, &ldisc) < 0) { 265 syslog(LOG_ERR, "can't attach %s line discipline on %s", disc, 266 dev); 267 goto bail_out; 268 } 269 270 /* line discpline specific setup */ 271 switch (ldisc) { 272 case NMEADISC: 273 if (ioctl(fd, TIOCSTSTAMP, &tstamps) < 0) { 274 warnx("TIOCSTSTAMP"); 275 goto bail_out; 276 } 277 /* FALLTHROUGH */ 278 case SLIPDISC: 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) < 0) { 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