xref: /openbsd-src/regress/usr.sbin/syslogd/ttylog.c (revision ae3cb403620ab940fbaabb3055fac045a63d56b7)
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