/*
* 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.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/acct.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <fcntl.h>
#include <exacct.h>
#include <pwd.h>
#include <grp.h>
#include <project.h>
#include <stdlib.h>
#include <strings.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#ifndef _LP64
#define FMT_UINT64 "%-15llu"
#else
#define FMT_UINT64 "%-15lu"
#endif
#define MAX_DEPTH 25 /* maximum depth level */
static int vflag = 0;
typedef struct catalog_item {
int type;
char *name;
} catalog_item_t;
/*
* The actual constants are defined in <sys/exacct_catalog.h>.
*/
static catalog_item_t catalog[] = {
{ EXD_VERSION, "version" },
{ EXD_FILETYPE, "filetype" },
{ EXD_CREATOR, "creator" },
{ EXD_HOSTNAME, "hostname" },
{ EXD_GROUP_HEADER, "group-header" },
{ EXD_GROUP_PROC, "group-proc" },
{ EXD_GROUP_TASK, "group-task" },
{ EXD_GROUP_LWP, "group-lwp" },
{ EXD_GROUP_FLOW, "group-flow" },
{ EXD_GROUP_PROC_TAG, "group-proc-tag" },
{ EXD_GROUP_TASK_TAG, "group-task-tag" },
{ EXD_GROUP_LWP_TAG, "group-lwp-tag" },
{ EXD_GROUP_PROC_PARTIAL, "group-proc-partial" },
{ EXD_GROUP_TASK_PARTIAL, "group-task-partial" },
{ EXD_GROUP_TASK_INTERVAL, "group-task-interval" },
{ EXD_PROC_PID, "pid" },
{ EXD_PROC_ANCPID, "ppid" },
{ EXD_PROC_UID, "uid" },
{ EXD_PROC_GID, "gid" },
{ EXD_PROC_TASKID, "taskid" },
{ EXD_PROC_PROJID, "projid" },
{ EXD_PROC_HOSTNAME, "hostname" },
{ EXD_PROC_COMMAND, "command" },
{ EXD_PROC_WAIT_STATUS, "wait-status" },
{ EXD_PROC_START_SEC, "start-sec" },
{ EXD_PROC_START_NSEC, "start-nsec" },
{ EXD_PROC_FINISH_SEC, "finish-sec" },
{ EXD_PROC_FINISH_NSEC, "finish-nsec" },
{ EXD_PROC_CPU_USER_SEC, "cpu-user-sec" },
{ EXD_PROC_CPU_USER_NSEC, "cpu-user-nsec" },
{ EXD_PROC_CPU_SYS_SEC, "cpu-sys-sec" },
{ EXD_PROC_CPU_SYS_NSEC, "cpu-sys-nsec" },
{ EXD_PROC_TTY_MAJOR, "tty-major" },
{ EXD_PROC_TTY_MINOR, "tty-minor" },
{ EXD_PROC_FAULTS_MAJOR, "faults-major" },
{ EXD_PROC_FAULTS_MINOR, "faults-minor" },
{ EXD_PROC_MESSAGES_RCV, "msgs-recv" },
{ EXD_PROC_MESSAGES_SND, "msgs-snd" },
{ EXD_PROC_BLOCKS_IN, "blocks-in" },
{ EXD_PROC_BLOCKS_OUT, "blocks-out" },
{ EXD_PROC_CHARS_RDWR, "chars-rdwr" },
{ EXD_PROC_CONTEXT_VOL, "ctxt-vol" },
{ EXD_PROC_CONTEXT_INV, "ctxt-inv" },
{ EXD_PROC_SIGNALS, "signals" },
{ EXD_PROC_SWAPS, "swaps" },
{ EXD_PROC_SYSCALLS, "syscalls" },
{ EXD_PROC_TAG, "proc-tag" },
{ EXD_PROC_ACCT_FLAGS, "acctflags" },
{ EXD_PROC_ZONENAME, "zone" },
{ EXD_PROC_MEM_RSS_AVG_K, "memory-rss-avg-k" },
{ EXD_PROC_MEM_RSS_MAX_K, "memory-rss-max-k" },
{ EXD_TASK_TASKID, "taskid" },
{ EXD_TASK_ANCTASKID, "anctaskid" },
{ EXD_TASK_PROJID, "projid" },
{ EXD_TASK_HOSTNAME, "hostname" },
{ EXD_TASK_START_SEC, "start-sec" },
{ EXD_TASK_START_NSEC, "start-nsec" },
{ EXD_TASK_FINISH_SEC, "finish-sec" },
{ EXD_TASK_FINISH_NSEC, "finish-nsec" },
{ EXD_TASK_CPU_USER_SEC, "cpu-user-sec" },
{ EXD_TASK_CPU_USER_NSEC, "cpu-user-nsec" },
{ EXD_TASK_CPU_SYS_SEC, "cpu-sys-sec" },
{ EXD_TASK_CPU_SYS_NSEC, "cpu-sys-nsec" },
{ EXD_TASK_FAULTS_MAJOR, "faults-major" },
{ EXD_TASK_FAULTS_MINOR, "faults-minor" },
{ EXD_TASK_MESSAGES_RCV, "msgs-recv" },
{ EXD_TASK_MESSAGES_SND, "msgs-snd" },
{ EXD_TASK_BLOCKS_IN, "blocks-in" },
{ EXD_TASK_BLOCKS_OUT, "blocks-out" },
{ EXD_TASK_CHARS_RDWR, "chars-rdwr" },
{ EXD_TASK_CONTEXT_VOL, "ctxt-vol" },
{ EXD_TASK_CONTEXT_INV, "ctxt-inv" },
{ EXD_TASK_SIGNALS, "signals" },
{ EXD_TASK_SWAPS, "swaps" },
{ EXD_TASK_SYSCALLS, "syscalls" },
{ EXD_TASK_TAG, "task-tag" },
{ EXD_TASK_ZONENAME, "zone" },
{ EXD_FLOW_V4SADDR, "src-addr-v4" },
{ EXD_FLOW_V4DADDR, "dest-addr-v4" },
{ EXD_FLOW_V6SADDR, "src-addr-v6" },
{ EXD_FLOW_V6DADDR, "dest-addr-v6" },
{ EXD_FLOW_SPORT, "src-port" },
{ EXD_FLOW_DPORT, "dest-port" },
{ EXD_FLOW_PROTOCOL, "protocol" },
{ EXD_FLOW_DSFIELD, "diffserv-field" },
{ EXD_FLOW_NBYTES, "total-bytes" },
{ EXD_FLOW_NPKTS, "total-packets" },
{ EXD_FLOW_CTIME, "creation-time" },
{ EXD_FLOW_LSEEN, "last-seen" },
{ EXD_FLOW_PROJID, "projid" },
{ EXD_FLOW_UID, "uid" },
{ EXD_FLOW_ANAME, "action-name" },
{ EXD_NONE, "none" }
};
static void disp_obj(ea_object_t *o, int indent);
/*
* Convert catalog ID into catalog name.
*/
static char *
catalog_name(int type)
{
int i = 0;
while (catalog[i].type != EXD_NONE) {
if (catalog[i].type == type)
return (catalog[i].name);
i++;
}
return ("unknown");
}
/*
* Display port information, if available
*/
static void
disp_port(uint16_t port)
{
struct servent *port_info;
port_info = getservbyport(htons(port), NULL);
if (port_info != NULL) {
(void) printf("%s", port_info->s_name);
}
}
/*
* Display host name for a given IP address if available.
*/
static void
disp_host(char *addr, int family)
{
struct hostent *phe;
uint_t len;
int error_num;
len = (family == AF_INET) ? sizeof (struct in_addr) :
sizeof (struct in6_addr);
if ((phe = getipnodebyaddr(addr, len, family, &error_num)) != NULL) {
(void) printf("%s", phe->h_name);
}
}
/*
* Display protocol information, if available.
*/
static void
disp_proto(uint8_t protocol)
{
struct protoent *proto_ent;
proto_ent = getprotobynumber(protocol);
if (proto_ent != NULL) {
(void) printf("%s", proto_ent->p_name);
}
}
/*
* Display recursively exacct objects in a given embedded group.
*/
static void
disp_embedded_group(ea_object_t *eo, int indent)
{
while (eo != NULL) {
disp_obj(eo, indent + 1);
if (eo->eo_type == EO_GROUP)
disp_embedded_group(eo->eo_group.eg_objs, indent + 1);
eo = eo->eo_next;
}
}
/*
* Display the data stored in a given exacct object.
*/
static void
disp_obj(ea_object_t *o, int indent)
{
char objname[30] = " ";
int eol = 1;
if (indent > MAX_DEPTH) {
objname[0] = '>';
indent = 1;
}
(void) printf("%6x\t", (o->eo_catalog & EXD_DATA_MASK));
(void) snprintf(objname + indent, 30 - indent, "%-s",
catalog_name(o->eo_catalog & EXD_DATA_MASK));
(void) printf("%-30s\t", objname);
switch (o->eo_catalog & EXT_TYPE_MASK) {
case EXT_UINT8:
(void) printf("%-15u", o->eo_item.ei_uint8);
if (vflag &&
((o->eo_catalog & EXD_DATA_MASK) == EXD_FLOW_PROTOCOL)) {
disp_proto(o->eo_item.ei_uint8);
}
break;
case EXT_UINT16:
(void) printf("%-15u", o->eo_item.ei_uint16);
if (vflag &&
(((o->eo_catalog & EXD_DATA_MASK) == EXD_FLOW_SPORT) ||
((o->eo_catalog & EXD_DATA_MASK) == EXD_FLOW_DPORT))) {
disp_port(o->eo_item.ei_uint16);
}
break;
case EXT_UINT32:
switch (o->eo_catalog & EXD_DATA_MASK) {
case EXD_PROC_WAIT_STATUS:
{
int wstat = o->eo_item.ei_uint32;
if (vflag) {
if (WIFEXITED(wstat))
(void) printf("%-14d exit",
WEXITSTATUS(wstat));
else if (WIFSIGNALED(wstat))
(void) printf("%14d, signal",
WTERMSIG(wstat));
else
(void) printf("%d", wstat);
} else {
(void) printf("%d", wstat);
}
}
break;
case EXD_PROC_UID:
{
uid_t uid = o->eo_item.ei_uint32;
(void) printf("%-15u", uid);
if (vflag) {
struct passwd *pwd;
if ((pwd = getpwuid(uid)) != NULL)
(void) printf("%s",
pwd->pw_name);
}
}
break;
case EXD_PROC_GID:
{
gid_t gid = o->eo_item.ei_uint32;
(void) printf("%-15u", gid);
if (vflag) {
struct group *grp;
if ((grp = getgrgid(gid)) != NULL)
(void) printf("%s",
grp->gr_name);
}
}
break;
case EXD_PROC_PROJID:
case EXD_TASK_PROJID:
{
projid_t projid = o->eo_item.ei_uint32;
(void) printf("%-15lu", projid);
if (vflag) {
struct project proj;
char projbuf[PROJECT_BUFSZ];
if (getprojbyid(projid, &proj, projbuf,
PROJECT_BUFSZ) != NULL)
(void) printf("%s",
proj.pj_name);
}
}
break;
case EXD_PROC_ACCT_FLAGS:
{
int flag = o->eo_item.ei_uint32;
(void) printf("%-15u", flag);
if (vflag) {
if (flag & AFORK)
(void) printf("FORK ");
if (flag & ASU)
(void) printf("SU");
}
}
break;
case EXD_FLOW_V4SADDR:
/* FALLTHRU */
case EXD_FLOW_V4DADDR:
{
char str[INET_ADDRSTRLEN];
uint32_t addr = htonl(o->eo_item.ei_uint32);
(void) printf("%-15s",
inet_ntop(AF_INET, &addr, str,
INET_ADDRSTRLEN));
if (vflag) {
disp_host((char *)&addr, AF_INET);
}
}
break;
default:
(void) printf("%u", o->eo_item.ei_uint32);
}
break;
case EXT_UINT64:
{
time_t _time;
char timebuf[20];
(void) printf(FMT_UINT64, o->eo_item.ei_uint64);
if (!vflag)
break;
if (ea_match_object_catalog(o, EXD_TASK_START_SEC) ||
ea_match_object_catalog(o, EXD_TASK_FINISH_SEC) ||
ea_match_object_catalog(o, EXD_PROC_START_SEC) ||
ea_match_object_catalog(o, EXD_PROC_FINISH_SEC) ||
ea_match_object_catalog(o, EXD_FLOW_LSEEN) ||
ea_match_object_catalog(o, EXD_FLOW_CTIME)) {
_time = o->eo_item.ei_uint64;
(void) strftime(timebuf, sizeof (timebuf),
"%D %T", localtime(&_time));
(void) fputs(timebuf, stdout);
}
}
break;
case EXT_DOUBLE:
(void) printf("%f", o->eo_item.ei_double);
break;
case EXT_STRING:
(void) printf("\"%s\"", o->eo_item.ei_string);
break;
case EXT_RAW:
switch (o->eo_catalog & EXD_DATA_MASK) {
case EXD_FLOW_V6SADDR:
/* FALLTHRU */
case EXD_FLOW_V6DADDR:
{
in6_addr_t *addr;
char str[INET6_ADDRSTRLEN];
addr = (in6_addr_t *)o->eo_item.ei_raw;
(void) printf("%-28s", inet_ntop(AF_INET6,
&addr->s6_addr, str, INET6_ADDRSTRLEN));
if (vflag) {
disp_host((char *)&addr->s6_addr,
AF_INET6);
}
}
break;
default:
{
ea_size_t size = o->eo_item.ei_size;
char *buf = o->eo_item.ei_raw;
uint64_t i;
for (i = 0; i < size && i < 6; i++)
(void) printf("0x%2X ", buf[i]);
if (size > 6)
(void) printf("...");
}
}
break;
case EXT_GROUP:
(void) printf("[group of %u object(s)]", o->eo_group.eg_nobjs);
break;
case EXT_EXACCT_OBJECT:
/*
* Embedded exacct records.
*/
{
ea_object_type_t ot;
ea_object_t *op;
ea_object_t *eo;
ot = ea_unpack_object(&op, EUP_ALLOC,
o->eo_item.ei_object, o->eo_item.ei_size);
if (ot == EO_ERROR) {
(void) printf("error: couldn't unpack embedded "
"object\n");
break;
}
eol = 0;
if (ot == EO_GROUP) {
(void) printf("[embedded group of %u "
"object(s)]\n", op->eo_group.eg_nobjs);
eo = op->eo_group.eg_objs;
disp_embedded_group(eo, indent);
} else {
(void) printf("[embedded object]\n");
disp_obj(op, indent);
}
ea_free_object(op, EUP_ALLOC);
}
break;
default:
(void) printf("[complex value]");
break;
}
if (eol)
(void) printf("\n");
}
/*
* Read and display a group of exacct objects from the file.
*/
static void
disp_group(ea_file_t *ef, uint_t nobjs, int indent)
{
uint_t i;
for (i = 0; i < nobjs; i++) {
ea_object_t scratch;
int res;
if ((res = ea_get_object(ef, &scratch)) == -1) {
(void) fprintf(stderr,
"bad file: ea_get_object()==%d\n", res);
exit(2);
}
disp_obj(&scratch, indent + 1);
if (scratch.eo_type == EO_GROUP)
disp_group(ef, scratch.eo_group.eg_nobjs, indent + 1);
else
(void) ea_free_item(&scratch, EUP_ALLOC);
}
}
static void
usage()
{
(void) fprintf(stderr, "Usage: exdump [-v] <file>\n");
exit(2);
}
int
main(int argc, char *argv[])
{
ea_file_t ef;
ea_object_t scratch;
char *fname;
int opt;
while ((opt = getopt(argc, argv, "v")) != EOF) {
switch (opt) {
case 'v':
vflag = 1;
break;
default:
usage();
}
}
if (argc == optind)
usage();
if (argc > optind)
fname = argv[optind++];
if (argc > optind)
usage();
if (ea_open(&ef, fname, NULL,
vflag ? EO_NO_VALID_HDR : 0, O_RDONLY, 0) == -1) {
(void) fprintf(stderr, "exdump: cannot open %s\n", fname);
return (1);
}
bzero(&scratch, sizeof (ea_object_t));
while (ea_get_object(&ef, &scratch) != -1) {
disp_obj(&scratch, 0);
if (scratch.eo_type == EO_GROUP)
disp_group(&ef, scratch.eo_group.eg_nobjs, 0);
else
(void) ea_free_item(&scratch, EUP_ALLOC);
(void) bzero(&scratch, sizeof (ea_object_t));
}
(void) ea_close(&ef);
return (0);
}