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