1 /* $OpenBSD: ttylog.c,v 1.6 2017/09/11 18:21:08 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 *console, *username, *tty; 44 int 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], ptyname[16], *logfile; 58 struct sigaction act; 59 sigset_t set; 60 ssize_t n; 61 int mfd; 62 63 if (argc != 3) 64 usage(); 65 if (strcmp(argv[1], "/dev/console") == 0) 66 console = argv[1]; 67 else 68 username = argv[1]; 69 logfile = argv[2]; 70 71 sigemptyset(&set); 72 sigaddset(&set, SIGTERM); 73 sigaddset(&set, SIGIO); 74 if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) 75 err(1, "sigprocmask block init"); 76 77 if ((lg = fopen(logfile, "w")) == NULL) 78 err(1, "fopen %s", logfile); 79 if (setvbuf(lg, NULL, _IOLBF, 0) != 0) 80 err(1, "setlinebuf"); 81 82 memset(&act, 0, sizeof(act)); 83 act.sa_mask = set; 84 act.sa_flags = SA_RESTART; 85 act.sa_handler = terminate; 86 if (sigaction(SIGTERM, &act, NULL) == -1) 87 err(1, "sigaction SIGTERM"); 88 if (sigaction(SIGINT, &act, NULL) == -1) 89 err(1, "sigaction SIGINT"); 90 91 if (openpty(&mfd, &sfd, ptyname, NULL, NULL) == -1) 92 err(1, "openpty"); 93 fprintf(lg, "openpty %s\n", ptyname); 94 if ((tty = strrchr(ptyname, '/')) == NULL) 95 errx(1, "tty: %s", ptyname); 96 tty++; 97 98 /* login(3) searches for a controlling tty, use the created one */ 99 if (dup2(sfd, 1) == -1) 100 err(1, "dup2 stdout"); 101 102 redirect(); 103 104 act.sa_handler = iostdin; 105 if (sigaction(SIGIO, &act, NULL) == -1) 106 err(1, "sigaction SIGIO"); 107 if (setpgid(0, 0) == -1) 108 err(1, "setpgid"); 109 if (fcntl(0, F_SETOWN, getpid()) == -1) 110 err(1, "fcntl F_SETOWN"); 111 if (fcntl(0, F_SETFL, O_ASYNC) == -1) 112 err(1, "fcntl O_ASYNC"); 113 114 act.sa_handler = timeout; 115 if (sigaction(SIGALRM, &act, NULL) == -1) 116 err(1, "sigaction SIGALRM"); 117 if (alarm(30) == (unsigned int)-1) 118 err(1, "alarm"); 119 120 fprintf(lg, "%s: started\n", getprogname()); 121 122 if (sigprocmask(SIG_UNBLOCK, &set, NULL) == -1) 123 err(1, "sigprocmask unblock init"); 124 125 /* do not block signals during read, it has to be interrupted */ 126 while ((n = read(mfd, buf, sizeof(buf))) > 0) { 127 if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) 128 err(1, "sigprocmask block write"); 129 fprintf(lg, ">>> "); 130 if (fwrite(buf, 1, n, lg) != (size_t)n) 131 err(1, "fwrite %s", logfile); 132 if (buf[n-1] != '\n') 133 fprintf(lg, "\n"); 134 if (sigprocmask(SIG_UNBLOCK, &set, NULL) == -1) 135 err(1, "sigprocmask unblock write"); 136 } 137 if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) 138 err(1, "sigprocmask block exit"); 139 if (n < 0) 140 err(1, "read %s", ptyname); 141 fprintf(lg, "EOF %s\n", ptyname); 142 143 restore(); 144 145 errx(3, "EOF"); 146 } 147 148 void 149 redirect(void) 150 { 151 struct utmp utmp; 152 int fd, on; 153 154 if (console) { 155 /* first remove any existing console redirection */ 156 on = 0; 157 if ((fd = open("/dev/console", O_WRONLY)) == -1) 158 err(1, "open /dev/console"); 159 if (ioctl(fd, TIOCCONS, &on) == -1) 160 err(1, "ioctl TIOCCONS"); 161 close(fd); 162 /* then redirect console to our pseudo tty */ 163 on = 1; 164 if (ioctl(sfd, TIOCCONS, &on) == -1) 165 err(1, "ioctl TIOCCONS on"); 166 fprintf(lg, "console %s on %s\n", console, tty); 167 } 168 if (username) { 169 memset(&utmp, 0, sizeof(utmp)); 170 strlcpy(utmp.ut_line, tty, sizeof(utmp.ut_line)); 171 strlcpy(utmp.ut_name, username, sizeof(utmp.ut_name)); 172 time(&utmp.ut_time); 173 login(&utmp); 174 fprintf(lg, "login %s %s\n", username, tty); 175 } 176 } 177 178 void 179 restore(void) 180 { 181 int on; 182 183 if (tty == NULL) 184 return; 185 if (console) { 186 on = 0; 187 if (ioctl(sfd, TIOCCONS, &on) == -1) 188 err(1, "ioctl TIOCCONS off"); 189 fprintf(lg, "console %s off\n", tty); 190 } 191 if (username) { 192 if (logout(tty) == 0) 193 errx(1, "logout %s", tty); 194 fprintf(lg, "logout %s\n", tty); 195 } 196 } 197 198 void 199 timeout(int sig) 200 { 201 fprintf(lg, "signal timeout %d\n", sig); 202 restore(); 203 errx(3, "timeout"); 204 } 205 206 void 207 terminate(int sig) 208 { 209 fprintf(lg, "signal terminate %d\n", sig); 210 restore(); 211 errx(3, "terminate"); 212 } 213 214 void 215 iostdin(int sig) 216 { 217 char buf[8192]; 218 ssize_t n; 219 220 fprintf(lg, "signal iostdin %d\n", sig); 221 if ((n = read(0, buf, sizeof(buf))) < 0) 222 err(1, "read stdin"); 223 restore(); 224 if (n > 0) 225 errx(3, "read stdin %zd bytes", n); 226 exit(0); 227 } 228