xref: /netbsd/usr.bin/last/want.c (revision c0c5b856)
1 /*	$NetBSD: want.c,v 1.17 2012/03/15 03:04:05 dholland Exp $	*/
2 
3 /*
4  * Copyright (c) 1987, 1993, 1994
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 static struct utmp *buf;
32 static time_t seentime;
33 
34 static void onintr(int);
35 static int want(struct utmp *, int);
36 static const char *gethost(struct utmp *, const char *, int);
37 
38 static const char *
39 /*ARGSUSED*/
gethost(struct utmp * ut,const char * host,int numeric)40 gethost(struct utmp *ut, const char *host, int numeric)
41 {
42 #if HAS_UT_SS == 0
43 	return numeric ? "" : host;
44 #else
45 	if (numeric) {
46 		static char hbuf[512];
47 		hbuf[0] = '\0';
48 		(void)sockaddr_snprintf(hbuf, sizeof(hbuf), "%a",
49 		    (struct sockaddr *)&ut->ut_ss);
50 		return hbuf;
51 	} else
52 		return host;
53 #endif
54 }
55 
56 #define NULTERM(what) \
57 	if (check ## what) \
58 		(void)strlcpy(what ## p = what ## buf, bp->ut_ ## what, \
59 		    sizeof(what ## buf)); \
60 	else \
61 		what ## p = bp->ut_ ## what
62 
63 /*
64  * wtmp --
65  *	read through the wtmp file
66  */
67 static void
wtmp(const char * file,int namesz,int linesz,int hostsz,int numeric)68 wtmp(const char *file, int namesz, int linesz, int hostsz, int numeric)
69 {
70 	struct utmp	*bp;		/* current structure */
71 	TTY	*T;			/* tty list entry */
72 	struct stat	stb;		/* stat of file for sz */
73 	off_t	offset;
74 	int	wfd;
75 	char	*ct;
76 	const char *crmsg;
77 	size_t  len = sizeof(*buf) * MAXUTMP;
78 	char namebuf[sizeof(bp->ut_name) + 1], *namep;
79 	char linebuf[sizeof(bp->ut_line) + 1], *linep;
80 	char hostbuf[sizeof(bp->ut_host) + 1], *hostp;
81 	int checkname = namesz > (int)sizeof(bp->ut_name);
82 	int checkline = linesz > (int)sizeof(bp->ut_line);
83 	int checkhost = hostsz > (int)sizeof(bp->ut_host);
84 
85 	if ((buf = malloc(len)) == NULL)
86 		err(EXIT_FAILURE, "Cannot allocate utmp buffer");
87 
88 	crmsg = NULL;
89 
90 	if (!strcmp(file, "-")) {
91 		wfd = STDIN_FILENO;
92 		file = "<stdin>";
93 	} else if ((wfd = open(file, O_RDONLY, 0)) < 0) {
94 		err(EXIT_FAILURE, "%s", file);
95 	}
96 
97 	if (lseek(wfd, 0, SEEK_CUR) < 0) {
98 		const char *dir;
99 		char *tfile;
100 		int tempfd;
101 		ssize_t tlen;
102 
103 		if (ESPIPE != errno) {
104 			err(EXIT_FAILURE, "lseek");
105 		}
106 		dir = getenv("TMPDIR");
107 		if (asprintf(&tfile, "%s/last.XXXXXX", dir ? dir : _PATH_TMP) == -1)
108 			err(EXIT_FAILURE, "asprintf");
109 		tempfd = mkstemp(tfile);
110 		if (tempfd < 0) {
111 			err(EXIT_FAILURE, "mkstemp");
112 		}
113 		unlink(tfile);
114 		for (;;) {
115 		   	tlen = read(wfd, buf, len);
116 			if (tlen < 0) {
117 				err(1, "%s: read", file);
118 			}
119 			if (tlen == 0) {
120 				break;
121 			}
122 			if (write(tempfd, buf, tlen) != tlen) {
123 				err(1, "%s: write", tfile);
124 			}
125 		}
126 		wfd = tempfd;
127 	}
128 
129 	if (fstat(wfd, &stb) == -1)
130 		err(EXIT_FAILURE, "%s: fstat", file);
131 	if (!S_ISREG(stb.st_mode))
132 		errx(EXIT_FAILURE, "%s: Not a regular file", file);
133 
134 	seentime = stb.st_mtime;
135 	(void)signal(SIGINT, onintr);
136 	(void)signal(SIGQUIT, onintr);
137 
138 	offset = stb.st_size;
139 	/* Ignore trailing garbage or partial record */
140 	offset -= offset % (off_t) sizeof(*buf);
141 
142 	while (offset >= (off_t) sizeof(*buf)) {
143 		ssize_t ret, i;
144 		size_t size;
145 
146 		size = MIN((off_t)len, offset);
147 		offset -= size; /* Always a multiple of sizeof(*buf) */
148 		ret = pread(wfd, buf, size, offset);
149 		if (ret < 0) {
150 			err(EXIT_FAILURE, "%s: pread", file);
151 		} else if ((size_t) ret < size) {
152 			err(EXIT_FAILURE, "%s: Unexpected end of file", file);
153 		}
154 
155 		for (i = ret / sizeof(*buf) - 1; i >= 0; i--) {
156 			bp = &buf[i];
157 
158 			NULTERM(name);
159 			NULTERM(line);
160 			NULTERM(host);
161 
162 			seentime = bp->ut_timefld;
163 
164 			/*
165 			 * if the terminal line is '~', the machine stopped.
166 			 * see utmp(5) for more info.
167 			 */
168 			if (linep[0] == '~' && !linep[1]) {
169 				/* everybody just logged out */
170 				for (T = ttylist; T; T = T->next)
171 					T->logout = -bp->ut_timefld;
172 				currentout = -bp->ut_timefld;
173 				crmsg = strncmp(namep, "shutdown",
174 				    namesz) ? "crash" : "shutdown";
175 				if (want(bp, NO)) {
176 					ct = fmttime(bp->ut_timefld, fulltime);
177 					printf("%-*.*s  %-*.*s %-*.*s %s\n",
178 					    namesz, namesz, namep,
179 					    linesz, linesz, linep,
180 					    hostsz, hostsz,
181 					    gethost(bp, hostp, numeric), ct);
182 					if (maxrec != -1 && !--maxrec)
183 						return;
184 				}
185 				continue;
186 			}
187 			/*
188 			 * if the line is '{' or '|', date got set; see
189 			 * utmp(5) for more info.
190 			 */
191 			if ((linep[0] == '{' || linep[0] == '|') && !linep[1]) {
192 				if (want(bp, NO)) {
193 					ct = fmttime(bp->ut_timefld, fulltime);
194 					printf("%-*.*s  %-*.*s %-*.*s %s\n",
195 					    namesz, namesz, namep,
196 					    linesz, linesz, linep,
197 					    hostsz, hostsz,
198 					    gethost(bp, hostp, numeric),
199 					    ct);
200 					if (maxrec && !--maxrec)
201 						return;
202 				}
203 				continue;
204 			}
205 			/* find associated tty */
206 			for (T = ttylist;; T = T->next) {
207 				if (!T) {
208 					/* add new one */
209 					T = addtty(linep);
210 					break;
211 				}
212 				if (!strncmp(T->tty, linep, LINESIZE))
213 					break;
214 			}
215 			if (TYPE(bp) == SIGNATURE)
216 				continue;
217 			if (namep[0] && want(bp, YES)) {
218 				ct = fmttime(bp->ut_timefld, fulltime);
219 				printf("%-*.*s  %-*.*s %-*.*s %s ",
220 				    namesz, namesz, namep,
221 				    linesz, linesz, linep,
222 				    hostsz, hostsz,
223 				    gethost(bp, hostp, numeric),
224 				    ct);
225 				if (!T->logout)
226 					puts("  still logged in");
227 				else {
228 					time_t	delta;			/* time difference */
229 
230 					if (T->logout < 0) {
231 						T->logout = -T->logout;
232 						printf("- %s", crmsg);
233 					}
234 					else
235 						printf("- %s",
236 						    fmttime(T->logout,
237 						    fulltime | TIMEONLY));
238 					delta = T->logout - bp->ut_timefld;
239 					if (delta < SECSPERDAY)
240 						printf("  (%s)\n",
241 						    fmttime(delta,
242 						    fulltime | TIMEONLY | GMT));
243 					else
244 						printf(" (%lld+%s)\n",
245 						    (long long)
246 						    delta / SECSPERDAY,
247 						    fmttime(delta,
248 						    fulltime | TIMEONLY | GMT));
249 				}
250 				if (maxrec != -1 && !--maxrec)
251 					return;
252 			}
253 			T->logout = bp->ut_timefld;
254 		}
255 	}
256 	fulltime = 1;	/* show full time */
257 	crmsg = fmttime(seentime, FULLTIME);
258 	if ((ct = strrchr(file, '/')) != NULL)
259 		ct++;
260 	printf("\n%s begins %s\n", ct ? ct : file, crmsg);
261 }
262 
263 /*
264  * want --
265  *	see if want this entry
266  */
267 static int
want(struct utmp * bp,int check)268 want(struct utmp *bp, int check)
269 {
270 	ARG *step;
271 
272 	if (check) {
273 		/*
274 		 * when uucp and ftp log in over a network, the entry in
275 		 * the utmp file is the name plus their process id.  See
276 		 * etc/ftpd.c and usr.bin/uucp/uucpd.c for more information.
277 		 */
278 		if (!strncmp(bp->ut_line, "ftp", sizeof("ftp") - 1))
279 			bp->ut_line[3] = '\0';
280 		else if (!strncmp(bp->ut_line, "uucp", sizeof("uucp") - 1))
281 			bp->ut_line[4] = '\0';
282 	}
283 	if (!arglist)
284 		return (YES);
285 
286 	for (step = arglist; step; step = step->next)
287 		switch(step->type) {
288 		case HOST_TYPE:
289 			if (!strncasecmp(step->name, bp->ut_host, HOSTSIZE))
290 				return (YES);
291 			break;
292 		case TTY_TYPE:
293 			if (!strncmp(step->name, bp->ut_line, LINESIZE))
294 				return (YES);
295 			break;
296 		case USER_TYPE:
297 			if (!strncmp(step->name, bp->ut_name, NAMESIZE))
298 				return (YES);
299 			break;
300 	}
301 	return (NO);
302 }
303 
304 /*
305  * onintr --
306  *	on interrupt, we inform the user how far we've gotten
307  */
308 static void
onintr(int signo)309 onintr(int signo)
310 {
311 	/* FIXME: None of this is allowed in a signal handler */
312 	printf("\ninterrupted %s\n", fmttime(seentime, FULLTIME));
313 	if (signo == SIGINT) {
314 		(void)raise_default_signal(signo);
315 		exit(EXIT_FAILURE);
316 	}
317 	(void)fflush(stdout);		/* fix required for rsh */
318 }
319