# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # cmd/lastcomm/Makefile # PROG = lastcomm OBJS= lastcomm.o lc_pacct.o lc_exacct.o lc_utils.o EXPOBJ= expand.o SRCS= $(OBJS:%.o=%.c) $(EXPOBJ:%.o=../acct/lib/%.c) lint : LINTFLAGS = -ux include ../Makefile.cmd LDLIBS += -lexacct CFLAGS += $(CCVERBOSE) FILEMODE = 0555 .KEEP_STATE: all: $(PROG) $(PROG): $(OBJS) $(EXPOBJ) $(LINK.c) -o $@ $(OBJS) $(EXPOBJ) $(LDLIBS) $(POST_PROCESS) install: all $(ROOTPROG) clean: $(RM) $(OBJS) $(EXPOBJ) lint: lint_SRCS %.o: ../acct/lib/%.c $(COMPILE.c) $< include ../Makefile.targ Copyright (c) 1980 Regents of the University of California. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by the University of California, Berkeley and its contributors. 4. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PORTIONS OF LASTCOMM COMMAND FUNCTIONALITY /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 1983-2000 by Sun Microsystems, Inc. * All rights reserved. */ #include #include #include #include #include #include "lastcomm.h" int main(int argc, char *argv[]) { int res; int opt; char *filename = NULL; char buf[PATH_MAX]; ea_file_t ef; int xflag = 0; while ((opt = getopt(argc, argv, "f:x")) != EOF) { switch (opt) { case 'f': filename = optarg; break; case 'x': xflag = 1; break; default: (void) fprintf(stderr, gettext("Usage:\tlastcomm [-x] [-f filename]" " [command] ... [user] ... [terminal] ...\n")); exit(2); } } if (xflag) { /* * User wants to see extended accounting statistics. */ if (filename) { return (lc_exacct(filename, argc, argv, optind)); } else { if (acctctl(AC_PROC | AC_FILE_GET, buf, PATH_MAX) < 0) { (void) fprintf(stderr, gettext("lastcomm: " "cannot open extended accounting file: " "%s\n"), strerror(errno)); return (1); } else { return (lc_exacct(buf, argc, argv, optind)); } } } if (filename == NULL) { /* * If no option is specified, then first try to open current * extended process accounting file and then old-style process * accounting. */ if (acctctl(AC_PROC | AC_FILE_GET, buf, PATH_MAX) < 0) return (lc_pacct("/var/adm/pacct", argc, argv, optind)); else return (lc_exacct(buf, argc, argv, optind)); } else { /* * If accounting file was specified and we don't know its * format, then first try to open it as an extended accounting * file and then as an old-style accounting file. */ if ((res = ea_open(&ef, filename, EXACCT_CREATOR, EO_TAIL | EO_VALID_HDR, O_RDONLY, 0)) >= 0) (void) ea_close(&ef); if (res < 0) return (lc_pacct(filename, argc, argv, optind)); else return (lc_exacct(filename, argc, argv, optind)); } } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 1999-2000 by Sun Microsystems, Inc. * All rights reserved. */ #ifndef _LASTCOMM_H #define _LASTCOMM_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define AHZ 64 #define fldsiz(str, fld) (sizeof (((struct str *)0)->fld)) #define BUF_MAX 4096 #define NACCT (BUF_MAX / sizeof (struct acct)) #define BUF_SIZ (NACCT * sizeof (struct acct)) #define PATHNAMLEN 32 #define EXACCT_CREATOR "Hammerhead" /* * utmpx defines wider fields for user and line. For compatibility of output, * we are limiting these to the old maximums in utmp. Define UTMPX_NAMELEN * to use the full lengths. */ #ifndef UTMPX_NAMELEN /* XXX - utmp - fix name length */ #define NMAX (_POSIX_LOGIN_NAME_MAX - 1) #define LMAX 12 #else /* UTMPX_NAMELEN */ static struct utmpx dummy; #define NMAX (sizeof (dummy.ut_user)) #define LMAX (sizeof (dummy.ut_line)) #endif /* UTMPX_NAMELEN */ extern char *getdev(dev_t); extern char *getname(uid_t); extern char *flagbits(int); extern int lc_pacct(char *, int, char **, int); extern int lc_exacct(char *, int, char **, int); #ifdef __cplusplus } #endif #endif /* _LASTCOMM_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include "lastcomm.h" /* ARGSUSED1 */ static void skip_group(ea_file_t *ef, uint_t nobjs) { ea_object_t curr_obj; if (ea_previous_object(ef, &curr_obj) == -1) { (void) fprintf(stderr, gettext("lastcomm: " "corrupted exacct file\n")); exit(1); } } static int ok(int argc, char *argv[], int index, uid_t uid, dev_t tty, char *command) { int j; for (j = index; j < argc; j++) if (strcmp(getname(uid), argv[j]) && strcmp(getdev(tty), argv[j]) && strncmp(command, argv[j], fldsiz(acct, ac_comm))) break; return (j == argc); } static void disp_group(ea_file_t *ef, uint_t nobjs, int argc, char *argv[], int index) { uint_t i; char *command = NULL; double cpu_usr_secs = 0.; double cpu_usr_nsecs = 0.; double cpu_sys_secs = 0.; double cpu_sys_nsecs = 0.; double totalsecs; dev_t tty = 0; major_t tty_major = 0; minor_t tty_minor = 0; uid_t uid = 0; time_t time = 0; uint32_t flag = 0; for (i = 0; i < nobjs; i++) { ea_object_t curr_obj; if (ea_get_object(ef, &curr_obj) == -1) { (void) fprintf(stderr, gettext("lastcomm: " "corrupted exacct file\n")); exit(1); } switch (curr_obj.eo_catalog) { case EXT_STRING | EXC_DEFAULT | EXD_PROC_COMMAND: command = curr_obj.eo_item.ei_string; break; case EXT_UINT32 | EXC_DEFAULT | EXD_PROC_UID: uid = curr_obj.eo_item.ei_uint32; break; case EXT_UINT64 | EXC_DEFAULT | EXD_PROC_CPU_SYS_SEC: cpu_sys_secs = curr_obj.eo_item.ei_uint64; break; case EXT_UINT64 | EXC_DEFAULT | EXD_PROC_CPU_USER_SEC: cpu_usr_secs = curr_obj.eo_item.ei_uint64; break; case EXT_UINT64 | EXC_DEFAULT | EXD_PROC_CPU_SYS_NSEC: cpu_sys_nsecs = curr_obj.eo_item.ei_uint64; break; case EXT_UINT64 | EXC_DEFAULT | EXD_PROC_CPU_USER_NSEC: cpu_usr_nsecs = curr_obj.eo_item.ei_uint64; break; case EXT_UINT32 | EXC_DEFAULT | EXD_PROC_TTY_MAJOR: tty_major = curr_obj.eo_item.ei_uint32; break; case EXT_UINT32 | EXC_DEFAULT | EXD_PROC_TTY_MINOR: tty_minor = curr_obj.eo_item.ei_uint32; break; case EXT_UINT32 | EXC_DEFAULT | EXD_PROC_ACCT_FLAGS: flag = curr_obj.eo_item.ei_uint32; break; case EXT_UINT64 | EXC_DEFAULT | EXD_PROC_START_SEC: time = (uint32_t)curr_obj.eo_item.ei_uint64; break; default: break; } if (curr_obj.eo_type == EO_GROUP) disp_group(ef, curr_obj.eo_group.eg_nobjs, argc, argv, index); } if (command == NULL) { (void) fprintf(stderr, gettext("lastcomm: " "corrupted exacct file\n")); exit(1); } /* * If a 64-bit kernel returns a major or minor value that would exceed * the capacity of a 32-bit dev_t (and these also become visible in the * filesystem), then the 32-bit makedev may be inaccurate and return * NODEV. When this occurs, we can remedy the problem by providing * either a function which returns "dev64_t"'s or by providing an LP64 * version of lastcomm. */ tty = makedev(tty_major, tty_minor); /* * If this record doesn't match the optional arguments, go on to the * next record. */ if (argc > index && !ok(argc, argv, index, uid, tty, command)) return; totalsecs = cpu_usr_secs + cpu_usr_nsecs / NANOSEC + cpu_sys_secs + cpu_sys_nsecs / NANOSEC; (void) printf("%-*.*s %s %-*s %-*s %6.2f secs %.16s\n", fldsiz(acct, ac_comm), fldsiz(acct, ac_comm), command, flagbits(flag), NMAX, getname(uid), LMAX, getdev(tty), totalsecs, ctime(&time)); } int lc_exacct(char *filename, int argc, char *argv[], int index) { ea_file_t ef; ea_object_t curr_obj; if (ea_open(&ef, filename, EXACCT_CREATOR, EO_TAIL | EO_VALID_HDR, O_RDONLY, 0) < 0) { switch (ea_error()) { case EXR_CORRUPT_FILE: (void) fprintf(stderr, gettext("lastcomm: " "exacct file corrupted\n")); break; case EXR_SYSCALL_FAIL: (void) fprintf(stderr, gettext("lastcomm: " "cannot open %s: %s\n"), filename, strerror(errno)); break; default: break; } return (1); } while (ea_previous_object(&ef, &curr_obj) != -1) { if (ea_get_object(&ef, &curr_obj) == -1) { (void) fprintf(stderr, gettext("lastcomm: " "exacct file corrupted\n")); exit(1); } /* * lc_exacct(), in parsing the extended process accounting file, * has knowledge of the fact that process records are top-level * records. */ if ((curr_obj.eo_catalog & EXT_TYPE_MASK) == EXT_GROUP) { if (curr_obj.eo_catalog == (EXT_GROUP | EXC_DEFAULT | EXD_GROUP_PROC)) disp_group(&ef, curr_obj.eo_group.eg_nobjs, argc, argv, index); else skip_group(&ef, curr_obj.eo_group.eg_nobjs); } /* * Back up to the head of the object we just consumed. */ if (ea_previous_object(&ef, &curr_obj) == -1) { if (ea_error() == EXR_EOF) break; (void) fprintf(stderr, gettext("lastcomm: " "exacct file corrupted\n")); exit(1); } } if (ea_error() != EXR_EOF) { (void) fprintf(stderr, gettext("lastcomm: " "exacct file corrupted\n")); exit(1); } (void) ea_close(&ef); return (0); } /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. * * Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T * All rights reserved. * * * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include "lastcomm.h" /* * lc_pacct() provides the functionality of lastcomm when applied to the basic * SVR4 accounting file, /var/adm/pacct. Definitions for this accounting file * format are given in . */ extern ulong_t expand(comp_t); static int ok(int argc, char *argv[], int index, struct acct *acp) { int j; for (j = index; j < argc; j++) if (strcmp(getname(acp->ac_uid), argv[j]) && strcmp(getdev(acp->ac_tty), argv[j]) && strncmp(acp->ac_comm, argv[j], fldsiz(acct, ac_comm))) break; return (j == argc); } int lc_pacct(char *name, int argc, char *argv[], int index) { struct acct buf[NACCT]; int bn, cc; struct acct *acp; struct stat sb; time_t t; int fd; if ((fd = open(name, O_RDONLY)) < 0) { perror(name); return (1); } (void) fstat(fd, &sb); if (sb.st_size % sizeof (struct acct)) { (void) fprintf(stderr, gettext("lastcomm: accounting file" " is corrupted\n")); return (1); } for (bn = ((unsigned)sb.st_size / BUF_SIZ) + 1; bn >= 0; bn--) { if (lseek(fd, (unsigned)bn * BUF_SIZ, 0) == -1) { perror("lseek"); return (1); } cc = read(fd, buf, BUF_SIZ); if (cc < 0) { perror("read"); return (1); } acp = buf + (cc / sizeof (buf[0])) - 1; for (; acp >= buf; acp--) { char *cp; ulong_t x; if (acp->ac_flag > 0100) { (void) fprintf(stderr, gettext("lastcomm: " "accounting file is corrupted\n")); return (1); } if (acp->ac_comm[0] == '\0') (void) strcpy(acp->ac_comm, "?"); for (cp = &acp->ac_comm[0]; cp < &acp->ac_comm[fldsiz(acct, ac_comm)] && *cp; cp++) if (!isascii(*cp) || iscntrl(*cp)) *cp = '?'; if (argc > index && !ok(argc, argv, index, acp)) continue; x = expand(acp->ac_utime) + expand(acp->ac_stime); t = acp->ac_btime; (void) printf("%-*.*s %s %-*s %-*s %6.2f secs %.16s\n", fldsiz(acct, ac_comm), fldsiz(acct, ac_comm), acp->ac_comm, flagbits(acp->ac_flag), NMAX, getname(acp->ac_uid), LMAX, getdev(acp->ac_tty), x / (double)HZ, ctime(&t)); } } return (0); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include "lastcomm.h" /* * lc_utils contains utility functions used by both the basic and extended * accounting components of lastcomm. getdev(), on its first call, builds * the set of tty device name to dev_t mappings. */ #define N_DEVS 43 /* hash value for device names */ #define NDEVS 500 /* max number of file names in /dev */ #define HASH(d) (((int)d) % N_DEVS) /* hash function */ struct devhash { dev_t dev_dev; char dev_name [PATHNAMLEN]; struct devhash *dev_nxt; }; static struct devhash *dev_hash[N_DEVS]; static struct devhash *dev_chain; static int ndevs = NDEVS; static struct devhash *hashtab; /* * Default search list, used if /etc/ttysrch unavailable or unparsable. */ static char *def_srch_dirs[] = { "/dev/term", "/dev/pts", "/dev/xt", NULL }; static char *raw_sf; /* buffer containing raw image of the search file */ #define SRCH_FILE_NAME "/etc/ttysrch" /* * /etc/ttysrch tokens. */ #define COMMENT_CHAR '#' #define EOLN '\n' /* * /etc/ttysrch parser states. */ #define START_STATE 1 #define COMMENT_STATE 2 #define DIRNAME_STATE 3 /* * The following 2 routines are modified version of get_pri_dirs * and srch_dir in ttyname.c. */ static char ** get_pri_dirs() { int bcount = 0; int c; int sf_lines = 0; /* number of lines in search file */ int dirno = 0; int state; FILE *sf; char **pri_dirs; /* priority search list */ char *sfp; /* pointer inside the raw image buffer */ struct stat sfsb; /* search file's stat structure buffer */ if ((sf = fopen(SRCH_FILE_NAME, "r")) == NULL) return (def_srch_dirs); if (stat(SRCH_FILE_NAME, &sfsb) < 0) { (void) fclose(sf); return (def_srch_dirs); } raw_sf = malloc(sfsb.st_size + 1); sfp = raw_sf; while ((bcount++ < sfsb.st_size) && ((c = getc(sf)) != EOF)) { *sfp++ = (char)c; if (c == EOLN) sf_lines++; } (void) fclose(sf); *sfp = EOLN; pri_dirs = malloc(++sf_lines * sizeof (char *)); sfp = raw_sf; state = START_STATE; while (--bcount) { switch (state) { case START_STATE: if (*sfp == COMMENT_CHAR) { state = COMMENT_STATE; } else if (!isspace(*sfp)) { state = DIRNAME_STATE; pri_dirs[dirno++] = sfp; } break; case COMMENT_STATE: if (*sfp == EOLN) state = START_STATE; break; case DIRNAME_STATE: if (*sfp == EOLN) { *sfp = '\0'; state = START_STATE; } else if (isspace(*sfp)) { *sfp = '\0'; state = COMMENT_STATE; } break; } /* switch */ sfp++; } *sfp = '\0'; pri_dirs[dirno] = NULL; return (pri_dirs); } /* * Build a chain of character devices in dev_chain, starting with the given * path. */ static int srch_dir(char *path) { DIR *dirp; struct dirent *direntp; struct stat st; char file_name[PATHNAMLEN]; if ((dirp = opendir(path)) == NULL) return (0); if ((readdir(dirp) == NULL) || (readdir(dirp) == NULL)) return (0); while ((direntp = readdir(dirp)) != NULL) { (void) strcpy(file_name, path); (void) strcat(file_name, "/"); (void) strcat(file_name, direntp->d_name); if (stat((const char *)file_name, &st) < 0) continue; if ((st.st_mode & S_IFMT) == S_IFCHR) { (void) strcpy(hashtab->dev_name, file_name + strlen("/dev/")); hashtab->dev_nxt = dev_chain; dev_chain = hashtab; hashtab++; if (--ndevs < 0) return (-1); } } (void) closedir(dirp); return (1); } static void setupdevs() { int dirno = 0; char **srch_dirs; hashtab = malloc(NDEVS * sizeof (struct devhash)); if (hashtab == NULL) { (void) fprintf(stderr, gettext("No memory for device table\n")); return; } srch_dirs = get_pri_dirs(); while (srch_dirs[dirno] != NULL) { if (srch_dir(srch_dirs[dirno]) < 0) return; dirno++; } dirno = 0; while (srch_dirs[dirno] != NULL) { if (strcmp("/dev", srch_dirs[dirno]) == 0) /* * Don't search /dev twice. */ return; dirno++; } } char * getdev(dev_t dev) { struct devhash *hp, *nhp; struct stat statb; char name[PATHNAMLEN]; static dev_t lastdev = (dev_t)-1; static char *lastname; static int init = 0; if (dev == NODEV) return ("__"); if (dev == lastdev) return (lastname); if (!init) { setupdevs(); init++; } for (hp = dev_hash[HASH(dev)]; hp; hp = hp->dev_nxt) if (hp->dev_dev == dev) { lastdev = dev; return (lastname = hp->dev_name); } for (hp = dev_chain; hp; hp = nhp) { nhp = hp->dev_nxt; (void) strcpy(name, "/dev/"); (void) strcat(name, hp->dev_name); if (stat(name, &statb) < 0) /* name truncated usually */ continue; if ((statb.st_mode & S_IFMT) != S_IFCHR) continue; hp->dev_dev = statb.st_rdev; hp->dev_nxt = dev_hash[HASH(hp->dev_dev)]; dev_hash[HASH(hp->dev_dev)] = hp; if (hp->dev_dev == dev) { dev_chain = nhp; lastdev = dev; return (lastname = hp->dev_name); } } dev_chain = NULL; return ("??"); } char * flagbits(int f) { int i = 0; static char flags[20]; #define BIT(flag, ch) flags[i++] = (f & flag) ? ch : ' ' BIT(ASU, 'S'); BIT(AFORK, 'F'); flags[i] = '\0'; return (flags); #undef BIT } char * getname(uid_t uid) { struct passwd *pw; static char uidname[NMAX]; if ((pw = getpwuid(uid)) == NULL) { (void) sprintf(uidname, "%u", uid); return (uidname); } return (pw->pw_name); }