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