xref: /onnv-gate/usr/src/cmd/abi/apptracecmd/apptrace.c (revision 1399:f3a549d00510)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
50Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
60Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
70Sstevel@tonic-gate  * with the License.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
100Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
110Sstevel@tonic-gate  * See the License for the specific language governing permissions
120Sstevel@tonic-gate  * and limitations under the License.
130Sstevel@tonic-gate  *
140Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
150Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
160Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
170Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
180Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
190Sstevel@tonic-gate  *
200Sstevel@tonic-gate  * CDDL HEADER END
210Sstevel@tonic-gate  */
2271Scraigm 
230Sstevel@tonic-gate /*
24*1399Sahl  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
250Sstevel@tonic-gate  * Use is subject to license terms.
260Sstevel@tonic-gate  */
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
290Sstevel@tonic-gate 
300Sstevel@tonic-gate #include <stdio.h>
310Sstevel@tonic-gate #include <stdlib.h>
320Sstevel@tonic-gate #include <unistd.h>
330Sstevel@tonic-gate #include <string.h>
340Sstevel@tonic-gate #include <errno.h>
350Sstevel@tonic-gate #include <sys/wait.h>
360Sstevel@tonic-gate #include <apptrace.h>
370Sstevel@tonic-gate #include <libintl.h>
380Sstevel@tonic-gate #include <locale.h>
390Sstevel@tonic-gate 
400Sstevel@tonic-gate #ifdef TRUE
410Sstevel@tonic-gate #undef TRUE
420Sstevel@tonic-gate #endif
430Sstevel@tonic-gate #ifdef FALSE
440Sstevel@tonic-gate #undef FALSE
450Sstevel@tonic-gate #endif
460Sstevel@tonic-gate #define	TRUE  1
470Sstevel@tonic-gate #define	FALSE 0
480Sstevel@tonic-gate 
490Sstevel@tonic-gate /* Various list pointers */
500Sstevel@tonic-gate static char *fromlist;
510Sstevel@tonic-gate static char *fromexcl;
520Sstevel@tonic-gate static char *tolist;
530Sstevel@tonic-gate static char *toexcl;
540Sstevel@tonic-gate 
550Sstevel@tonic-gate static char *iflist;
560Sstevel@tonic-gate static char *ifexcl;
570Sstevel@tonic-gate static char *viflist;
580Sstevel@tonic-gate static char *vifexcl;
590Sstevel@tonic-gate 
600Sstevel@tonic-gate /* The supported options */
610Sstevel@tonic-gate static char const *optlet = "F:fo:T:t:v:";
620Sstevel@tonic-gate /* basename(argv[0]) */
630Sstevel@tonic-gate static char const *command;
640Sstevel@tonic-gate 
650Sstevel@tonic-gate /* The environment variables that'll get picked up by apptrace.so.1 */
660Sstevel@tonic-gate static char const *APPTRACE_BINDTO = "APPTRACE_BINDTO=";
670Sstevel@tonic-gate static char const *APPTRACE_BINDTO_EXCLUDE = "APPTRACE_BINDTO_EXCLUDE=";
680Sstevel@tonic-gate static char const *APPTRACE_BINDFROM = "APPTRACE_BINDFROM=";
690Sstevel@tonic-gate static char const *APPTRACE_BINDFROM_EXCLUDE = "APPTRACE_BINDFROM_EXCLUDE=";
700Sstevel@tonic-gate static char const *APPTRACE_OUTPUT = "APPTRACE_OUTPUT=";
710Sstevel@tonic-gate static char const *APPTRACE_PID = "APPTRACE_PID=";
720Sstevel@tonic-gate static char const *APPTRACE_INTERFACES = "APPTRACE_INTERFACES=";
730Sstevel@tonic-gate static char const *APPTRACE_INTERFACES_EXCLUDE = "APPTRACE_INTERFACES_EXCLUDE=";
740Sstevel@tonic-gate static char const *APPTRACE_VERBOSE = "APPTRACE_VERBOSE=";
750Sstevel@tonic-gate static char const *APPTRACE_VERBOSE_EXCLUDE = "APPTRACE_VERBOSE_EXCLUDE=";
760Sstevel@tonic-gate 
770Sstevel@tonic-gate /* Some default values for the above */
780Sstevel@tonic-gate static char *LD_AUDIT = "LD_AUDIT=/usr/lib/abi/apptrace.so.1";
790Sstevel@tonic-gate #if	defined(sparc) || defined(__sparcv9)
800Sstevel@tonic-gate static char *LD_AUDIT_64 =
810Sstevel@tonic-gate 	"LD_AUDIT_64=/usr/lib/abi/sparcv9/apptrace.so.1";
820Sstevel@tonic-gate #elif	defined(i386) || defined(__amd64)
830Sstevel@tonic-gate static char *LD_AUDIT_64 =
840Sstevel@tonic-gate 	"LD_AUDIT_64=/usr/lib/abi/amd64/apptrace.so.1";
850Sstevel@tonic-gate #else
860Sstevel@tonic-gate #error Unsupported Platform
870Sstevel@tonic-gate #endif
880Sstevel@tonic-gate 
890Sstevel@tonic-gate static char const *one = "1";
900Sstevel@tonic-gate 
910Sstevel@tonic-gate /* The local support functions */
920Sstevel@tonic-gate static void usage(char const *);
930Sstevel@tonic-gate static void stuffenv(char const *, char const *);
940Sstevel@tonic-gate static char *buildlist(char **, char const *);
950Sstevel@tonic-gate 
960Sstevel@tonic-gate int
main(int argc,char ** argv)970Sstevel@tonic-gate main(int argc, char **argv)
980Sstevel@tonic-gate {
990Sstevel@tonic-gate 	int	opt;
1000Sstevel@tonic-gate 	int	fflag = FALSE;
1010Sstevel@tonic-gate 	int	errflg = FALSE;
1020Sstevel@tonic-gate 	char	*outfile = NULL;
1030Sstevel@tonic-gate 	int	stat_loc;
1040Sstevel@tonic-gate 	pid_t	wret, pid;
1050Sstevel@tonic-gate 
1060Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
1070Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
1080Sstevel@tonic-gate #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
1090Sstevel@tonic-gate #endif
1100Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
1110Sstevel@tonic-gate 
1120Sstevel@tonic-gate 
1130Sstevel@tonic-gate 	/* Squirrel the basename of the command name away. */
1140Sstevel@tonic-gate 	if ((command = strrchr(argv[0], '/')) != NULL)
1150Sstevel@tonic-gate 		command++;
1160Sstevel@tonic-gate 	else
1170Sstevel@tonic-gate 		command = argv[0];
1180Sstevel@tonic-gate 
1190Sstevel@tonic-gate 	while ((opt = getopt(argc, argv, optlet)) != EOF) {
1200Sstevel@tonic-gate 		switch (opt) {
1210Sstevel@tonic-gate 		case 'F':
1220Sstevel@tonic-gate 			if (*optarg == '!')
1230Sstevel@tonic-gate 				(void) buildlist(&fromexcl, optarg + 1);
1240Sstevel@tonic-gate 			else
1250Sstevel@tonic-gate 				(void) buildlist(&fromlist, optarg);
1260Sstevel@tonic-gate 			break;
1270Sstevel@tonic-gate 		case 'f':
1280Sstevel@tonic-gate 			fflag = TRUE;
1290Sstevel@tonic-gate 			break;
1300Sstevel@tonic-gate 		case 'o':
1310Sstevel@tonic-gate 			outfile = optarg;
1320Sstevel@tonic-gate 			break;
1330Sstevel@tonic-gate 		case 'T':
1340Sstevel@tonic-gate 			if (*optarg == '!')
1350Sstevel@tonic-gate 				(void) buildlist(&toexcl, optarg + 1);
1360Sstevel@tonic-gate 			else
1370Sstevel@tonic-gate 				(void) buildlist(&tolist, optarg);
1380Sstevel@tonic-gate 			break;
1390Sstevel@tonic-gate 		case 't':
1400Sstevel@tonic-gate 			if (*optarg == '!')
1410Sstevel@tonic-gate 				(void) buildlist(&ifexcl, optarg + 1);
1420Sstevel@tonic-gate 			else
1430Sstevel@tonic-gate 				(void) buildlist(&iflist, optarg);
1440Sstevel@tonic-gate 			break;
1450Sstevel@tonic-gate 		case 'v':
1460Sstevel@tonic-gate 			if (*optarg == '!')
1470Sstevel@tonic-gate 				(void) buildlist(&vifexcl, optarg + 1);
1480Sstevel@tonic-gate 			else
1490Sstevel@tonic-gate 				(void) buildlist(&viflist, optarg);
1500Sstevel@tonic-gate 			break;
1510Sstevel@tonic-gate 		default:
1520Sstevel@tonic-gate 			errflg = TRUE;
1530Sstevel@tonic-gate 			break;
1540Sstevel@tonic-gate 		}
1550Sstevel@tonic-gate 	}
1560Sstevel@tonic-gate 
1570Sstevel@tonic-gate 	/*
1580Sstevel@tonic-gate 	 * Whack the argument vector so that the remainder will be
1590Sstevel@tonic-gate 	 * ready for passing to exec
1600Sstevel@tonic-gate 	 */
1610Sstevel@tonic-gate 	argc -= optind;
1620Sstevel@tonic-gate 	argv += optind;
1630Sstevel@tonic-gate 
1640Sstevel@tonic-gate 	/*
1650Sstevel@tonic-gate 	 * If there was a problem with the options, or there was no command
1660Sstevel@tonic-gate 	 * to be run, then give the usage message and bugout.
1670Sstevel@tonic-gate 	 */
1680Sstevel@tonic-gate 	if (errflg || argc <= 0) {
1690Sstevel@tonic-gate 		usage(command);
1700Sstevel@tonic-gate 		exit(EXIT_FAILURE);
1710Sstevel@tonic-gate 	}
1720Sstevel@tonic-gate 
1730Sstevel@tonic-gate 	/*
1740Sstevel@tonic-gate 	 * This is where the environment gets setup.
1750Sstevel@tonic-gate 	 */
1760Sstevel@tonic-gate 	if (fflag == TRUE)
1770Sstevel@tonic-gate 		stuffenv(APPTRACE_PID, one);
1780Sstevel@tonic-gate 
1790Sstevel@tonic-gate 	if (fromexcl != NULL)
1800Sstevel@tonic-gate 		stuffenv(APPTRACE_BINDFROM_EXCLUDE, fromexcl);
1810Sstevel@tonic-gate 	if (fromlist != NULL)
1820Sstevel@tonic-gate 		stuffenv(APPTRACE_BINDFROM, fromlist);
1830Sstevel@tonic-gate 
1840Sstevel@tonic-gate 	if (tolist != NULL)
1850Sstevel@tonic-gate 		stuffenv(APPTRACE_BINDTO, tolist);
1860Sstevel@tonic-gate 	if (toexcl != NULL)
1870Sstevel@tonic-gate 		stuffenv(APPTRACE_BINDTO_EXCLUDE, toexcl);
1880Sstevel@tonic-gate 
1890Sstevel@tonic-gate 	if (iflist != NULL)
1900Sstevel@tonic-gate 		stuffenv(APPTRACE_INTERFACES, iflist);
1910Sstevel@tonic-gate 	if (ifexcl != NULL)
1920Sstevel@tonic-gate 		stuffenv(APPTRACE_INTERFACES_EXCLUDE, ifexcl);
1930Sstevel@tonic-gate 
1940Sstevel@tonic-gate 	if (viflist != NULL)
1950Sstevel@tonic-gate 		stuffenv(APPTRACE_VERBOSE, viflist);
1960Sstevel@tonic-gate 	if (vifexcl != NULL)
1970Sstevel@tonic-gate 		stuffenv(APPTRACE_VERBOSE_EXCLUDE, vifexcl);
1980Sstevel@tonic-gate 
1990Sstevel@tonic-gate 	if (outfile != NULL)
2000Sstevel@tonic-gate 		stuffenv(APPTRACE_OUTPUT, outfile);
2010Sstevel@tonic-gate 
2020Sstevel@tonic-gate 	/*
2030Sstevel@tonic-gate 	 * It is the setting of the LD_AUDIT environment variable
2040Sstevel@tonic-gate 	 * that tells ld.so.1 to enable link auditing when the child
2050Sstevel@tonic-gate 	 * is exec()ed.
2060Sstevel@tonic-gate 	 */
2070Sstevel@tonic-gate 	(void) putenv(LD_AUDIT);
2080Sstevel@tonic-gate 	(void) putenv(LD_AUDIT_64);
2090Sstevel@tonic-gate 
2100Sstevel@tonic-gate 	/*
2110Sstevel@tonic-gate 	 * The environment is now all setup.
2120Sstevel@tonic-gate 	 * For those about to rock, we salute you!
2130Sstevel@tonic-gate 	 */
2140Sstevel@tonic-gate 	pid = fork();
2150Sstevel@tonic-gate 	switch (pid) {
2160Sstevel@tonic-gate 		/* Error */
2170Sstevel@tonic-gate 	case -1:
2180Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("%s: fork failed: %s\n"),
2190Sstevel@tonic-gate 		    command, strerror(errno));
2200Sstevel@tonic-gate 		exit(EXIT_FAILURE);
2210Sstevel@tonic-gate 		break;
2220Sstevel@tonic-gate 		/* Child */
2230Sstevel@tonic-gate 	case 0:
2240Sstevel@tonic-gate 		/*
2250Sstevel@tonic-gate 		 * Usual failure is argv[0] does not exist or is
2260Sstevel@tonic-gate 		 * not executable.
2270Sstevel@tonic-gate 		 */
2280Sstevel@tonic-gate 		if (execvp(argv[0], argv)) {
2290Sstevel@tonic-gate 			(void) fprintf(stderr, gettext("%s: %s: %s\n"),
2300Sstevel@tonic-gate 			    command, argv[0], strerror(errno));
2310Sstevel@tonic-gate 			_exit(EXIT_FAILURE);
2320Sstevel@tonic-gate 		}
2330Sstevel@tonic-gate 		break;
2340Sstevel@tonic-gate 		/* Parent */
2350Sstevel@tonic-gate 	default:
2360Sstevel@tonic-gate 		wret = waitpid(pid, &stat_loc, 0);
2370Sstevel@tonic-gate 		if (wret == -1) {
2380Sstevel@tonic-gate 			(void) fprintf(stderr,
2390Sstevel@tonic-gate 			    gettext("%s: waitpid failed: %s\n"),
2400Sstevel@tonic-gate 			    command, strerror(errno));
2410Sstevel@tonic-gate 			exit(EXIT_FAILURE);
2420Sstevel@tonic-gate 		}
2430Sstevel@tonic-gate 
2440Sstevel@tonic-gate 		if (wret != pid) {
2450Sstevel@tonic-gate 			(void) fprintf(stderr,
2460Sstevel@tonic-gate 			    gettext("%s: "
2470Sstevel@tonic-gate 			    "waitpid returned %ld when child pid was %ld\n"),
2480Sstevel@tonic-gate 			    command, wret, pid);
2490Sstevel@tonic-gate 			exit(EXIT_FAILURE);
2500Sstevel@tonic-gate 		}
2510Sstevel@tonic-gate 
2520Sstevel@tonic-gate 		if (WIFSIGNALED(stat_loc)) {
2530Sstevel@tonic-gate 			(void) fprintf(stderr, gettext("\n%s: %s: %s"),
2540Sstevel@tonic-gate 			    command, argv[0], strsignal(WTERMSIG(stat_loc)));
2550Sstevel@tonic-gate 			if (WCOREDUMP(stat_loc)) {
2560Sstevel@tonic-gate 				(void) fputs(gettext("(Core dump)"), stderr);
2570Sstevel@tonic-gate #ifdef DEBUG
2580Sstevel@tonic-gate 				(void) fputs(gettext("\nRunning pstack:\n"),
2590Sstevel@tonic-gate 				    stderr);
2600Sstevel@tonic-gate 				(void) putenv("LD_AUDIT=");
2610Sstevel@tonic-gate 				(void) putenv("LD_AUDIT_64=");
2620Sstevel@tonic-gate 				(void) system("/usr/proc/bin/pstack core");
2630Sstevel@tonic-gate #endif
2640Sstevel@tonic-gate 			}
2650Sstevel@tonic-gate 			(void) putc('\n', stderr);
2660Sstevel@tonic-gate 		}
2670Sstevel@tonic-gate 
2680Sstevel@tonic-gate 		/* Normal return from main() */
2690Sstevel@tonic-gate 		return (WEXITSTATUS(stat_loc));
2700Sstevel@tonic-gate 	}
27171Scraigm 	return (0);
2720Sstevel@tonic-gate 	/* NOTREACHED */
2730Sstevel@tonic-gate }
2740Sstevel@tonic-gate 
2750Sstevel@tonic-gate /*
2760Sstevel@tonic-gate  * Take a string in the form "VAR=" and another in the
2770Sstevel@tonic-gate  * form "value" and paste them together.
2780Sstevel@tonic-gate  */
2790Sstevel@tonic-gate static void
stuffenv(char const * var,char const * val)2800Sstevel@tonic-gate stuffenv(char const *var, char const *val)
2810Sstevel@tonic-gate {
2820Sstevel@tonic-gate 	int lenvar, lenval;
2830Sstevel@tonic-gate 	char *stuff;
2840Sstevel@tonic-gate 
2850Sstevel@tonic-gate 	lenvar = strlen(var);
2860Sstevel@tonic-gate 	lenval = strlen(val);
2870Sstevel@tonic-gate 
2880Sstevel@tonic-gate 	if ((stuff = malloc(lenvar + lenval + 1)) == NULL) {
2890Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("%s: malloc failed\n"), command);
2900Sstevel@tonic-gate 		exit(EXIT_FAILURE);
2910Sstevel@tonic-gate 	}
2920Sstevel@tonic-gate 	(void) sprintf(stuff, "%s%s", var, val);
2930Sstevel@tonic-gate 	(void) putenv(stuff);
2940Sstevel@tonic-gate }
2950Sstevel@tonic-gate 
2960Sstevel@tonic-gate /*
2970Sstevel@tonic-gate  * If *dst is empty, use strdup to duplicate src.
2980Sstevel@tonic-gate  * Otherwise:  dst = dst + "," + src;
2990Sstevel@tonic-gate  */
3000Sstevel@tonic-gate static char *
buildlist(char ** dst,char const * src)3010Sstevel@tonic-gate buildlist(char **dst, char const *src)
3020Sstevel@tonic-gate {
3030Sstevel@tonic-gate 	int len;
3040Sstevel@tonic-gate 	char *p;
3050Sstevel@tonic-gate 
3060Sstevel@tonic-gate 	/*
3070Sstevel@tonic-gate 	 * If dst is still empty then dup,
3080Sstevel@tonic-gate 	 * if dup succeeds set dst.
3090Sstevel@tonic-gate 	 */
3100Sstevel@tonic-gate 	if (*dst == NULL) {
3110Sstevel@tonic-gate 		p = strdup(src);
3120Sstevel@tonic-gate 		if (p == NULL)
3130Sstevel@tonic-gate 			goto error;
3140Sstevel@tonic-gate 		*dst = p;
3150Sstevel@tonic-gate 		return (p);
3160Sstevel@tonic-gate 	}
3170Sstevel@tonic-gate 
3180Sstevel@tonic-gate 	len = strlen(*dst);
3190Sstevel@tonic-gate 
3200Sstevel@tonic-gate 	/* +2 because of the comma we add below */
3210Sstevel@tonic-gate 	if ((p = realloc(*dst, len + strlen(src) + 2)) == NULL)
3220Sstevel@tonic-gate 		goto error;
3230Sstevel@tonic-gate 
3240Sstevel@tonic-gate 	*dst = p;
3250Sstevel@tonic-gate 
3260Sstevel@tonic-gate 	*(*dst + len) = ',';
3270Sstevel@tonic-gate 	(void) strcpy((*dst + len + 1), src);
3280Sstevel@tonic-gate 
3290Sstevel@tonic-gate 	return (*dst);
3300Sstevel@tonic-gate 
3310Sstevel@tonic-gate error:
3320Sstevel@tonic-gate 	(void) fprintf(stderr, gettext("%s: allocation failed: %s\n"),
3330Sstevel@tonic-gate 	    command, strerror(errno));
3340Sstevel@tonic-gate 	exit(EXIT_FAILURE);
3350Sstevel@tonic-gate 	/* NOTREACHED */
3360Sstevel@tonic-gate }
3370Sstevel@tonic-gate 
3380Sstevel@tonic-gate static void
usage(char const * prog)3390Sstevel@tonic-gate usage(char const *prog)
3400Sstevel@tonic-gate {
3410Sstevel@tonic-gate 	(void) fprintf(stderr, gettext("Usage: %s [-f][-F [!]tracefromlist]"
3420Sstevel@tonic-gate 	    "[-T [!]tracetolist][-o outputfile]\n"
3430Sstevel@tonic-gate 	    "	[-t calls][-v calls] prog [prog arguments]\n"
3440Sstevel@tonic-gate 
3450Sstevel@tonic-gate 	    "	-F <bindfromlist>\n"
3460Sstevel@tonic-gate 	    "		A comma separated list of libraries that are to be\n"
3470Sstevel@tonic-gate 	    "		traced.  Only calls from these libraries will be\n"
3480Sstevel@tonic-gate 	    "		traced.  The default is to trace calls from the\n"
3490Sstevel@tonic-gate 	    "		main executable.\n"
3500Sstevel@tonic-gate 	    "		If <bindfromlist> begins with a ! then it defines\n"
3510Sstevel@tonic-gate 	    "		a list of libraries to exclude from the trace.\n"
3520Sstevel@tonic-gate 	    "	-T <bindtolist>\n"
3530Sstevel@tonic-gate 	    "		A comma separated list of libraries that are to be\n"
3540Sstevel@tonic-gate 	    "		traced.  Only calls to these libraries will be\n"
3550Sstevel@tonic-gate 	    "		traced.  The default is to trace all calls.\n"
3560Sstevel@tonic-gate 	    "		If <bindtolist> begins with a ! then it defines\n"
3570Sstevel@tonic-gate 	    "		a list of libraries to exclude from the trace.\n"
3580Sstevel@tonic-gate 	    "	-o <outputfile>\n"
3590Sstevel@tonic-gate 	    "		%s output will be directed to 'outputfile'.\n"
3600Sstevel@tonic-gate 	    "		by default it is placed on stderr\n"
3610Sstevel@tonic-gate 	    "	-f\n"
3620Sstevel@tonic-gate 	    "		Follow all children created by fork() and also\n"
3630Sstevel@tonic-gate 	    "		print apptrace output for the children.  This also\n"
3640Sstevel@tonic-gate 	    "		causes a 'pid' to be added to each output line\n"
3650Sstevel@tonic-gate 	    "	-t <tracelist>\n"
3660Sstevel@tonic-gate 	    "		A comma separated list of interfaces to trace.\n"
3670Sstevel@tonic-gate 	    "		A list preceded by ! is an exlusion list.\n"
3680Sstevel@tonic-gate 	    "	-v <verboselist>\n"
369*1399Sahl 	    "		A comma separated list of interfaces to trace\n"
3700Sstevel@tonic-gate 	    "		verbosely.\n"
3710Sstevel@tonic-gate 	    "		A list preceded by ! is an exclusion list.\n"
3720Sstevel@tonic-gate 	    "		Interfaces matched in -v do not also need to be\n"
3730Sstevel@tonic-gate 	    "		named by -t\n"
3740Sstevel@tonic-gate 	    "	All lists may use shell style wild cards.\n"
3750Sstevel@tonic-gate 	    "	Leading path components or suffixes are not required when\n"
3760Sstevel@tonic-gate 	    "	listing libraries (ie. libc will match /usr/lib/libc.so.1).\n"),
3770Sstevel@tonic-gate 	    prog, prog);
3780Sstevel@tonic-gate }
379