1 1.23 wiz /* $NetBSD: lastcomm.c,v 1.23 2012/01/31 21:53:42 wiz Exp $ */ 2 1.6 jtc 3 1.1 cgd /* 4 1.6 jtc * Copyright (c) 1980, 1993 5 1.6 jtc * The Regents of the University of California. All rights reserved. 6 1.1 cgd * 7 1.1 cgd * Redistribution and use in source and binary forms, with or without 8 1.1 cgd * modification, are permitted provided that the following conditions 9 1.1 cgd * are met: 10 1.1 cgd * 1. Redistributions of source code must retain the above copyright 11 1.1 cgd * notice, this list of conditions and the following disclaimer. 12 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 cgd * notice, this list of conditions and the following disclaimer in the 14 1.1 cgd * documentation and/or other materials provided with the distribution. 15 1.15 agc * 3. Neither the name of the University nor the names of its contributors 16 1.1 cgd * may be used to endorse or promote products derived from this software 17 1.1 cgd * without specific prior written permission. 18 1.1 cgd * 19 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 1.1 cgd * SUCH DAMAGE. 30 1.1 cgd */ 31 1.1 cgd 32 1.11 lukem #include <sys/cdefs.h> 33 1.1 cgd #ifndef lint 34 1.20 lukem __COPYRIGHT("@(#) Copyright (c) 1980, 1993\ 35 1.20 lukem The Regents of the University of California. All rights reserved."); 36 1.1 cgd #endif /* not lint */ 37 1.1 cgd 38 1.1 cgd #ifndef lint 39 1.6 jtc #if 0 40 1.7 jtc static char sccsid[] = "@(#)lastcomm.c 8.2 (Berkeley) 4/29/95"; 41 1.6 jtc #endif 42 1.23 wiz __RCSID("$NetBSD: lastcomm.c,v 1.23 2012/01/31 21:53:42 wiz Exp $"); 43 1.1 cgd #endif /* not lint */ 44 1.1 cgd 45 1.1 cgd #include <sys/param.h> 46 1.6 jtc #include <sys/stat.h> 47 1.1 cgd #include <sys/acct.h> 48 1.6 jtc 49 1.1 cgd #include <ctype.h> 50 1.6 jtc #include <err.h> 51 1.6 jtc #include <fcntl.h> 52 1.9 ghudson #include <math.h> 53 1.11 lukem #include <pwd.h> 54 1.1 cgd #include <stdio.h> 55 1.4 jtc #include <stdlib.h> 56 1.6 jtc #include <string.h> 57 1.6 jtc #include <struct.h> 58 1.14 kleink #include <time.h> 59 1.9 ghudson #include <tzfile.h> 60 1.6 jtc #include <unistd.h> 61 1.6 jtc #include <utmp.h> 62 1.1 cgd #include "pathnames.h" 63 1.1 cgd 64 1.18 christos static time_t expand(u_int); 65 1.18 christos static char *flagbits(int); 66 1.18 christos static const char *getdev(dev_t); 67 1.22 christos static int requested(char *[], const struct acct *); 68 1.19 perry static void usage(void) __dead; 69 1.18 christos 70 1.6 jtc int 71 1.18 christos main(int argc, char *argv[]) 72 1.1 cgd { 73 1.11 lukem char *p; 74 1.5 cgd struct acct ab; 75 1.1 cgd struct stat sb; 76 1.5 cgd FILE *fp; 77 1.5 cgd off_t size; 78 1.6 jtc time_t t; 79 1.9 ghudson double delta; 80 1.22 christos int ch, wflag, lwidth; 81 1.18 christos const char *acctfile = _PATH_ACCT; 82 1.18 christos 83 1.18 christos setprogname(argv[0]); 84 1.22 christos wflag = 0; 85 1.22 christos lwidth = -6; 86 1.1 cgd 87 1.22 christos while ((ch = getopt(argc, argv, "f:w")) != -1) 88 1.1 cgd switch((char)ch) { 89 1.1 cgd case 'f': 90 1.1 cgd acctfile = optarg; 91 1.1 cgd break; 92 1.22 christos case 'w': 93 1.22 christos wflag = 1; 94 1.22 christos break; 95 1.1 cgd case '?': 96 1.1 cgd default: 97 1.6 jtc usage(); 98 1.1 cgd } 99 1.6 jtc argc -= optind; 100 1.1 cgd argv += optind; 101 1.1 cgd 102 1.5 cgd /* Open the file. */ 103 1.5 cgd if ((fp = fopen(acctfile, "r")) == NULL || fstat(fileno(fp), &sb)) 104 1.5 cgd err(1, "%s", acctfile); 105 1.5 cgd 106 1.5 cgd /* 107 1.5 cgd * Round off to integral number of accounting records, probably 108 1.5 cgd * not necessary, but it doesn't hurt. 109 1.5 cgd */ 110 1.22 christos size = sb.st_size - sb.st_size % sizeof(ab); 111 1.5 cgd 112 1.6 jtc /* Check if any records to display. */ 113 1.22 christos if (size < (off_t)sizeof(ab)) 114 1.22 christos return 0; 115 1.5 cgd 116 1.22 christos size -= sizeof(ab); 117 1.22 christos if (fseeko(fp, size, SEEK_SET) == -1) 118 1.5 cgd err(1, "%s", acctfile); 119 1.5 cgd 120 1.22 christos lwidth = (int)fldsiz(acct, ac_comm) - ((wflag)? 0: 6); 121 1.5 cgd for (;;) { 122 1.22 christos if (fread(&ab, sizeof(ab), 1, fp) != 1) 123 1.5 cgd err(1, "%s", acctfile); 124 1.5 cgd 125 1.5 cgd if (ab.ac_comm[0] == '\0') { 126 1.5 cgd ab.ac_comm[0] = '?'; 127 1.5 cgd ab.ac_comm[1] = '\0'; 128 1.5 cgd } else 129 1.5 cgd for (p = &ab.ac_comm[0]; 130 1.5 cgd p < &ab.ac_comm[fldsiz(acct, ac_comm)] && *p; ++p) 131 1.16 dsl if (!isprint((unsigned char)*p)) 132 1.5 cgd *p = '?'; 133 1.10 kleink if (!*argv || requested(argv, &ab)) { 134 1.5 cgd 135 1.22 christos if (!wflag) 136 1.22 christos ab.ac_comm[10] = '\0'; 137 1.10 kleink t = expand(ab.ac_utime) + expand(ab.ac_stime); 138 1.10 kleink (void)printf( 139 1.11 lukem "%-*.*s %-7s %-*.*s %-*.*s %6.2f secs %.16s", 140 1.22 christos lwidth, lwidth, 141 1.22 christos ab.ac_comm, flagbits(ab.ac_flag), 142 1.22 christos UT_NAMESIZE, UT_NAMESIZE, 143 1.22 christos user_from_uid(ab.ac_uid, 0), UT_LINESIZE, 144 1.22 christos UT_LINESIZE, getdev(ab.ac_tty), 145 1.22 christos t / (double)AHZ, ctime(&ab.ac_btime)); 146 1.10 kleink delta = expand(ab.ac_etime) / (double)AHZ; 147 1.22 christos (void)printf(" (%1.0f:%02.0f:%05.2f)\n", 148 1.22 christos floor(delta / SECSPERHOUR), 149 1.22 christos floor(fmod(delta, SECSPERHOUR) / SECSPERMIN), 150 1.22 christos fmod(delta, SECSPERMIN)); 151 1.10 kleink } 152 1.10 kleink /* are we at the beginning of the file yet? */ 153 1.10 kleink if (size == 0) 154 1.10 kleink break; 155 1.10 kleink /* seek backward over the one we read and the next to read */ 156 1.22 christos if (fseeko(fp, 2 * -(off_t)sizeof(ab), SEEK_CUR) == -1) 157 1.10 kleink err(1, "%s", acctfile); 158 1.10 kleink /* and account for its size */ 159 1.22 christos size -= sizeof(ab); 160 1.1 cgd } 161 1.22 christos return 0; 162 1.1 cgd } 163 1.1 cgd 164 1.18 christos static time_t 165 1.18 christos expand(u_int t) 166 1.1 cgd { 167 1.11 lukem time_t nt; 168 1.1 cgd 169 1.1 cgd nt = t & 017777; 170 1.1 cgd t >>= 13; 171 1.1 cgd while (t) { 172 1.1 cgd t--; 173 1.1 cgd nt <<= 3; 174 1.1 cgd } 175 1.22 christos return nt; 176 1.1 cgd } 177 1.1 cgd 178 1.18 christos static char * 179 1.18 christos flagbits(int f) 180 1.1 cgd { 181 1.6 jtc static char flags[20] = "-"; 182 1.6 jtc char *p; 183 1.1 cgd 184 1.6 jtc #define BIT(flag, ch) if (f & flag) *p++ = ch 185 1.6 jtc 186 1.6 jtc p = flags + 1; 187 1.1 cgd BIT(ASU, 'S'); 188 1.1 cgd BIT(AFORK, 'F'); 189 1.1 cgd BIT(ACOMPAT, 'C'); 190 1.1 cgd BIT(ACORE, 'D'); 191 1.1 cgd BIT(AXSIG, 'X'); 192 1.6 jtc *p = '\0'; 193 1.22 christos return flags; 194 1.1 cgd } 195 1.1 cgd 196 1.18 christos static int 197 1.22 christos requested(char *argv[], const struct acct *acp) 198 1.1 cgd { 199 1.1 cgd do { 200 1.7 jtc if (!strcmp(user_from_uid(acp->ac_uid, 0), *argv)) 201 1.22 christos return 1; 202 1.7 jtc if (!strcmp(getdev(acp->ac_tty), *argv)) 203 1.22 christos return 1; 204 1.1 cgd if (!strncmp(acp->ac_comm, *argv, fldsiz(acct, ac_comm))) 205 1.22 christos return 1; 206 1.1 cgd } while (*++argv); 207 1.22 christos return 0; 208 1.1 cgd } 209 1.1 cgd 210 1.18 christos static const char * 211 1.18 christos getdev(dev_t dev) 212 1.1 cgd { 213 1.22 christos static dev_t lastdev = NODEV; 214 1.18 christos static const char *lastname; 215 1.1 cgd 216 1.6 jtc if (dev == NODEV) /* Special case. */ 217 1.22 christos return "__"; 218 1.6 jtc if (dev == lastdev) /* One-element cache. */ 219 1.22 christos return lastname; 220 1.22 christos if ((lastname = devname(dev, S_IFCHR)) != NULL) 221 1.22 christos return lastname; 222 1.22 christos return "??"; 223 1.6 jtc } 224 1.6 jtc 225 1.18 christos static void 226 1.18 christos usage(void) 227 1.6 jtc { 228 1.6 jtc (void)fprintf(stderr, 229 1.23 wiz "Usage: %s [-w] [-f file] [command ...] [user ...] [terminal ...]\n", 230 1.18 christos getprogname()); 231 1.6 jtc exit(1); 232 1.1 cgd } 233