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