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