1*5a9255e7Sbluhm /* $OpenBSD: ttylog.c,v 1.8 2021/07/06 11:50:34 bluhm Exp $ */
2ef01d180Sbluhm
37b30e940Sbluhm /*
47b30e940Sbluhm * Copyright (c) 2015 Alexander Bluhm <bluhm@openbsd.org>
57b30e940Sbluhm *
67b30e940Sbluhm * Permission to use, copy, modify, and distribute this software for any
77b30e940Sbluhm * purpose with or without fee is hereby granted, provided that the above
87b30e940Sbluhm * copyright notice and this permission notice appear in all copies.
97b30e940Sbluhm *
107b30e940Sbluhm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
117b30e940Sbluhm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
127b30e940Sbluhm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
137b30e940Sbluhm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
147b30e940Sbluhm * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
157b30e940Sbluhm * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
167b30e940Sbluhm * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
177b30e940Sbluhm */
187b30e940Sbluhm
197b30e940Sbluhm #include <sys/ioctl.h>
207b30e940Sbluhm #include <sys/sockio.h>
217b30e940Sbluhm
227b30e940Sbluhm #include <errno.h>
237b30e940Sbluhm #include <err.h>
247b30e940Sbluhm #include <fcntl.h>
257b30e940Sbluhm #include <stdio.h>
267b30e940Sbluhm #include <stdlib.h>
277b30e940Sbluhm #include <signal.h>
287b30e940Sbluhm #include <string.h>
297b30e940Sbluhm #include <termios.h>
307b30e940Sbluhm #include <time.h>
317b30e940Sbluhm #include <unistd.h>
327b30e940Sbluhm #include <util.h>
337b30e940Sbluhm #include <utmp.h>
347b30e940Sbluhm
357b30e940Sbluhm __dead void usage(void);
3629e70a6fSbluhm void redirect(void);
3729e70a6fSbluhm void restore(void);
387b30e940Sbluhm void timeout(int);
397b30e940Sbluhm void terminate(int);
407b30e940Sbluhm void iostdin(int);
417b30e940Sbluhm
427b30e940Sbluhm FILE *lg;
43e2237327Sbluhm char ptyname[16], *console, *username, *logfile, *tty;
44e2237327Sbluhm int mfd, sfd;
457b30e940Sbluhm
467b30e940Sbluhm __dead void
usage()477b30e940Sbluhm usage()
487b30e940Sbluhm {
4929e70a6fSbluhm fprintf(stderr, "usage: %s /dev/console|username logfile\n",
5029e70a6fSbluhm getprogname());
517b30e940Sbluhm exit(2);
527b30e940Sbluhm }
537b30e940Sbluhm
547b30e940Sbluhm int
main(int argc,char * argv[])557b30e940Sbluhm main(int argc, char *argv[])
567b30e940Sbluhm {
57e2237327Sbluhm char buf[8192];
58c3c9e563Sbluhm struct sigaction act;
59c3c9e563Sbluhm sigset_t set;
607b30e940Sbluhm ssize_t n;
617b30e940Sbluhm
627b30e940Sbluhm if (argc != 3)
637b30e940Sbluhm usage();
6429e70a6fSbluhm if (strcmp(argv[1], "/dev/console") == 0)
6529e70a6fSbluhm console = argv[1];
6629e70a6fSbluhm else
677b30e940Sbluhm username = argv[1];
687b30e940Sbluhm logfile = argv[2];
697b30e940Sbluhm
70c3c9e563Sbluhm sigemptyset(&set);
71c3c9e563Sbluhm sigaddset(&set, SIGTERM);
72c3c9e563Sbluhm sigaddset(&set, SIGIO);
73c3c9e563Sbluhm if (sigprocmask(SIG_BLOCK, &set, NULL) == -1)
74c3c9e563Sbluhm err(1, "sigprocmask block init");
75c3c9e563Sbluhm
767b30e940Sbluhm if ((lg = fopen(logfile, "w")) == NULL)
777b30e940Sbluhm err(1, "fopen %s", logfile);
78b6915e4eStedu if (setvbuf(lg, NULL, _IOLBF, 0) != 0)
797b30e940Sbluhm err(1, "setlinebuf");
807b30e940Sbluhm
81c3c9e563Sbluhm memset(&act, 0, sizeof(act));
82c3c9e563Sbluhm act.sa_mask = set;
83c3c9e563Sbluhm act.sa_flags = SA_RESTART;
84c3c9e563Sbluhm act.sa_handler = terminate;
85c3c9e563Sbluhm if (sigaction(SIGTERM, &act, NULL) == -1)
86c3c9e563Sbluhm err(1, "sigaction SIGTERM");
87c3c9e563Sbluhm if (sigaction(SIGINT, &act, NULL) == -1)
88c3c9e563Sbluhm err(1, "sigaction SIGINT");
897b30e940Sbluhm
907b30e940Sbluhm if (openpty(&mfd, &sfd, ptyname, NULL, NULL) == -1)
917b30e940Sbluhm err(1, "openpty");
927b30e940Sbluhm fprintf(lg, "openpty %s\n", ptyname);
937b30e940Sbluhm if ((tty = strrchr(ptyname, '/')) == NULL)
947b30e940Sbluhm errx(1, "tty: %s", ptyname);
957b30e940Sbluhm tty++;
967b30e940Sbluhm
977b30e940Sbluhm /* login(3) searches for a controlling tty, use the created one */
987b30e940Sbluhm if (dup2(sfd, 1) == -1)
997b30e940Sbluhm err(1, "dup2 stdout");
1007b30e940Sbluhm
10129e70a6fSbluhm redirect();
1027b30e940Sbluhm
103c3c9e563Sbluhm act.sa_handler = iostdin;
104c3c9e563Sbluhm if (sigaction(SIGIO, &act, NULL) == -1)
105c3c9e563Sbluhm err(1, "sigaction SIGIO");
1067b30e940Sbluhm if (setpgid(0, 0) == -1)
1077b30e940Sbluhm err(1, "setpgid");
108c3c9e563Sbluhm if (fcntl(0, F_SETOWN, getpid()) == -1)
109c3c9e563Sbluhm err(1, "fcntl F_SETOWN");
1107b30e940Sbluhm if (fcntl(0, F_SETFL, O_ASYNC) == -1)
1117b30e940Sbluhm err(1, "fcntl O_ASYNC");
1127b30e940Sbluhm
113c3c9e563Sbluhm act.sa_handler = timeout;
114c3c9e563Sbluhm if (sigaction(SIGALRM, &act, NULL) == -1)
115c3c9e563Sbluhm err(1, "sigaction SIGALRM");
116*5a9255e7Sbluhm alarm(30);
1177b30e940Sbluhm
1187b30e940Sbluhm fprintf(lg, "%s: started\n", getprogname());
1197b30e940Sbluhm
120c3c9e563Sbluhm if (sigprocmask(SIG_UNBLOCK, &set, NULL) == -1)
121c3c9e563Sbluhm err(1, "sigprocmask unblock init");
122c3c9e563Sbluhm
123c3c9e563Sbluhm /* do not block signals during read, it has to be interrupted */
1247b30e940Sbluhm while ((n = read(mfd, buf, sizeof(buf))) > 0) {
125c3c9e563Sbluhm if (sigprocmask(SIG_BLOCK, &set, NULL) == -1)
126c3c9e563Sbluhm err(1, "sigprocmask block write");
1277b30e940Sbluhm fprintf(lg, ">>> ");
1287b30e940Sbluhm if (fwrite(buf, 1, n, lg) != (size_t)n)
1297b30e940Sbluhm err(1, "fwrite %s", logfile);
1307b30e940Sbluhm if (buf[n-1] != '\n')
1317b30e940Sbluhm fprintf(lg, "\n");
132c3c9e563Sbluhm if (sigprocmask(SIG_UNBLOCK, &set, NULL) == -1)
133c3c9e563Sbluhm err(1, "sigprocmask unblock write");
1347b30e940Sbluhm }
135c3c9e563Sbluhm if (sigprocmask(SIG_BLOCK, &set, NULL) == -1)
136c3c9e563Sbluhm err(1, "sigprocmask block exit");
1377b30e940Sbluhm if (n < 0)
1387b30e940Sbluhm err(1, "read %s", ptyname);
1397b30e940Sbluhm fprintf(lg, "EOF %s\n", ptyname);
1407b30e940Sbluhm
14129e70a6fSbluhm restore();
14229e70a6fSbluhm
14329e70a6fSbluhm errx(3, "EOF");
14429e70a6fSbluhm }
14529e70a6fSbluhm
14629e70a6fSbluhm void
redirect(void)14729e70a6fSbluhm redirect(void)
14829e70a6fSbluhm {
14929e70a6fSbluhm struct utmp utmp;
15029e70a6fSbluhm int fd, on;
15129e70a6fSbluhm
15229e70a6fSbluhm if (console) {
15329e70a6fSbluhm /* first remove any existing console redirection */
15429e70a6fSbluhm on = 0;
15529e70a6fSbluhm if ((fd = open("/dev/console", O_WRONLY)) == -1)
15629e70a6fSbluhm err(1, "open /dev/console");
15729e70a6fSbluhm if (ioctl(fd, TIOCCONS, &on) == -1)
15829e70a6fSbluhm err(1, "ioctl TIOCCONS");
15929e70a6fSbluhm close(fd);
16029e70a6fSbluhm /* then redirect console to our pseudo tty */
16129e70a6fSbluhm on = 1;
16229e70a6fSbluhm if (ioctl(sfd, TIOCCONS, &on) == -1)
16329e70a6fSbluhm err(1, "ioctl TIOCCONS on");
16429e70a6fSbluhm fprintf(lg, "console %s on %s\n", console, tty);
16529e70a6fSbluhm }
16629e70a6fSbluhm if (username) {
16729e70a6fSbluhm memset(&utmp, 0, sizeof(utmp));
16829e70a6fSbluhm strlcpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
16929e70a6fSbluhm strlcpy(utmp.ut_name, username, sizeof(utmp.ut_name));
17029e70a6fSbluhm time(&utmp.ut_time);
17129e70a6fSbluhm login(&utmp);
17229e70a6fSbluhm fprintf(lg, "login %s %s\n", username, tty);
17329e70a6fSbluhm }
17429e70a6fSbluhm }
17529e70a6fSbluhm
17629e70a6fSbluhm void
restore(void)17729e70a6fSbluhm restore(void)
17829e70a6fSbluhm {
17929e70a6fSbluhm int on;
18029e70a6fSbluhm
18129e70a6fSbluhm if (tty == NULL)
18229e70a6fSbluhm return;
18329e70a6fSbluhm if (console) {
18429e70a6fSbluhm on = 0;
18529e70a6fSbluhm if (ioctl(sfd, TIOCCONS, &on) == -1)
18629e70a6fSbluhm err(1, "ioctl TIOCCONS off");
18729e70a6fSbluhm fprintf(lg, "console %s off\n", tty);
18829e70a6fSbluhm }
18929e70a6fSbluhm if (username) {
1907b30e940Sbluhm if (logout(tty) == 0)
1917b30e940Sbluhm errx(1, "logout %s", tty);
1927b30e940Sbluhm fprintf(lg, "logout %s\n", tty);
19329e70a6fSbluhm }
1947b30e940Sbluhm }
1957b30e940Sbluhm
1967b30e940Sbluhm void
timeout(int sig)1977b30e940Sbluhm timeout(int sig)
1987b30e940Sbluhm {
1997b30e940Sbluhm fprintf(lg, "signal timeout %d\n", sig);
20029e70a6fSbluhm restore();
2017b30e940Sbluhm errx(3, "timeout");
2027b30e940Sbluhm }
2037b30e940Sbluhm
2047b30e940Sbluhm void
terminate(int sig)2057b30e940Sbluhm terminate(int sig)
2067b30e940Sbluhm {
2077b30e940Sbluhm fprintf(lg, "signal terminate %d\n", sig);
20829e70a6fSbluhm restore();
2097b30e940Sbluhm errx(3, "terminate");
2107b30e940Sbluhm }
2117b30e940Sbluhm
2127b30e940Sbluhm void
iostdin(int sig)2137b30e940Sbluhm iostdin(int sig)
2147b30e940Sbluhm {
2157b30e940Sbluhm char buf[8192];
216b2c23601Sbluhm ssize_t n;
2177b30e940Sbluhm
2187b30e940Sbluhm fprintf(lg, "signal iostdin %d\n", sig);
219e2237327Sbluhm
220e2237327Sbluhm /* try to read as many log messages as possible before terminating */
221e2237327Sbluhm if (fcntl(mfd, F_SETFL, O_NONBLOCK) == -1)
222e2237327Sbluhm err(1, "fcntl O_NONBLOCK");
223e2237327Sbluhm while ((n = read(mfd, buf, sizeof(buf))) > 0) {
224e2237327Sbluhm fprintf(lg, ">>> ");
225e2237327Sbluhm if (fwrite(buf, 1, n, lg) != (size_t)n)
226e2237327Sbluhm err(1, "fwrite %s", logfile);
227e2237327Sbluhm if (buf[n-1] != '\n')
228e2237327Sbluhm fprintf(lg, "\n");
229e2237327Sbluhm }
230e2237327Sbluhm if (n < 0 && errno != EAGAIN)
231e2237327Sbluhm err(1, "read %s", ptyname);
232e2237327Sbluhm
2337b30e940Sbluhm if ((n = read(0, buf, sizeof(buf))) < 0)
2347b30e940Sbluhm err(1, "read stdin");
23529e70a6fSbluhm restore();
2367b30e940Sbluhm if (n > 0)
2377b30e940Sbluhm errx(3, "read stdin %zd bytes", n);
2387b30e940Sbluhm exit(0);
2397b30e940Sbluhm }
240