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