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