1 /* $OpenBSD: ttylog.c,v 1.7 2019/07/09 15:09:38 bluhm Exp $ */ 2 3 /* 4 * Copyright (c) 2015 Alexander Bluhm <bluhm@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 #include <sys/ioctl.h> 20 #include <sys/sockio.h> 21 22 #include <errno.h> 23 #include <err.h> 24 #include <fcntl.h> 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <signal.h> 28 #include <string.h> 29 #include <termios.h> 30 #include <time.h> 31 #include <unistd.h> 32 #include <util.h> 33 #include <utmp.h> 34 35 __dead void usage(void); 36 void redirect(void); 37 void restore(void); 38 void timeout(int); 39 void terminate(int); 40 void iostdin(int); 41 42 FILE *lg; 43 char ptyname[16], *console, *username, *logfile, *tty; 44 int mfd, sfd; 45 46 __dead void 47 usage() 48 { 49 fprintf(stderr, "usage: %s /dev/console|username logfile\n", 50 getprogname()); 51 exit(2); 52 } 53 54 int 55 main(int argc, char *argv[]) 56 { 57 char buf[8192]; 58 struct sigaction act; 59 sigset_t set; 60 ssize_t n; 61 62 if (argc != 3) 63 usage(); 64 if (strcmp(argv[1], "/dev/console") == 0) 65 console = argv[1]; 66 else 67 username = argv[1]; 68 logfile = argv[2]; 69 70 sigemptyset(&set); 71 sigaddset(&set, SIGTERM); 72 sigaddset(&set, SIGIO); 73 if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) 74 err(1, "sigprocmask block init"); 75 76 if ((lg = fopen(logfile, "w")) == NULL) 77 err(1, "fopen %s", logfile); 78 if (setvbuf(lg, NULL, _IOLBF, 0) != 0) 79 err(1, "setlinebuf"); 80 81 memset(&act, 0, sizeof(act)); 82 act.sa_mask = set; 83 act.sa_flags = SA_RESTART; 84 act.sa_handler = terminate; 85 if (sigaction(SIGTERM, &act, NULL) == -1) 86 err(1, "sigaction SIGTERM"); 87 if (sigaction(SIGINT, &act, NULL) == -1) 88 err(1, "sigaction SIGINT"); 89 90 if (openpty(&mfd, &sfd, ptyname, NULL, NULL) == -1) 91 err(1, "openpty"); 92 fprintf(lg, "openpty %s\n", ptyname); 93 if ((tty = strrchr(ptyname, '/')) == NULL) 94 errx(1, "tty: %s", ptyname); 95 tty++; 96 97 /* login(3) searches for a controlling tty, use the created one */ 98 if (dup2(sfd, 1) == -1) 99 err(1, "dup2 stdout"); 100 101 redirect(); 102 103 act.sa_handler = iostdin; 104 if (sigaction(SIGIO, &act, NULL) == -1) 105 err(1, "sigaction SIGIO"); 106 if (setpgid(0, 0) == -1) 107 err(1, "setpgid"); 108 if (fcntl(0, F_SETOWN, getpid()) == -1) 109 err(1, "fcntl F_SETOWN"); 110 if (fcntl(0, F_SETFL, O_ASYNC) == -1) 111 err(1, "fcntl O_ASYNC"); 112 113 act.sa_handler = timeout; 114 if (sigaction(SIGALRM, &act, NULL) == -1) 115 err(1, "sigaction SIGALRM"); 116 if (alarm(30) == (unsigned int)-1) 117 err(1, "alarm"); 118 119 fprintf(lg, "%s: started\n", getprogname()); 120 121 if (sigprocmask(SIG_UNBLOCK, &set, NULL) == -1) 122 err(1, "sigprocmask unblock init"); 123 124 /* do not block signals during read, it has to be interrupted */ 125 while ((n = read(mfd, buf, sizeof(buf))) > 0) { 126 if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) 127 err(1, "sigprocmask block write"); 128 fprintf(lg, ">>> "); 129 if (fwrite(buf, 1, n, lg) != (size_t)n) 130 err(1, "fwrite %s", logfile); 131 if (buf[n-1] != '\n') 132 fprintf(lg, "\n"); 133 if (sigprocmask(SIG_UNBLOCK, &set, NULL) == -1) 134 err(1, "sigprocmask unblock write"); 135 } 136 if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) 137 err(1, "sigprocmask block exit"); 138 if (n < 0) 139 err(1, "read %s", ptyname); 140 fprintf(lg, "EOF %s\n", ptyname); 141 142 restore(); 143 144 errx(3, "EOF"); 145 } 146 147 void 148 redirect(void) 149 { 150 struct utmp utmp; 151 int fd, on; 152 153 if (console) { 154 /* first remove any existing console redirection */ 155 on = 0; 156 if ((fd = open("/dev/console", O_WRONLY)) == -1) 157 err(1, "open /dev/console"); 158 if (ioctl(fd, TIOCCONS, &on) == -1) 159 err(1, "ioctl TIOCCONS"); 160 close(fd); 161 /* then redirect console to our pseudo tty */ 162 on = 1; 163 if (ioctl(sfd, TIOCCONS, &on) == -1) 164 err(1, "ioctl TIOCCONS on"); 165 fprintf(lg, "console %s on %s\n", console, tty); 166 } 167 if (username) { 168 memset(&utmp, 0, sizeof(utmp)); 169 strlcpy(utmp.ut_line, tty, sizeof(utmp.ut_line)); 170 strlcpy(utmp.ut_name, username, sizeof(utmp.ut_name)); 171 time(&utmp.ut_time); 172 login(&utmp); 173 fprintf(lg, "login %s %s\n", username, tty); 174 } 175 } 176 177 void 178 restore(void) 179 { 180 int on; 181 182 if (tty == NULL) 183 return; 184 if (console) { 185 on = 0; 186 if (ioctl(sfd, TIOCCONS, &on) == -1) 187 err(1, "ioctl TIOCCONS off"); 188 fprintf(lg, "console %s off\n", tty); 189 } 190 if (username) { 191 if (logout(tty) == 0) 192 errx(1, "logout %s", tty); 193 fprintf(lg, "logout %s\n", tty); 194 } 195 } 196 197 void 198 timeout(int sig) 199 { 200 fprintf(lg, "signal timeout %d\n", sig); 201 restore(); 202 errx(3, "timeout"); 203 } 204 205 void 206 terminate(int sig) 207 { 208 fprintf(lg, "signal terminate %d\n", sig); 209 restore(); 210 errx(3, "terminate"); 211 } 212 213 void 214 iostdin(int sig) 215 { 216 char buf[8192]; 217 ssize_t n; 218 219 fprintf(lg, "signal iostdin %d\n", sig); 220 221 /* try to read as many log messages as possible before terminating */ 222 if (fcntl(mfd, F_SETFL, O_NONBLOCK) == -1) 223 err(1, "fcntl O_NONBLOCK"); 224 while ((n = read(mfd, buf, sizeof(buf))) > 0) { 225 fprintf(lg, ">>> "); 226 if (fwrite(buf, 1, n, lg) != (size_t)n) 227 err(1, "fwrite %s", logfile); 228 if (buf[n-1] != '\n') 229 fprintf(lg, "\n"); 230 } 231 if (n < 0 && errno != EAGAIN) 232 err(1, "read %s", ptyname); 233 234 if ((n = read(0, buf, sizeof(buf))) < 0) 235 err(1, "read stdin"); 236 restore(); 237 if (n > 0) 238 errx(3, "read stdin %zd bytes", n); 239 exit(0); 240 } 241