xref: /titanic_50/usr/src/cmd/tnf/prex/main.c (revision 8b464eb836173b92f2b7a65623cd06c8c3c59289)
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 2006 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 /*
29  * Includes
30  */
31 
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <string.h>
36 #include <signal.h>
37 #include <errno.h>
38 #include <locale.h>
39 #include <libintl.h>
40 #include <fcntl.h>
41 #include <limits.h>
42 #include <sys/stat.h>
43 #include <sys/types.h>
44 #include <sys/param.h>
45 #include <sys/procfs.h>
46 #include <libelf.h>
47 #include <gelf.h>
48 #include <sys/systeminfo.h>
49 
50 #include <tnf/tnfctl.h>
51 
52 #include "set.h"
53 #include "cmd.h"
54 #include "spec.h"
55 #include "expr.h"
56 #include "source.h"
57 #include "list.h"
58 #include "prbk.h"
59 
60 /*
61  * Defines - Project private interfaces
62  */
63 
64 #define	DEBUG_ENTRY		"tnf_probe_debug"
65 #ifdef TESTING
66 #define	EMPTY_ENTRY		"tnf_probe_empty"
67 #endif
68 
69 #define	USER_OUTSIZE		(4*1024*1024)
70 #define	KERNEL_OUTSIZE		(384*1024)
71 
72 #if defined(__sparc)
73 #define	PREX32DIR	"/sparcv7/"
74 #elif defined(__i386) || defined(__amd64)
75 #define	PREX32DIR	"/i86/"
76 #endif
77 #define	PREX32EXEC	"/usr/bin" PREX32DIR "prex"
78 
79 /*
80  * Globals
81  */
82 
83 char			**g_argv;	/* copy of argv pointer */
84 tnfctl_handle_t		*g_hndl;	/* handle on target or kernel */
85 
86 static int		g_verbose;	/* debugging to stderr */
87 static char		*g_cmdname;	/* target command name */
88 static char		**g_cmdargs;	/* target command args */
89 static pid_t		g_targetpid;	/* target process id */
90 static volatile boolean_t g_getcmds;	/* accept input flag */
91 static boolean_t	g_testflag;	/* asserted in test mode */
92 static char		*g_preload;	/* objects to preload */
93 static char		*g_outname;	/* tracefile name */
94 static char		*tracefile;	/* tracefile name used by list cmd */
95 int			g_outsize;	/* tracefile size */
96 boolean_t		g_kernelmode;	/* -k flag: kernel mode */
97 static int		prex_dmodel;	/* prex data model */
98 /*
99  * Local Declarations
100  */
101 
102 static void usage(char **argv, const char *msg);
103 static void scanargs(int argc, char **argv);
104 static int set_signal(void);
105 static int get_data_model(pid_t pid);
106 static int get_elf_class(char *filename);
107 static int get_executable(char *);
108 static void prex_isaexec(char **argv, char **envp);
109 static void check_pid_model(char **argv, char **envp);
110 static void check_exec_model(char **argv, char **envp);
111 
112 /* #### - FIXME - need to put this in a private header file */
113 extern void err_fatal(char *s, ...);
114 
115 extern int	  yyparse(void);
116 
117 static tnfctl_errcode_t check_trace_error(tnfctl_handle_t *hndl);
118 static void set_default_cmd(void);
119 static void get_commands(void);
120 static tnfctl_errcode_t set_tracefile(tnfctl_handle_t *hndl);
121 static tnfctl_errcode_t set_probe_discovery_callback(tnfctl_handle_t *hndl);
122 static void * perprobe(tnfctl_handle_t *hndl, tnfctl_probe_t *probe_p);
123 static tnfctl_errcode_t perprobe2(tnfctl_handle_t *hndl,
124 	tnfctl_probe_t *probe_p, void *ignored);
125 static tnfctl_errcode_t percmd(expr_t *expr_p, cmd_kind_t kind, fcn_t *fcn_p,
126 	boolean_t isnew, void *calldata_p);
127 void quit(boolean_t killtarget, boolean_t runtarget);
128 void cmd_listtracefile();
129 
130 
131 /*
132  * usage() - gives a description of the arguments, and exits
133  */
134 
135 static void
136 usage(char *argv[], const char *msg)
137 {
138 	if (msg)
139 		(void) fprintf(stderr,
140 			gettext("%s: %s\n"), argv[0], msg);
141 
142 	(void) fprintf(stderr, gettext(
143 		"usage: %s [options] <cmd> [cmd-args...]\n"), argv[0]);
144 	(void) fprintf(stderr, gettext(
145 		"usage: %s [options] -p <pid>\n"), argv[0]);
146 	(void) fprintf(stderr, gettext(
147 		"usage: %s -s <kbytes-size> -k\n"), argv[0]);
148 	(void) fprintf(stderr, gettext(
149 		"options:\n"));
150 	(void) fprintf(stderr, gettext(
151 		"	-o <outfilename>   set trace output file name\n"));
152 	(void) fprintf(stderr, gettext(
153 		"	-s <kbytes-size>   set trace file size\n"));
154 	(void) fprintf(stderr, gettext(
155 		"	-l <sharedobjs>    shared objects to "
156 		"be preloaded (cmd only)\n"));
157 
158 	exit(1);
159 }
160 
161 
162 /*
163  * main() -
164  */
165 
166 int
167 main(int argc, char **argv, char **envp)
168 {
169 	tnfctl_errcode_t	err = TNFCTL_ERR_NONE;
170 	int			sys_err;
171 	tnfctl_trace_attrs_t	trace_attrs;
172 	tnfctl_event_t		event = TNFCTL_EVENT_EINTR;
173 	pid_t			prex_pid;
174 
175 	/* internationalization stuff */
176 	(void) setlocale(LC_ALL, "");
177 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
178 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
179 #endif
180 	(void) textdomain(TEXT_DOMAIN);
181 
182 	g_argv = argv;
183 
184 	prex_pid = getpid();
185 #if defined(DEBUG)
186 	fprintf(stderr, "### prex_pid = %d ###\n", prex_pid);
187 #endif
188 	prex_dmodel = get_data_model(prex_pid);
189 #if defined(DEBUG)
190 	fprintf(stderr, "### prex_dmodel = %d ###\n", prex_dmodel);
191 #endif
192 	scanargs(argc, argv);
193 
194 	if (g_kernelmode) {
195 		/* prexing the kernel */
196 		err = tnfctl_kernel_open(&g_hndl);
197 		if (err) {
198 			err_fatal(gettext(
199 				"%s: trouble attaching to the kernel: %s\n"),
200 				argv[0], tnfctl_strerror(err));
201 		}
202 	} else {
203 		/* prexing a user process */
204 		if (g_targetpid != 0) {
205 			/* check data model */
206 			check_pid_model(argv, envp);
207 			/* attach case */
208 			err = tnfctl_pid_open(g_targetpid, &g_hndl);
209 			if (err == TNFCTL_ERR_NOLIBTNFPROBE) {
210 				err_fatal(gettext(
211 					"%s: missing symbols, is "
212 					"libtnfprobe.so loaded in target?\n"),
213 					argv[0], tnfctl_strerror(err));
214 			} else if (err) {
215 				err_fatal(gettext(
216 					"%s: trouble attaching to target "
217 					"process: %s\n"),
218 					argv[0], tnfctl_strerror(err));
219 			}
220 		} else {
221 			/* check elf class model */
222 			check_exec_model(argv, envp);
223 			/* exec case */
224 			err = tnfctl_exec_open(g_cmdname, g_cmdargs, NULL,
225 					g_preload, NULL, &g_hndl);
226 			if (err == TNFCTL_ERR_NONE)
227 				err = tnfctl_trace_attrs_get(g_hndl,
228 						&trace_attrs);
229 			if (err) {
230 				err_fatal(gettext(
231 					"%s: trouble creating target process: "
232 					"%s\n"),
233 					argv[0], tnfctl_strerror(err));
234 			}
235 			g_targetpid = trace_attrs.targ_pid;
236 		}
237 
238 		sys_err = set_signal();
239 		if (sys_err)
240 			err_fatal(gettext(
241 				"%s: trouble setting up signal handler: %s\n"),
242 				argv[0], strerror(err));
243 	}
244 
245 	/* initialize the source stack for the parser */
246 	source_init();
247 
248 	if (!g_kernelmode) {
249 		/* set the tracefile name and size */
250 		err = set_tracefile(g_hndl);
251 		if (err) {
252 			(void) fprintf(stderr, gettext(
253 				"%s: trouble initializing tracefile: %s\n"),
254 				argv[0], tnfctl_strerror(err));
255 			goto Cleanup;
256 		}
257 		err = check_trace_error(g_hndl);
258 		if (err) {
259 			(void) fprintf(stderr, gettext(
260 				"%s: cannot read tracing status : %s\n"),
261 				argv[0], tnfctl_strerror(err));
262 			goto Cleanup;
263 		}
264 	}
265 
266 	/* accept commands from stdin the first time through */
267 	g_getcmds = B_TRUE;
268 
269 	/* set up default aliases */
270 	set_default_cmd();
271 
272 	/* set up creator/destructor function to call for new probes */
273 	err = set_probe_discovery_callback(g_hndl);
274 	if (err) {
275 		(void) fprintf(stderr, gettext(
276 			"%s: error in probe discovery : %s\n"),
277 			argv[0], tnfctl_strerror(err));
278 		goto Cleanup;
279 	}
280 
281 	if (g_kernelmode) {
282 		prbk_warn_pfilter_empty();
283 	}
284 
285 	while (err == TNFCTL_ERR_NONE) {
286 
287 		if (g_kernelmode || g_getcmds) {
288 			g_getcmds = B_FALSE;
289 			get_commands();
290 		}
291 
292 		if (!g_kernelmode && (g_getcmds == B_FALSE)) {
293 		    err = tnfctl_continue(g_hndl, &event, NULL);
294 		    if (err) {
295 			(void) fprintf(stderr, gettext(
296 				"%s: cannot continue target : %s\n"),
297 				argv[0], tnfctl_strerror(err));
298 			goto Cleanup;
299 		    }
300 		}
301 		err = check_trace_error(g_hndl);
302 		if (err) {
303 			(void) fprintf(stderr, gettext(
304 				"%s: cannot read tracing status : %s\n"),
305 				argv[0], tnfctl_strerror(err));
306 			goto Cleanup;
307 		}
308 		if (!g_kernelmode) {
309 			if (event == TNFCTL_EVENT_EXEC) {
310 			    (void) printf(gettext(
311 				"Target process exec'd\n"));
312 			    quit(B_FALSE, B_TRUE);	/* quit resume */
313 			} else if (event == TNFCTL_EVENT_EXIT) {
314 			    /* target exited */
315 			    (void) fprintf(stderr, gettext(
316 				"%s: target process exited\n"),
317 				g_argv[0]);
318 			    goto Cleanup;
319 			} else if (event == TNFCTL_EVENT_TARGGONE) {
320 				/* target terminated */
321 			    (void) fprintf(stderr,
322 	gettext("%s: target process disappeared (without calling exit)\n"),
323 				g_argv[0]);
324 			    goto Cleanup;
325 			}
326 		}
327 	}
328 
329 Cleanup:
330 	err = tnfctl_close(g_hndl, TNFCTL_TARG_DEFAULT);
331 	if (err)
332 		(void) fprintf(stderr, gettext(
333 			"%s: error on closing : %s\n"),
334 			argv[0], tnfctl_strerror(err));
335 
336 	exit(0);
337 
338 	return (0);
339 
340 }
341 
342 /*
343  * check_trace_error() - checks whether there was an error in tracing
344  */
345 static tnfctl_errcode_t
346 check_trace_error(tnfctl_handle_t *hndl)
347 {
348 	tnfctl_trace_attrs_t	trace_attrs;
349 	tnfctl_errcode_t	err;
350 
351 	err = tnfctl_trace_attrs_get(hndl, &trace_attrs);
352 	if (err)
353 		return (err);
354 
355 	if (trace_attrs.trace_buf_state == TNFCTL_BUF_BROKEN) {
356 		(void) printf(gettext("Tracing shut down in target program "
357 			"due to an internal error - Please restart prex "
358 			"and target\n"));
359 	}
360 
361 	return (TNFCTL_ERR_NONE);
362 }
363 
364 /*
365  * set_default_cmd() - set the default debug entry and $all
366  */
367 static void
368 set_default_cmd(void)
369 {
370 	if (!g_kernelmode)
371 		fcn(strdup("debug"), DEBUG_ENTRY);
372 #ifdef TESTING
373 	fcn(strdup("empty"), EMPTY_ENTRY);
374 #endif
375 	(void) set(strdup("all"), expr(spec(strdup("keys"), SPEC_EXACT),
376 				spec(strdup(".*"), SPEC_REGEXP)));
377 
378 }
379 
380 /*
381  * process() - enable and disable selected probes
382  */
383 
384 typedef struct {
385 	tnfctl_probe_t	*probe_p;
386 	tnfctl_handle_t	*hndl;
387 } process_args_t;
388 
389 static tnfctl_errcode_t
390 percmd(expr_t *expr_p, cmd_kind_t kind, fcn_t *fcn_p, boolean_t isnew,
391 	void *calldata_p)
392 {
393 	process_args_t *args_p = (process_args_t *)calldata_p;
394 	tnfctl_handle_t	*hndl = args_p->hndl;
395 	tnfctl_probe_t	*probe_p = args_p->probe_p;
396 	tnfctl_errcode_t err = TNFCTL_ERR_NONE;
397 	char *attrs;
398 
399 	attrs = list_getattrs(probe_p);
400 
401 	if (expr_match(expr_p, attrs)) {
402 #if defined(DEBUG) || defined(lint)
403 		if (g_verbose) {
404 			char		   *cmdstr[] = {
405 				"enable", "disable",
406 				"connect", "clear",
407 				"trace", "untrace"};
408 
409 			(void) fprintf(stderr, ": %s command: %s ",
410 				(isnew) ? "new" : "old", cmdstr[kind]);
411 			expr_print(stderr, expr_p);
412 		}
413 #endif
414 
415 		switch (kind) {
416 		case CMD_ENABLE:
417 			err = tnfctl_probe_enable(hndl, probe_p, NULL);
418 			break;
419 		case CMD_DISABLE:
420 			err = tnfctl_probe_disable(hndl, probe_p, NULL);
421 			break;
422 		case CMD_TRACE:
423 			err = tnfctl_probe_trace(hndl, probe_p, NULL);
424 			break;
425 		case CMD_UNTRACE:
426 			err = tnfctl_probe_untrace(hndl, probe_p, NULL);
427 			break;
428 		case CMD_CONNECT:
429 			err = tnfctl_probe_connect(hndl, probe_p, NULL,
430 				fcn_p->entry_name_p);
431 			break;
432 		case CMD_CLEAR:
433 			err = tnfctl_probe_disconnect_all(hndl, probe_p, NULL);
434 			break;
435 		}
436 
437 #if defined(DEBUG) || defined(lint)
438 		if (g_verbose)
439 			(void) fprintf(stderr, "\n");
440 #endif
441 
442 	}
443 	if (attrs)
444 		free(attrs);
445 
446 	return (err);
447 
448 }
449 
450 /*ARGSUSED*/
451 static void *
452 perprobe(tnfctl_handle_t *hndl, tnfctl_probe_t *probe_p)
453 {
454 	process_args_t  args;
455 	tnfctl_errcode_t err;
456 
457 	args.probe_p = probe_p;
458 	args.hndl = hndl;
459 	err = cmd_traverse(percmd, &args);
460 	if (err) {
461 		(void) fprintf(stderr, gettext(
462 				"%s: error on new (dlopened) probe : %s\n"),
463 				g_argv[0], tnfctl_strerror(err));
464 	}
465 	return (NULL);
466 }
467 
468 static tnfctl_errcode_t
469 set_probe_discovery_callback(tnfctl_handle_t *hndl)
470 {
471 	tnfctl_errcode_t err;
472 
473 	err = tnfctl_register_funcs(hndl, perprobe, NULL);
474 	if (err)
475 		return (err);
476 
477 	return (TNFCTL_ERR_NONE);
478 }
479 
480 static tnfctl_errcode_t
481 perprobe2(tnfctl_handle_t *hndl, tnfctl_probe_t *probe_p, void *cd)
482 {
483 	cmd_t		*cmd = cd;
484 	process_args_t  args;
485 	tnfctl_errcode_t err;
486 
487 	args.probe_p = probe_p;
488 	args.hndl = hndl;
489 	err = cmd_callback(cmd, percmd, &args);
490 	if (err) {
491 		(void) fprintf(stderr, gettext(
492 				"%s: error on probe operation: %s\n"),
493 				g_argv[0], tnfctl_strerror(err));
494 	}
495 	return (err);
496 }
497 
498 void
499 process_cmd(tnfctl_handle_t *hndl, cmd_t *cmd)
500 {
501 #if defined(DEBUG) || defined(lint)
502 	if (g_verbose)
503 		(void) fprintf(stderr, "processing commands\n");
504 #endif
505 	(void) tnfctl_probe_apply(hndl, perprobe2, cmd);
506 }
507 
508 /*
509  * get_commands() - process commands from stdin
510  */
511 static void
512 get_commands(void)
513 {
514 	/* Read commands from STDIN */
515 	if (g_kernelmode) {
516 		(void) printf(gettext("Type \"help\" for help ...\n"));
517 	} else {
518 		if (g_testflag)
519 			(void) printf("prex(%ld), target(%ld): ",
520 					getpid(), g_targetpid);
521 		(void) printf(gettext("Target process stopped\n"));
522 		(void) printf(gettext(
523 			"Type \"continue\" to resume the target, "
524 			"\"help\" for help ...\n"));
525 	}
526 
527 	while (yyparse());
528 }
529 
530 
531 /*
532  * quit() - called to quit the controlling process. The boolean argument
533  * specifies whether to terminate the target as well.
534  */
535 
536 void
537 quit(boolean_t killtarget, boolean_t runtarget)
538 {
539 	tnfctl_errcode_t err;
540 
541 	if (killtarget && runtarget)
542 		err = tnfctl_close(g_hndl, TNFCTL_TARG_DEFAULT);
543 	else if (killtarget && !runtarget)
544 		err = tnfctl_close(g_hndl, TNFCTL_TARG_KILL);
545 	else if (!killtarget && runtarget)
546 		err = tnfctl_close(g_hndl, TNFCTL_TARG_RESUME);
547 	else if (!killtarget && !runtarget)
548 		err = tnfctl_close(g_hndl, TNFCTL_TARG_SUSPEND);
549 	if (err) {
550 		(void) fprintf(stderr, gettext(
551 				"%s: trouble quitting : %s\n"),
552 				g_argv[0], tnfctl_strerror(err));
553 		exit(1);
554 	}
555 	exit(0);
556 }
557 
558 
559 /*
560  * scanargs() - processes the command line arguments
561  */
562 
563 #define	strneq(s1, s2, n) 	(strncmp(s1, s2, n) == 0)
564 
565 static void
566 scanargs(int argc,
567 	char **argv)
568 {
569 	int			 c;
570 #if defined(DEBUG) || defined(lint)
571 	char		   *optstr = "l:o:p:s:tkv:";	/* debugging options */
572 #else
573 	char		   *optstr = "l:o:p:s:tk";	/* production options */
574 #endif
575 
576 	/* set up some defaults */
577 	g_targetpid = 0;
578 	g_cmdname = NULL;
579 	g_cmdargs = NULL;
580 	g_preload = NULL;
581 	g_outname = NULL;
582 	g_outsize = -1;
583 
584 	while ((c = getopt(argc, argv, optstr)) != EOF) {
585 		switch (c) {
586 		case 'l':	/* preload objects */
587 			g_preload = optarg;
588 			break;
589 		case 'o':	/* tracefile name */
590 			g_outname = optarg;
591 			break;
592 		case 'p':	/* target pid (attach case) */
593 			g_targetpid = atoi(optarg);
594 			break;
595 		case 's':	/* tracefile size */
596 			g_outsize = atoi(optarg) * 1024;
597 			break;
598 		case 't':	/* test flag */
599 			g_testflag = B_TRUE;
600 			(void) setvbuf(stdout, NULL, _IOLBF, 0);
601 			break;
602 		case 'k':	/* kernel mode */
603 			g_kernelmode = B_TRUE;
604 			break;
605 #if defined(DEBUG) || defined(lint)
606 		case 'v':	/* verbose flag */
607 			g_verbose = atoi(optarg);
608 			break;
609 #endif
610 		case '?':	/* error case */
611 			usage(argv, gettext("unrecognized argument"));
612 		}
613 	}
614 
615 	if (optind < argc) {
616 		g_cmdname = strdup(argv[optind]);
617 		g_cmdargs = &argv[optind];
618 	}
619 	/* sanity clause */
620 	if (!g_kernelmode && (g_cmdname == NULL && g_targetpid == 0))
621 		usage(argv, gettext("need to specify cmd or pid"));
622 	if (g_cmdname != NULL && g_targetpid != 0)
623 		usage(argv, gettext("can't specify both cmd and pid"));
624 	if (g_targetpid && g_preload)
625 		usage(argv, gettext("can't use preload option with attach"));
626 	if (g_kernelmode) {
627 		if (g_outname)
628 			usage(argv, "can't specify a filename in kernel mode");
629 		if (g_cmdname)
630 			usage(argv, "can't specify a command in kernel mode");
631 		if (g_targetpid)
632 			usage(argv, "can't specify pid in kernel mode");
633 		if (g_preload)
634 			usage(argv, "can't use preload option in kernel mode");
635 	}
636 	/* default output size */
637 	if (g_outsize == -1)
638 		g_outsize = g_kernelmode ? KERNEL_OUTSIZE : USER_OUTSIZE;
639 
640 #ifdef OLD
641 	int			 i;
642 
643 	for (i = 1; i < argc; i++) {
644 		if (strneq(argv[i], "-v", 2)) {
645 			int			 vlevel;
646 
647 			vlevel = (strlen(argv[i]) > 2)? atoi(&argv[i][2]) : 1;
648 			g_verbose = B_TRUE;
649 			prb_verbose_set(vlevel);
650 		} else if (strneq(argv[i], "-pid", 2)) {
651 			if (++i >= argc)
652 				usage(argv, gettext("missing pid argument"));
653 			g_targetpid = atoi(argv[i]);
654 		} else if (strneq(argv[i], "-t", 2)) {
655 			g_testflag = B_TRUE;
656 			(void) setvbuf(stdout, NULL, _IOLBF, 0);
657 		} else if (argv[i][0] != '-') {
658 			g_cmdname = strdup(argv[i]);
659 			if (!g_cmdname) {
660 				err_fatal(gettext(
661 					"%s: out of memory"), argv[0]);
662 			}
663 			if (g_verbose >= 2) {
664 				(void) fprintf(stderr,
665 					"cmdname=%s\n", g_cmdname);
666 			}
667 			/*
668 			 * rest of arguments are the args to the executable -
669 			 * by convention argv[0] should be name of
670 			 * executable, so we don't increment i
671 			 */
672 			g_cmdargs = &argv[i];
673 			break;
674 		} else {
675 			usage(argv, gettext("unrecognized argument"));
676 		}
677 	}
678 #endif
679 
680 }				/* end scanargs */
681 
682 
683 /*
684  * sig_handler() - cleans up if a signal is received
685  */
686 
687 /*ARGSUSED*/
688 static void
689 sig_handler(int signo)
690 {
691 	g_getcmds = B_TRUE;
692 }				/* end sig_handler */
693 
694 
695 /*
696  * set_signal() -  sets up function to call for clean up
697  */
698 
699 static int
700 set_signal(void)
701 {
702 	struct sigaction newact;
703 
704 	newact.sa_handler = sig_handler;
705 	(void) sigemptyset(&newact.sa_mask);
706 	newact.sa_flags = 0;
707 	if (sigaction(SIGINT, &newact, NULL) < 0) {
708 		return (errno);
709 	}
710 	return (0);
711 }
712 
713 
714 /*
715  * set_tracefile() - initializes tracefile, sets the tracefile name and size
716  */
717 static tnfctl_errcode_t
718 set_tracefile(tnfctl_handle_t *hndl)
719 {
720 	tnfctl_errcode_t	err;
721 	tnfctl_trace_attrs_t	attrs;
722 	size_t			minoutsize;
723 	char			path[MAXPATHLEN];
724 	char			*outfile_name;
725 	char			*tmpdir;
726 
727 	/* Init tracefile name used by list cmd */
728 	tracefile = NULL;
729 	err = tnfctl_trace_attrs_get(hndl, &attrs);
730 	if (err)
731 		return (err);
732 
733 	if (attrs.trace_buf_state == TNFCTL_BUF_BROKEN)
734 		return (TNFCTL_ERR_BUFBROKEN);
735 	if (attrs.trace_buf_state == TNFCTL_BUF_OK) {
736 		/* trace file set already - can't change it */
737 		return (TNFCTL_ERR_NONE);
738 	}
739 
740 	minoutsize = attrs.trace_min_size;
741 	if (g_outsize < minoutsize)	{
742 		(void) fprintf(stderr,
743 			gettext("specified tracefile size smaller then "
744 				"minimum; setting to %d kbytes\n"),
745 				minoutsize / 1024);
746 		g_outsize = minoutsize;
747 	}
748 
749 	/* where is $TMPDIR? */
750 	tmpdir = getenv("TMPDIR");
751 	if (!tmpdir || *tmpdir == '\0') {
752 		tmpdir = "/tmp";
753 	}
754 
755 	/* do we have an absolute, relative or no pathname specified? */
756 	if (g_outname == NULL) {
757 		/* default, no tracefile specified */
758 		if ((strlen(tmpdir) + 1 + 20) > (size_t)MAXPATHLEN) {
759 			(void) fprintf(stderr, gettext(
760 				"%s: $TMPDIR too long\n"), g_argv[0]);
761 			exit(1);
762 		}
763 		(void) sprintf(path, "%s/trace-%ld", tmpdir, g_targetpid);
764 		outfile_name = path;
765 	} else {
766 		/* filename specified */
767 		outfile_name = g_outname;
768 	}
769 	tracefile = strdup(outfile_name);
770 	if (tracefile == NULL) {
771 		if ((errno == ENOMEM) || (errno == EAGAIN)) {
772 			return (TNFCTL_ERR_ALLOCFAIL);
773 		} else {
774 			return (TNFCTL_ERR_INTERNAL);
775 		}
776 	}
777 
778 #if defined(DEBUG) || defined(lint)
779 	if (g_verbose)
780 		(void) fprintf(stderr,
781 			"setting tracefile name=\"%s\", size=%d\n",
782 			path, g_outsize);
783 #endif
784 	err = tnfctl_buffer_alloc(hndl, outfile_name, g_outsize);
785 	return (err);
786 }
787 /*
788  * get_data_model() - get the process data model from psinfo
789  *		      structure.
790  */
791 #define	PROCFORMAT	"/proc/%d"
792 static int
793 get_data_model(pid_t pid)
794 {
795 	char	path[MAXPATHLEN];
796 	int	fd, dmodel = -1;
797 	prpsinfo_t	psinfo;
798 
799 	(void) sprintf(path, PROCFORMAT, (int)pid);
800 	fd = open(path, O_RDONLY);
801 	if (fd == -1)
802 	    return (dmodel);
803 	if ((dmodel = ioctl(fd, PIOCPSINFO, &psinfo)) == -1)
804 	    return (dmodel);
805 	return ((int)psinfo.pr_dmodel);
806 }
807 /*
808  * get_executable - return file descriptor for PATH-resolved
809  *		    target file.
810  *
811  */
812 static int
813 get_executable(char *name) {
814     int fd = -1;
815 
816     if (name != NULL) {
817 	char path[PATH_MAX + 1];
818 	char line[MAX_INPUT + 1];
819 	char *p = line;
820 	char *fname = name;
821 	int N = sizeof (line);
822 	struct stat file_att;
823 
824 	while (*fname == ' ') fname++;
825 	if (fname[0] == '-' || strchr(fname, '/')) {
826 	    fd = open(fname, O_RDONLY);
827 	} else {
828 	    int len = strlen(fname);
829 	    char *dirlist = getenv("PATH");
830 	    char *dir = NULL;
831 
832 	    if (dirlist != NULL) {
833 		dirlist = strdup(dirlist);
834 		dir = strtok(dirlist, ":");
835 	    }
836 	    while (fd < 0 && dir != NULL) {
837 		if ((strlen(dir) + len + 1) < sizeof (path)) {
838 		    strcat(strcat(strcpy(path, dir), "/"), fname);
839 		    fd = open(path, O_RDONLY);
840 		}
841 		dir = strtok(NULL, ":");
842 	    }
843 	    if (dirlist != NULL) free(dirlist);
844 	}
845 	if (fstat(fd, &file_att) || !S_ISREG(file_att.st_mode)) {
846 	    if (fd >= 0)
847 		close(fd);
848 	    return (-1);
849 	}
850 	if (read(fd, p, 2) && p[0] == '#' && p[1] == '!') {
851 	    while (N-- > 1 && read(fd, p, 1) && *p != '\n')
852 		p++;
853 	    *p = '\0';
854 	    close(fd);
855 	    return (get_executable(line));
856 	}
857 	if (fd >= 0) lseek(fd, 0, SEEK_SET);
858 	} /* %$#@! cstyle complaint */
859     return (fd);
860 }
861 
862 /*
863  * get_elf_class - get the target executable elf class
864  *                 i.e. ELFCLASS64 or ELFCLASS32.
865  */
866 static int
867 get_elf_class(char *filename)
868 {
869 	int	elfclass = -1;
870 	int	elffd = get_executable(filename);
871 	Elf	*elf;
872 	size_t	size;
873 	char	*ident;
874 	GElf_Ehdr	ehdr;
875 
876 	if (elffd < 0)
877 		return (elfclass);
878 	if (elf_version(EV_CURRENT) == EV_NONE) {
879 		(void) close(elffd);
880 		return (elfclass);
881 	}
882 	elf = elf_begin(elffd, ELF_C_READ, (Elf *) 0);
883 	/*
884 	 * verify information in file header
885 	 */
886 	if (gelf_getehdr(elf, &ehdr) == (GElf_Ehdr *) 0) {
887 		close(elffd);
888 		return (elfclass);
889 	}
890 	ident = elf_getident(elf, &size);
891 	if (ident[EI_CLASS] == ELFCLASS32)
892 		elfclass = ELFCLASS32;
893 	if (ident[EI_CLASS] == ELFCLASS64)
894 		elfclass = ELFCLASS64;
895 	close(elffd);
896 	return (elfclass);
897 }
898 /*
899  * check_exec_model() - check the consistency between prex data model
900  *                      and target elf class and act accordingly
901  */
902 static void
903 check_exec_model(char **argv, char **envp)
904 {
905 	int	elfclass;
906 
907 	elfclass = get_elf_class(g_cmdname);
908 	if (((elfclass == ELFCLASS32) && (prex_dmodel == PR_MODEL_ILP32)) ||
909 	    ((elfclass == ELFCLASS64) && (prex_dmodel == PR_MODEL_LP64)))
910 	    return;
911 	if ((prex_dmodel == PR_MODEL_ILP32) &&
912 	    (elfclass == ELFCLASS64)) {
913 	    (void) fprintf(stderr, gettext(
914 		"Error: 32 bit prex can not exec 64 bit target\n"));
915 	    exit(1);
916 	}
917 	if ((prex_dmodel == PR_MODEL_LP64) &&
918 	    (elfclass == ELFCLASS32))
919 	    prex_isaexec(argv, envp);
920 }
921 
922 /*
923  * check_pid_model() - check the consistency between prex data model
924  *                     and target data model and act accordingly
925  */
926 static void
927 check_pid_model(char **argv, char **envp)
928 {
929 	int	dmodel;
930 
931 	dmodel = get_data_model(g_targetpid);
932 	if (prex_dmodel == dmodel)
933 		return;
934 	if ((prex_dmodel == PR_MODEL_ILP32) &&
935 		(dmodel == PR_MODEL_LP64)) {
936 		(void) fprintf(stderr, gettext(
937 		    "Error: 32 bit prex can not exec 64 bit target\n"));
938 		exit(1);
939 	}
940 	if ((prex_dmodel == PR_MODEL_LP64) &&
941 		(dmodel == PR_MODEL_ILP32))
942 		prex_isaexec(argv, envp);
943 }
944 /*
945  * prex_isaexec() - there is only one case this function get called
946  *                  64 bit prex, 32 bit target, need to exec 32 bit
947  *                  prex here.
948  */
949 static void
950 prex_isaexec(char **argv, char **envp)
951 {
952 	char path[PATH_MAX + sizeof (PREX32DIR)];
953 	strcat(strcat(strcpy(path, dirname(dirname(argv[0]))), PREX32DIR),
954 	    basename(argv[0]));
955 	if (get_elf_class(path) != ELFCLASS32)
956 	    strcpy(path, PREX32EXEC);
957 	argv[0] = path;
958 	(void) execve(path, argv, envp);
959 	(void) fprintf(stderr,
960 	    gettext("%s: execve(\"%s\") failed\n"),
961 	    argv[0], path);
962 	exit(1);
963 }
964 void
965 cmd_listtracefile()
966 {
967 
968 	if (g_kernelmode) {
969 	    (void) fprintf(stderr,
970 		    gettext("There is no trace file in kernel mode!\n"));
971 	} else {
972 	    (void) printf(gettext("Current trace file is: %s\n"), tracefile);
973 	}
974 }
975