xref: /openbsd/regress/usr.sbin/syslogd/ttylog.c (revision d89ec533)
1 /*	$OpenBSD: ttylog.c,v 1.8 2021/07/06 11:50:34 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 	alarm(30);
117 
118 	fprintf(lg, "%s: started\n", getprogname());
119 
120 	if (sigprocmask(SIG_UNBLOCK, &set, NULL) == -1)
121 		err(1, "sigprocmask unblock init");
122 
123 	/* do not block signals during read, it has to be interrupted */
124 	while ((n = read(mfd, buf, sizeof(buf))) > 0) {
125 		if (sigprocmask(SIG_BLOCK, &set, NULL) == -1)
126 			err(1, "sigprocmask block write");
127 		fprintf(lg, ">>> ");
128 		if (fwrite(buf, 1, n, lg) != (size_t)n)
129 			err(1, "fwrite %s", logfile);
130 		if (buf[n-1] != '\n')
131 			fprintf(lg, "\n");
132 		if (sigprocmask(SIG_UNBLOCK, &set, NULL) == -1)
133 			err(1, "sigprocmask unblock write");
134 	}
135 	if (sigprocmask(SIG_BLOCK, &set, NULL) == -1)
136 		err(1, "sigprocmask block exit");
137 	if (n < 0)
138 		err(1, "read %s", ptyname);
139 	fprintf(lg, "EOF %s\n", ptyname);
140 
141 	restore();
142 
143 	errx(3, "EOF");
144 }
145 
146 void
147 redirect(void)
148 {
149 	struct utmp utmp;
150 	int fd, on;
151 
152 	if (console) {
153 		/* first remove any existing console redirection */
154 		on = 0;
155 		if ((fd = open("/dev/console", O_WRONLY)) == -1)
156 			err(1, "open /dev/console");
157 		if (ioctl(fd, TIOCCONS, &on) == -1)
158 			err(1, "ioctl TIOCCONS");
159 		close(fd);
160 		/* then redirect console to our pseudo tty */
161 		on = 1;
162 		if (ioctl(sfd, TIOCCONS, &on) == -1)
163 			err(1, "ioctl TIOCCONS on");
164 		fprintf(lg, "console %s on %s\n", console, tty);
165 	}
166 	if (username) {
167 		memset(&utmp, 0, sizeof(utmp));
168 		strlcpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
169 		strlcpy(utmp.ut_name, username, sizeof(utmp.ut_name));
170 		time(&utmp.ut_time);
171 		login(&utmp);
172 		fprintf(lg, "login %s %s\n", username, tty);
173 	}
174 }
175 
176 void
177 restore(void)
178 {
179 	int on;
180 
181 	if (tty == NULL)
182 		return;
183 	if (console) {
184 		on = 0;
185 		if (ioctl(sfd, TIOCCONS, &on) == -1)
186 			err(1, "ioctl TIOCCONS off");
187 		fprintf(lg, "console %s off\n", tty);
188 	}
189 	if (username) {
190 		if (logout(tty) == 0)
191 			errx(1, "logout %s", tty);
192 		fprintf(lg, "logout %s\n", tty);
193 	}
194 }
195 
196 void
197 timeout(int sig)
198 {
199 	fprintf(lg, "signal timeout %d\n", sig);
200 	restore();
201 	errx(3, "timeout");
202 }
203 
204 void
205 terminate(int sig)
206 {
207 	fprintf(lg, "signal terminate %d\n", sig);
208 	restore();
209 	errx(3, "terminate");
210 }
211 
212 void
213 iostdin(int sig)
214 {
215 	char buf[8192];
216 	ssize_t n;
217 
218 	fprintf(lg, "signal iostdin %d\n", sig);
219 
220 	/* try to read as many log messages as possible before terminating */
221 	if (fcntl(mfd, F_SETFL, O_NONBLOCK) == -1)
222 		err(1, "fcntl O_NONBLOCK");
223 	while ((n = read(mfd, buf, sizeof(buf))) > 0) {
224 		fprintf(lg, ">>> ");
225 		if (fwrite(buf, 1, n, lg) != (size_t)n)
226 			err(1, "fwrite %s", logfile);
227 		if (buf[n-1] != '\n')
228 			fprintf(lg, "\n");
229 	}
230 	if (n < 0 && errno != EAGAIN)
231 		err(1, "read %s", ptyname);
232 
233 	if ((n = read(0, buf, sizeof(buf))) < 0)
234 		err(1, "read stdin");
235 	restore();
236 	if (n > 0)
237 		errx(3, "read stdin %zd bytes", n);
238 	exit(0);
239 }
240