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
usage(char * argv[],const char * msg)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
main(int argc,char ** argv,char ** envp)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
check_trace_error(tnfctl_handle_t * hndl)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
set_default_cmd(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
percmd(expr_t * expr_p,cmd_kind_t kind,fcn_t * fcn_p,boolean_t isnew,void * calldata_p)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 *
perprobe(tnfctl_handle_t * hndl,tnfctl_probe_t * probe_p)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
set_probe_discovery_callback(tnfctl_handle_t * hndl)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
perprobe2(tnfctl_handle_t * hndl,tnfctl_probe_t * probe_p,void * cd)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
process_cmd(tnfctl_handle_t * hndl,cmd_t * cmd)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
get_commands(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
quit(boolean_t killtarget,boolean_t runtarget)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
scanargs(int argc,char ** argv)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
sig_handler(int signo)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
set_signal(void)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
set_tracefile(tnfctl_handle_t * hndl)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
get_data_model(pid_t pid)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
get_executable(char * name)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
get_elf_class(char * filename)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
check_exec_model(char ** argv,char ** envp)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
check_pid_model(char ** argv,char ** envp)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
prex_isaexec(char ** argv,char ** envp)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
cmd_listtracefile()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