1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#pragma ident	"%Z%%M%	%I%	%E% SMI"
27
28#include <sys/acct.h>
29#include <sys/wait.h>
30#include <sys/types.h>
31#include <sys/socket.h>
32#include <stdio.h>
33#include <fcntl.h>
34#include <exacct.h>
35#include <pwd.h>
36#include <grp.h>
37#include <project.h>
38#include <stdlib.h>
39#include <strings.h>
40#include <netinet/in.h>
41#include <arpa/inet.h>
42#include <netdb.h>
43
44#ifndef _LP64
45#define	FMT_UINT64	"%-15llu"
46#else
47#define	FMT_UINT64	"%-15lu"
48#endif
49
50#define	MAX_DEPTH	25		/* maximum depth level */
51
52static int vflag = 0;
53
54typedef struct catalog_item {
55	int	type;
56	char	*name;
57} catalog_item_t;
58
59/*
60 * The actual constants are defined in <sys/exacct_catalog.h>.
61 */
62static catalog_item_t catalog[] = {
63	{ EXD_VERSION,			"version" },
64	{ EXD_FILETYPE,			"filetype" },
65	{ EXD_CREATOR,			"creator" },
66	{ EXD_HOSTNAME,			"hostname" },
67
68	{ EXD_GROUP_HEADER,		"group-header" },
69	{ EXD_GROUP_PROC,		"group-proc" },
70	{ EXD_GROUP_TASK,		"group-task" },
71	{ EXD_GROUP_LWP,		"group-lwp" },
72	{ EXD_GROUP_FLOW,		"group-flow" },
73	{ EXD_GROUP_PROC_TAG,		"group-proc-tag" },
74	{ EXD_GROUP_TASK_TAG,		"group-task-tag" },
75	{ EXD_GROUP_LWP_TAG,		"group-lwp-tag" },
76	{ EXD_GROUP_PROC_PARTIAL,	"group-proc-partial" },
77	{ EXD_GROUP_TASK_PARTIAL,	"group-task-partial" },
78	{ EXD_GROUP_TASK_INTERVAL,	"group-task-interval" },
79
80	{ EXD_PROC_PID,			"pid" },
81	{ EXD_PROC_ANCPID, 		"ppid" },
82	{ EXD_PROC_UID,			"uid" },
83	{ EXD_PROC_GID,			"gid" },
84	{ EXD_PROC_TASKID,		"taskid" },
85	{ EXD_PROC_PROJID,		"projid" },
86	{ EXD_PROC_HOSTNAME,		"hostname" },
87	{ EXD_PROC_COMMAND,		"command" },
88	{ EXD_PROC_WAIT_STATUS,		"wait-status" },
89	{ EXD_PROC_START_SEC,		"start-sec" },
90	{ EXD_PROC_START_NSEC,		"start-nsec" },
91	{ EXD_PROC_FINISH_SEC,		"finish-sec" },
92	{ EXD_PROC_FINISH_NSEC,		"finish-nsec" },
93	{ EXD_PROC_CPU_USER_SEC,	"cpu-user-sec" },
94	{ EXD_PROC_CPU_USER_NSEC,	"cpu-user-nsec" },
95	{ EXD_PROC_CPU_SYS_SEC,		"cpu-sys-sec" },
96	{ EXD_PROC_CPU_SYS_NSEC,	"cpu-sys-nsec" },
97	{ EXD_PROC_TTY_MAJOR,		"tty-major" },
98	{ EXD_PROC_TTY_MINOR,		"tty-minor" },
99	{ EXD_PROC_FAULTS_MAJOR,	"faults-major" },
100	{ EXD_PROC_FAULTS_MINOR,	"faults-minor" },
101	{ EXD_PROC_MESSAGES_RCV,	"msgs-recv" },
102	{ EXD_PROC_MESSAGES_SND,	"msgs-snd" },
103	{ EXD_PROC_BLOCKS_IN,		"blocks-in" },
104	{ EXD_PROC_BLOCKS_OUT,		"blocks-out" },
105	{ EXD_PROC_CHARS_RDWR,		"chars-rdwr" },
106	{ EXD_PROC_CONTEXT_VOL,		"ctxt-vol" },
107	{ EXD_PROC_CONTEXT_INV,		"ctxt-inv" },
108	{ EXD_PROC_SIGNALS,		"signals" },
109	{ EXD_PROC_SWAPS,		"swaps" },
110	{ EXD_PROC_SYSCALLS,		"syscalls" },
111	{ EXD_PROC_TAG,			"proc-tag" },
112	{ EXD_PROC_ACCT_FLAGS,		"acctflags" },
113	{ EXD_PROC_ZONENAME,		"zone" },
114	{ EXD_PROC_MEM_RSS_AVG_K,	"memory-rss-avg-k" },
115	{ EXD_PROC_MEM_RSS_MAX_K,	"memory-rss-max-k" },
116
117	{ EXD_TASK_TASKID,		"taskid" },
118	{ EXD_TASK_ANCTASKID,		"anctaskid" },
119	{ EXD_TASK_PROJID,		"projid" },
120	{ EXD_TASK_HOSTNAME,		"hostname" },
121	{ EXD_TASK_START_SEC,		"start-sec" },
122	{ EXD_TASK_START_NSEC,		"start-nsec" },
123	{ EXD_TASK_FINISH_SEC,		"finish-sec" },
124	{ EXD_TASK_FINISH_NSEC,		"finish-nsec" },
125	{ EXD_TASK_CPU_USER_SEC,	"cpu-user-sec" },
126	{ EXD_TASK_CPU_USER_NSEC,	"cpu-user-nsec" },
127	{ EXD_TASK_CPU_SYS_SEC,		"cpu-sys-sec" },
128	{ EXD_TASK_CPU_SYS_NSEC,	"cpu-sys-nsec" },
129	{ EXD_TASK_FAULTS_MAJOR,	"faults-major" },
130	{ EXD_TASK_FAULTS_MINOR,	"faults-minor" },
131	{ EXD_TASK_MESSAGES_RCV,	"msgs-recv" },
132	{ EXD_TASK_MESSAGES_SND,	"msgs-snd" },
133	{ EXD_TASK_BLOCKS_IN,		"blocks-in" },
134	{ EXD_TASK_BLOCKS_OUT,		"blocks-out" },
135	{ EXD_TASK_CHARS_RDWR,		"chars-rdwr" },
136	{ EXD_TASK_CONTEXT_VOL,		"ctxt-vol" },
137	{ EXD_TASK_CONTEXT_INV,		"ctxt-inv" },
138	{ EXD_TASK_SIGNALS,		"signals" },
139	{ EXD_TASK_SWAPS,		"swaps" },
140	{ EXD_TASK_SYSCALLS,		"syscalls" },
141	{ EXD_TASK_TAG,			"task-tag" },
142	{ EXD_TASK_ZONENAME,		"zone" },
143
144	{ EXD_FLOW_V4SADDR,		"src-addr-v4" },
145	{ EXD_FLOW_V4DADDR,		"dest-addr-v4" },
146	{ EXD_FLOW_V6SADDR,		"src-addr-v6" },
147	{ EXD_FLOW_V6DADDR,		"dest-addr-v6" },
148	{ EXD_FLOW_SPORT,		"src-port" },
149	{ EXD_FLOW_DPORT,		"dest-port" },
150	{ EXD_FLOW_PROTOCOL,		"protocol" },
151	{ EXD_FLOW_DSFIELD,		"diffserv-field" },
152	{ EXD_FLOW_NBYTES,		"total-bytes" },
153	{ EXD_FLOW_NPKTS,		"total-packets" },
154	{ EXD_FLOW_CTIME,		"creation-time" },
155	{ EXD_FLOW_LSEEN,		"last-seen" },
156	{ EXD_FLOW_PROJID,		"projid" },
157	{ EXD_FLOW_UID,			"uid" },
158	{ EXD_FLOW_ANAME,		"action-name" },
159
160	{ EXD_NONE,			"none" }
161};
162
163static void disp_obj(ea_object_t *o, int indent);
164
165/*
166 * Convert catalog ID into catalog name.
167 */
168static char *
169catalog_name(int type)
170{
171	int i = 0;
172
173	while (catalog[i].type != EXD_NONE) {
174		if (catalog[i].type == type)
175			return (catalog[i].name);
176		i++;
177	}
178
179	return ("unknown");
180}
181
182/*
183 * Display port information, if available
184 */
185static void
186disp_port(uint16_t port)
187{
188	struct servent *port_info;
189
190	port_info = getservbyport(htons(port), NULL);
191	if (port_info != NULL) {
192		(void) printf("%s", port_info->s_name);
193	}
194}
195
196/*
197 * Display host name for a given IP address if available.
198 */
199static void
200disp_host(char *addr, int family)
201{
202	struct hostent *phe;
203	uint_t len;
204	int error_num;
205
206	len = (family == AF_INET) ? sizeof (struct in_addr) :
207	    sizeof (struct in6_addr);
208
209	if ((phe = getipnodebyaddr(addr, len, family, &error_num)) != NULL) {
210		(void) printf("%s", phe->h_name);
211	}
212}
213
214/*
215 * Display protocol information, if available.
216 */
217static void
218disp_proto(uint8_t protocol)
219{
220	struct protoent *proto_ent;
221
222	proto_ent = getprotobynumber(protocol);
223	if (proto_ent != NULL) {
224		(void) printf("%s", proto_ent->p_name);
225	}
226
227}
228
229/*
230 * Display recursively exacct objects in a given embedded group.
231 */
232static void
233disp_embedded_group(ea_object_t *eo, int indent)
234{
235	while (eo != NULL) {
236		disp_obj(eo, indent + 1);
237		if (eo->eo_type == EO_GROUP)
238			disp_embedded_group(eo->eo_group.eg_objs, indent + 1);
239		eo = eo->eo_next;
240	}
241}
242
243/*
244 * Display the data stored in a given exacct object.
245 */
246static void
247disp_obj(ea_object_t *o, int indent)
248{
249	char objname[30] = "                              ";
250	int eol = 1;
251
252	if (indent > MAX_DEPTH) {
253		objname[0] = '>';
254		indent = 1;
255	}
256
257	(void) printf("%6x\t", (o->eo_catalog & EXD_DATA_MASK));
258	(void) snprintf(objname + indent, 30 - indent, "%-s",
259	    catalog_name(o->eo_catalog & EXD_DATA_MASK));
260	(void) printf("%-30s\t", objname);
261
262	switch (o->eo_catalog & EXT_TYPE_MASK) {
263	case EXT_UINT8:
264		(void) printf("%-15u", o->eo_item.ei_uint8);
265		if (vflag &&
266		    ((o->eo_catalog & EXD_DATA_MASK) == EXD_FLOW_PROTOCOL)) {
267			disp_proto(o->eo_item.ei_uint8);
268		}
269		break;
270	case EXT_UINT16:
271		(void) printf("%-15u", o->eo_item.ei_uint16);
272		if (vflag &&
273		    (((o->eo_catalog & EXD_DATA_MASK) == EXD_FLOW_SPORT) ||
274		    ((o->eo_catalog & EXD_DATA_MASK) == EXD_FLOW_DPORT))) {
275			disp_port(o->eo_item.ei_uint16);
276		}
277		break;
278	case EXT_UINT32:
279		switch (o->eo_catalog & EXD_DATA_MASK) {
280		case EXD_PROC_WAIT_STATUS:
281			{
282				int wstat = o->eo_item.ei_uint32;
283
284				if (vflag) {
285					if (WIFEXITED(wstat))
286						(void) printf("%-14d exit",
287						    WEXITSTATUS(wstat));
288					else if (WIFSIGNALED(wstat))
289						(void) printf("%14d, signal",
290						    WTERMSIG(wstat));
291					else
292						(void) printf("%d", wstat);
293				} else {
294					(void) printf("%d", wstat);
295				}
296			}
297			break;
298		case EXD_PROC_UID:
299			{
300				uid_t uid = o->eo_item.ei_uint32;
301
302				(void) printf("%-15u", uid);
303				if (vflag) {
304					struct passwd *pwd;
305					if ((pwd = getpwuid(uid)) != NULL)
306						(void) printf("%s",
307						    pwd->pw_name);
308				}
309			}
310			break;
311		case EXD_PROC_GID:
312			{
313				gid_t gid = o->eo_item.ei_uint32;
314
315				(void) printf("%-15u", gid);
316				if (vflag) {
317					struct group *grp;
318					if ((grp = getgrgid(gid)) != NULL)
319						(void) printf("%s",
320						    grp->gr_name);
321				}
322			}
323			break;
324		case EXD_PROC_PROJID:
325		case EXD_TASK_PROJID:
326			{
327				projid_t projid = o->eo_item.ei_uint32;
328
329				(void) printf("%-15lu", projid);
330				if (vflag) {
331					struct project proj;
332					char projbuf[PROJECT_BUFSZ];
333
334					if (getprojbyid(projid, &proj, projbuf,
335					    PROJECT_BUFSZ) != NULL)
336						(void) printf("%s",
337						    proj.pj_name);
338				}
339			}
340			break;
341		case EXD_PROC_ACCT_FLAGS:
342			{
343				int flag = o->eo_item.ei_uint32;
344
345				(void) printf("%-15u", flag);
346				if (vflag) {
347					if (flag & AFORK)
348						(void) printf("FORK ");
349					if (flag & ASU)
350						(void) printf("SU");
351				}
352			}
353			break;
354		case EXD_FLOW_V4SADDR:
355			/* FALLTHRU */
356		case EXD_FLOW_V4DADDR:
357			{
358				char str[INET_ADDRSTRLEN];
359				uint32_t addr = htonl(o->eo_item.ei_uint32);
360
361				(void) printf("%-15s",
362				    inet_ntop(AF_INET, &addr, str,
363				    INET_ADDRSTRLEN));
364				if (vflag) {
365					disp_host((char *)&addr, AF_INET);
366				}
367			}
368			break;
369		default:
370			(void) printf("%u", o->eo_item.ei_uint32);
371		}
372		break;
373	case EXT_UINT64:
374		{
375			time_t _time;
376			char timebuf[20];
377
378			(void) printf(FMT_UINT64, o->eo_item.ei_uint64);
379			if (!vflag)
380				break;
381			if (ea_match_object_catalog(o, EXD_TASK_START_SEC) ||
382			    ea_match_object_catalog(o, EXD_TASK_FINISH_SEC) ||
383			    ea_match_object_catalog(o, EXD_PROC_START_SEC) ||
384			    ea_match_object_catalog(o, EXD_PROC_FINISH_SEC) ||
385			    ea_match_object_catalog(o, EXD_FLOW_LSEEN) ||
386			    ea_match_object_catalog(o, EXD_FLOW_CTIME)) {
387				_time = o->eo_item.ei_uint64;
388				(void) strftime(timebuf, sizeof (timebuf),
389				    "%D %T", localtime(&_time));
390				(void) fputs(timebuf, stdout);
391			}
392		}
393		break;
394	case EXT_DOUBLE:
395		(void) printf("%f", o->eo_item.ei_double);
396		break;
397	case EXT_STRING:
398		(void) printf("\"%s\"", o->eo_item.ei_string);
399		break;
400	case EXT_RAW:
401		switch (o->eo_catalog & EXD_DATA_MASK) {
402		case EXD_FLOW_V6SADDR:
403			/* FALLTHRU */
404		case EXD_FLOW_V6DADDR:
405			{
406				in6_addr_t *addr;
407				char str[INET6_ADDRSTRLEN];
408
409				addr = (in6_addr_t *)o->eo_item.ei_raw;
410				(void) printf("%-28s", inet_ntop(AF_INET6,
411				    &addr->s6_addr, str, INET6_ADDRSTRLEN));
412				if (vflag) {
413					disp_host((char *)&addr->s6_addr,
414					    AF_INET6);
415				}
416
417			}
418			break;
419		default:
420			{
421				ea_size_t size = o->eo_item.ei_size;
422				char *buf = o->eo_item.ei_raw;
423				uint64_t i;
424
425				for (i = 0; i < size && i < 6; i++)
426					(void) printf("0x%2X ", buf[i]);
427				if (size > 6)
428					(void) printf("...");
429			}
430		}
431		break;
432	case EXT_GROUP:
433		(void) printf("[group of %u object(s)]", o->eo_group.eg_nobjs);
434		break;
435	case EXT_EXACCT_OBJECT:
436		/*
437		 * Embedded exacct records.
438		 */
439		{
440			ea_object_type_t ot;
441			ea_object_t *op;
442			ea_object_t *eo;
443
444			ot = ea_unpack_object(&op, EUP_ALLOC,
445			    o->eo_item.ei_object, o->eo_item.ei_size);
446
447			if (ot == EO_ERROR) {
448				(void) printf("error: couldn't unpack embedded "
449				    "object\n");
450				break;
451			}
452			eol = 0;
453			if (ot == EO_GROUP) {
454				(void) printf("[embedded group of %u "
455				    "object(s)]\n", op->eo_group.eg_nobjs);
456				eo = op->eo_group.eg_objs;
457				disp_embedded_group(eo, indent);
458			} else {
459				(void) printf("[embedded object]\n");
460				disp_obj(op, indent);
461			}
462			ea_free_object(op, EUP_ALLOC);
463		}
464		break;
465	default:
466		(void) printf("[complex value]");
467		break;
468	}
469
470	if (eol)
471		(void) printf("\n");
472
473}
474
475/*
476 * Read and display a group of exacct objects from the file.
477 */
478static void
479disp_group(ea_file_t *ef, uint_t nobjs, int indent)
480{
481	uint_t i;
482
483	for (i = 0; i < nobjs; i++) {
484		ea_object_t scratch;
485		int res;
486
487		if ((res = ea_get_object(ef, &scratch)) == -1) {
488			(void) fprintf(stderr,
489			    "bad file: ea_get_object()==%d\n", res);
490			exit(2);
491		}
492
493		disp_obj(&scratch, indent + 1);
494
495		if (scratch.eo_type == EO_GROUP)
496			disp_group(ef, scratch.eo_group.eg_nobjs, indent + 1);
497		else
498			(void) ea_free_item(&scratch, EUP_ALLOC);
499	}
500}
501
502static void
503usage()
504{
505	(void) fprintf(stderr, "Usage: exdump [-v] <file>\n");
506	exit(2);
507}
508
509int
510main(int argc, char *argv[])
511{
512	ea_file_t ef;
513	ea_object_t scratch;
514	char *fname;
515	int opt;
516
517	while ((opt = getopt(argc, argv, "v")) != EOF) {
518		switch (opt) {
519		case 'v':
520			vflag = 1;
521			break;
522		default:
523			usage();
524		}
525	}
526
527	if (argc == optind)
528		usage();
529	if (argc > optind)
530		fname = argv[optind++];
531	if (argc > optind)
532		usage();
533
534	if (ea_open(&ef, fname, NULL,
535	    vflag ? EO_NO_VALID_HDR : 0, O_RDONLY, 0) == -1) {
536		(void) fprintf(stderr, "exdump: cannot open %s\n", fname);
537		return (1);
538	}
539
540	bzero(&scratch, sizeof (ea_object_t));
541	while (ea_get_object(&ef, &scratch) != -1) {
542		disp_obj(&scratch, 0);
543		if (scratch.eo_type == EO_GROUP)
544			disp_group(&ef, scratch.eo_group.eg_nobjs, 0);
545		else
546			(void) ea_free_item(&scratch, EUP_ALLOC);
547		(void) bzero(&scratch, sizeof (ea_object_t));
548	}
549
550	(void) ea_close(&ef);
551	return (0);
552}
553