1*0Sstevel@tonic-gate# 2*0Sstevel@tonic-gate# ident "%Z%%M% %I% %E% SMI" 3*0Sstevel@tonic-gate# 4*0Sstevel@tonic-gate# Copyright 2005 Sun Microsystems, Inc. All rights reserved. 5*0Sstevel@tonic-gate# Use is subject to license terms. 6*0Sstevel@tonic-gate# 7*0Sstevel@tonic-gate# CDDL HEADER START 8*0Sstevel@tonic-gate# 9*0Sstevel@tonic-gate# The contents of this file are subject to the terms of the 10*0Sstevel@tonic-gate# Common Development and Distribution License, Version 1.0 only 11*0Sstevel@tonic-gate# (the "License"). You may not use this file except in compliance 12*0Sstevel@tonic-gate# with the License. 13*0Sstevel@tonic-gate# 14*0Sstevel@tonic-gate# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 15*0Sstevel@tonic-gate# or http://www.opensolaris.org/os/licensing. 16*0Sstevel@tonic-gate# See the License for the specific language governing permissions 17*0Sstevel@tonic-gate# and limitations under the License. 18*0Sstevel@tonic-gate# 19*0Sstevel@tonic-gate# When distributing Covered Code, include this CDDL HEADER in each 20*0Sstevel@tonic-gate# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 21*0Sstevel@tonic-gate# If applicable, add the following below this CDDL HEADER, with the 22*0Sstevel@tonic-gate# fields enclosed by brackets "[]" replaced with your own identifying 23*0Sstevel@tonic-gate# information: Portions Copyright [yyyy] [name of copyright owner] 24*0Sstevel@tonic-gate# 25*0Sstevel@tonic-gate# CDDL HEADER END 26*0Sstevel@tonic-gate# 27*0Sstevel@tonic-gate 28*0Sstevel@tonic-gate# 29*0Sstevel@tonic-gate# This module contains utility routines and data for use by the appcert 30*0Sstevel@tonic-gate# programs: appcert, symprof, symcheck, and symreport. 31*0Sstevel@tonic-gate# 32*0Sstevel@tonic-gate 33*0Sstevel@tonic-gatepackage AppcertUtil; 34*0Sstevel@tonic-gate 35*0Sstevel@tonic-gaterequire 5.005; 36*0Sstevel@tonic-gateuse strict; 37*0Sstevel@tonic-gateuse locale; 38*0Sstevel@tonic-gateuse Getopt::Std; 39*0Sstevel@tonic-gateuse POSIX qw(locale_h); 40*0Sstevel@tonic-gateuse Sun::Solaris::Utils qw(textdomain gettext); 41*0Sstevel@tonic-gateuse File::Basename; 42*0Sstevel@tonic-gateuse File::Path; 43*0Sstevel@tonic-gate 44*0Sstevel@tonic-gateBEGIN { 45*0Sstevel@tonic-gate use Exporter(); 46*0Sstevel@tonic-gate use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); 47*0Sstevel@tonic-gate 48*0Sstevel@tonic-gate @ISA = qw(Exporter); 49*0Sstevel@tonic-gate @EXPORT = qw( 50*0Sstevel@tonic-gate $command_name 51*0Sstevel@tonic-gate $object_dir 52*0Sstevel@tonic-gate $solaris_library_ld_path 53*0Sstevel@tonic-gate $uname_p 54*0Sstevel@tonic-gate $working_dir 55*0Sstevel@tonic-gate $appcert_lib_dir 56*0Sstevel@tonic-gate $batch_report 57*0Sstevel@tonic-gate $binary_count 58*0Sstevel@tonic-gate $block_min 59*0Sstevel@tonic-gate $block_max 60*0Sstevel@tonic-gate $tmp_dir 61*0Sstevel@tonic-gate 62*0Sstevel@tonic-gate $cmd_dump 63*0Sstevel@tonic-gate $cmd_elfdump 64*0Sstevel@tonic-gate $cmd_file 65*0Sstevel@tonic-gate $cmd_find 66*0Sstevel@tonic-gate $cmd_ldd 67*0Sstevel@tonic-gate $cmd_ls 68*0Sstevel@tonic-gate $cmd_more 69*0Sstevel@tonic-gate $cmd_pvs 70*0Sstevel@tonic-gate $cmd_sort 71*0Sstevel@tonic-gate $cmd_uname 72*0Sstevel@tonic-gate $cmd_uniq 73*0Sstevel@tonic-gate 74*0Sstevel@tonic-gate @lib_index_loaded 75*0Sstevel@tonic-gate 76*0Sstevel@tonic-gate %lib_index_definition 77*0Sstevel@tonic-gate %text 78*0Sstevel@tonic-gate %model_tweak 79*0Sstevel@tonic-gate %skip_symbols 80*0Sstevel@tonic-gate %scoped_symbol 81*0Sstevel@tonic-gate %scoped_symbol_all 82*0Sstevel@tonic-gate %warnings_bind 83*0Sstevel@tonic-gate %warnings_desc 84*0Sstevel@tonic-gate %warnings_match 85*0Sstevel@tonic-gate 86*0Sstevel@tonic-gate &object_to_dir_name 87*0Sstevel@tonic-gate &dir_name_to_path 88*0Sstevel@tonic-gate &next_dir_name 89*0Sstevel@tonic-gate &cmd_output_file 90*0Sstevel@tonic-gate &cmd_output_dump 91*0Sstevel@tonic-gate &all_ldd_neededs 92*0Sstevel@tonic-gate &all_ldd_neededs_string 93*0Sstevel@tonic-gate &direct_syms 94*0Sstevel@tonic-gate &import_vars_from_environment 95*0Sstevel@tonic-gate &export_vars_to_environment 96*0Sstevel@tonic-gate &c_locale 97*0Sstevel@tonic-gate &overall_result_code 98*0Sstevel@tonic-gate &trim 99*0Sstevel@tonic-gate &sort_on_count 100*0Sstevel@tonic-gate &print_line 101*0Sstevel@tonic-gate &list_format 102*0Sstevel@tonic-gate &emsg 103*0Sstevel@tonic-gate &pmsg 104*0Sstevel@tonic-gate &nofile 105*0Sstevel@tonic-gate &nopathexist 106*0Sstevel@tonic-gate &norunprog 107*0Sstevel@tonic-gate &nocreatedir 108*0Sstevel@tonic-gate &exiter 109*0Sstevel@tonic-gate &set_clean_up_exit_routine 110*0Sstevel@tonic-gate &signals 111*0Sstevel@tonic-gate &create_tmp_dir 112*0Sstevel@tonic-gate &dir_is_empty 113*0Sstevel@tonic-gate &follow_symlink 114*0Sstevel@tonic-gate &is_statically_linked 115*0Sstevel@tonic-gate &is_elf 116*0Sstevel@tonic-gate &is_shared_object 117*0Sstevel@tonic-gate &is_aout 118*0Sstevel@tonic-gate &is_suid 119*0Sstevel@tonic-gate &bin_type 120*0Sstevel@tonic-gate &files_equal 121*0Sstevel@tonic-gate &purge_caches 122*0Sstevel@tonic-gate &filter_lib_type 123*0Sstevel@tonic-gate &load_model_index 124*0Sstevel@tonic-gate &load_misc_check_databases 125*0Sstevel@tonic-gate ); 126*0Sstevel@tonic-gate 127*0Sstevel@tonic-gate @EXPORT_OK = (); 128*0Sstevel@tonic-gate 129*0Sstevel@tonic-gate %EXPORT_TAGS = (); 130*0Sstevel@tonic-gate} 131*0Sstevel@tonic-gate 132*0Sstevel@tonic-gateuse vars @EXPORT; 133*0Sstevel@tonic-gateuse vars @EXPORT_OK; 134*0Sstevel@tonic-gate 135*0Sstevel@tonic-gateuse vars qw( 136*0Sstevel@tonic-gate $lib_match_initialized 137*0Sstevel@tonic-gate 138*0Sstevel@tonic-gate %lib_index 139*0Sstevel@tonic-gate %lib_index_loaded 140*0Sstevel@tonic-gate %shared_object_index 141*0Sstevel@tonic-gate 142*0Sstevel@tonic-gate %file_inode_cache 143*0Sstevel@tonic-gate %file_exists_cache 144*0Sstevel@tonic-gate %filter_lib_cache 145*0Sstevel@tonic-gate %lib_match_cache 146*0Sstevel@tonic-gate %cmd_output_file_cache 147*0Sstevel@tonic-gate %cmd_output_dump_cache 148*0Sstevel@tonic-gate %all_ldd_neededs_cache 149*0Sstevel@tonic-gate); 150*0Sstevel@tonic-gate 151*0Sstevel@tonic-gatemy $clean_up_exit_routine; 152*0Sstevel@tonic-gatemy $tmp_dir_count = 0; 153*0Sstevel@tonic-gatemy $next_dir_name_dh; 154*0Sstevel@tonic-gatemy $LC_ALL = ''; 155*0Sstevel@tonic-gate 156*0Sstevel@tonic-gate# Get the name of the program: 157*0Sstevel@tonic-gate$command_name = basename($0); 158*0Sstevel@tonic-gate 159*0Sstevel@tonic-gate$cmd_dump = '/usr/ccs/bin/dump'; 160*0Sstevel@tonic-gate$cmd_elfdump = '/usr/ccs/bin/elfdump'; 161*0Sstevel@tonic-gate$cmd_file = '/usr/bin/file'; 162*0Sstevel@tonic-gate$cmd_find = '/usr/bin/find'; 163*0Sstevel@tonic-gate$cmd_ldd = '/usr/bin/ldd'; 164*0Sstevel@tonic-gate$cmd_ls = '/usr/bin/ls'; 165*0Sstevel@tonic-gate$cmd_more = '/usr/bin/more'; 166*0Sstevel@tonic-gate$cmd_pvs = '/usr/bin/pvs'; 167*0Sstevel@tonic-gate$cmd_sort = '/usr/bin/sort'; 168*0Sstevel@tonic-gate$cmd_uname = '/usr/bin/uname'; 169*0Sstevel@tonic-gate$cmd_uniq = '/usr/bin/uniq'; 170*0Sstevel@tonic-gate 171*0Sstevel@tonic-gatechomp($uname_p = `$cmd_uname -p`); 172*0Sstevel@tonic-gate 173*0Sstevel@tonic-gate 174*0Sstevel@tonic-gate# Initialize constants: 175*0Sstevel@tonic-gate 176*0Sstevel@tonic-gate$solaris_library_ld_path = "/usr/openwin/lib:/usr/dt/lib"; 177*0Sstevel@tonic-gate 178*0Sstevel@tonic-gate# Prefix for every object's profiling (etc) subdir in $working_dir. 179*0Sstevel@tonic-gate$object_dir = 'objects/'; 180*0Sstevel@tonic-gate 181*0Sstevel@tonic-gate$text{'Summary_Result_None_Checked'} = gettext( 182*0Sstevel@tonic-gate "No binaries were checked."); 183*0Sstevel@tonic-gate$text{'Summary_Result_Some_Failed'} = gettext( 184*0Sstevel@tonic-gate "Potential binary stability problem(s) detected."); 185*0Sstevel@tonic-gate$text{'Summary_Result_Some_Incomplete'} = gettext( 186*0Sstevel@tonic-gate "No stability problems detected, but not all binaries were checked."); 187*0Sstevel@tonic-gate$text{'Summary_Result_All_Passed'} = gettext( 188*0Sstevel@tonic-gate "No binary stability problems detected."); 189*0Sstevel@tonic-gate 190*0Sstevel@tonic-gate 191*0Sstevel@tonic-gate$text{'Message_Private_Symbols_Check_Outfile'} = <<"END"; 192*0Sstevel@tonic-gate# 193*0Sstevel@tonic-gate# <binary>|<abi>|<caller>|<callee>|private|<symbol> 194*0Sstevel@tonic-gate# 195*0Sstevel@tonic-gateEND 196*0Sstevel@tonic-gate 197*0Sstevel@tonic-gate$text{'Message_Public_Symbols_Check_Outfile'} = 198*0Sstevel@tonic-gate $text{'Message_Private_Symbols_Check_Outfile'}; 199*0Sstevel@tonic-gate$text{'Message_Public_Symbols_Check_Outfile'} =~ s/private/public/g; 200*0Sstevel@tonic-gate 201*0Sstevel@tonic-gate# 202*0Sstevel@tonic-gate# Maps a filesystem path of a binary object to a subdirectory name (in 203*0Sstevel@tonic-gate# $working_dir). $working_dir is NOT prepended. 204*0Sstevel@tonic-gate# 205*0Sstevel@tonic-gate# Maps, e.g., /home/auser/bin/netscape.sparc 206*0Sstevel@tonic-gate# ===> objects/:=home=auser=bin=netscape.sparc 207*0Sstevel@tonic-gate# 208*0Sstevel@tonic-gatesub object_to_dir_name 209*0Sstevel@tonic-gate{ 210*0Sstevel@tonic-gate my ($filename) = @_; 211*0Sstevel@tonic-gate 212*0Sstevel@tonic-gate my $dirname = $filename; 213*0Sstevel@tonic-gate 214*0Sstevel@tonic-gate # protect any percents there: 215*0Sstevel@tonic-gate $dirname =~ s,%,%%,g; 216*0Sstevel@tonic-gate 217*0Sstevel@tonic-gate # protect any equals there: 218*0Sstevel@tonic-gate $dirname =~ s,=,%=,g; 219*0Sstevel@tonic-gate 220*0Sstevel@tonic-gate # now change slashes to equals: 221*0Sstevel@tonic-gate $dirname =~ s,/,=,g; 222*0Sstevel@tonic-gate 223*0Sstevel@tonic-gate # 224*0Sstevel@tonic-gate # Prepend "objects/" and ":" tag to avoid dirname starting 225*0Sstevel@tonic-gate # with "=" or "." 226*0Sstevel@tonic-gate # 227*0Sstevel@tonic-gate $dirname = $object_dir . ':' . $dirname; 228*0Sstevel@tonic-gate 229*0Sstevel@tonic-gate return $dirname; 230*0Sstevel@tonic-gate} 231*0Sstevel@tonic-gate 232*0Sstevel@tonic-gate# 233*0Sstevel@tonic-gate# Takes the application output data directory and returns the path to 234*0Sstevel@tonic-gate# the actual binary. 235*0Sstevel@tonic-gate# 236*0Sstevel@tonic-gatesub dir_name_to_path 237*0Sstevel@tonic-gate{ 238*0Sstevel@tonic-gate my ($dirname) = @_; 239*0Sstevel@tonic-gate my $path = ''; 240*0Sstevel@tonic-gate 241*0Sstevel@tonic-gate if (! -f "$dirname/info.path") { 242*0Sstevel@tonic-gate exiter(nofile("$dirname/info.path", $!)); 243*0Sstevel@tonic-gate } else { 244*0Sstevel@tonic-gate my $info_path_fh = do { local *FH; *FH }; 245*0Sstevel@tonic-gate open($info_path_fh, "<$dirname/info.path") || 246*0Sstevel@tonic-gate exiter(nofile("$dirname/info.path", $!)); 247*0Sstevel@tonic-gate chomp($path = <$info_path_fh>); 248*0Sstevel@tonic-gate close($info_path_fh); 249*0Sstevel@tonic-gate } 250*0Sstevel@tonic-gate 251*0Sstevel@tonic-gate return $path; 252*0Sstevel@tonic-gate} 253*0Sstevel@tonic-gate 254*0Sstevel@tonic-gate# 255*0Sstevel@tonic-gate# This subroutine repeatly returns the object dirnames in the 256*0Sstevel@tonic-gate# working_dir. The full path to the dirname is returned. "undef" is 257*0Sstevel@tonic-gate# returned when all have been cycled through. 258*0Sstevel@tonic-gate# 259*0Sstevel@tonic-gatesub next_dir_name 260*0Sstevel@tonic-gate{ 261*0Sstevel@tonic-gate # object directory: 262*0Sstevel@tonic-gate my $object_directory = $working_dir; 263*0Sstevel@tonic-gate $object_directory .= "/" . $object_dir if ($object_dir); 264*0Sstevel@tonic-gate 265*0Sstevel@tonic-gate # Check if we have the directory handle already open: 266*0Sstevel@tonic-gate if (! defined($next_dir_name_dh)) { 267*0Sstevel@tonic-gate # If not, then opendir it: 268*0Sstevel@tonic-gate $next_dir_name_dh = do { local *FH; *FH }; 269*0Sstevel@tonic-gate if (! opendir($next_dir_name_dh, $object_directory)) { 270*0Sstevel@tonic-gate exiter(nodir($object_directory, $!)); 271*0Sstevel@tonic-gate } 272*0Sstevel@tonic-gate } 273*0Sstevel@tonic-gate 274*0Sstevel@tonic-gate my $dirname; 275*0Sstevel@tonic-gate 276*0Sstevel@tonic-gate # 277*0Sstevel@tonic-gate # Loop over directory entries until one matches the magic tag 278*0Sstevel@tonic-gate # "object:" Return undef when done reading the directory. 279*0Sstevel@tonic-gate # 280*0Sstevel@tonic-gate while (1) { 281*0Sstevel@tonic-gate $dirname = readdir($next_dir_name_dh); 282*0Sstevel@tonic-gate 283*0Sstevel@tonic-gate if (! defined($dirname)) { 284*0Sstevel@tonic-gate # Done with dir. Clean up for next time: 285*0Sstevel@tonic-gate closedir($next_dir_name_dh); 286*0Sstevel@tonic-gate undef($next_dir_name_dh); 287*0Sstevel@tonic-gate return undef; 288*0Sstevel@tonic-gate } elsif ($dirname =~ m,^:,) { 289*0Sstevel@tonic-gate # Return the full path to object's directory: 290*0Sstevel@tonic-gate return "$object_directory/$dirname"; 291*0Sstevel@tonic-gate } 292*0Sstevel@tonic-gate } 293*0Sstevel@tonic-gate} 294*0Sstevel@tonic-gate 295*0Sstevel@tonic-gate# 296*0Sstevel@tonic-gate# When appcert started up, it stored the /usr/bin/file output in the 297*0Sstevel@tonic-gate# app's output directory (appcert: record_binary()). This subroutine 298*0Sstevel@tonic-gate# retrieves it. If it cannot find it, it runs the file command 299*0Sstevel@tonic-gate# instead. The result is stored in memory in %cmd_output_file_cache 300*0Sstevel@tonic-gate# 301*0Sstevel@tonic-gate# Returns the single line of "file" output including the "\n". It 302*0Sstevel@tonic-gate# returns the null string if it had trouble, usually only if filename 303*0Sstevel@tonic-gate# doesn't exist. 304*0Sstevel@tonic-gate# 305*0Sstevel@tonic-gatesub cmd_output_file 306*0Sstevel@tonic-gate{ 307*0Sstevel@tonic-gate my ($filename) = @_; 308*0Sstevel@tonic-gate 309*0Sstevel@tonic-gate # Check if we have it cached: 310*0Sstevel@tonic-gate if (exists($cmd_output_file_cache{$filename})) { 311*0Sstevel@tonic-gate return $cmd_output_file_cache{$filename}; 312*0Sstevel@tonic-gate } 313*0Sstevel@tonic-gate 314*0Sstevel@tonic-gate # Otherwise, try to look it up in the $working_dir: 315*0Sstevel@tonic-gate my $outfile = object_to_dir_name($filename); 316*0Sstevel@tonic-gate $outfile = "$working_dir/$outfile/info.file"; 317*0Sstevel@tonic-gate 318*0Sstevel@tonic-gate my $str; 319*0Sstevel@tonic-gate 320*0Sstevel@tonic-gate if (-f $outfile) { 321*0Sstevel@tonic-gate my $file_cmd_fh = do { local *FH; *FH }; 322*0Sstevel@tonic-gate if (open($file_cmd_fh, "<$outfile")) { 323*0Sstevel@tonic-gate $str = <$file_cmd_fh>; 324*0Sstevel@tonic-gate close($file_cmd_fh); 325*0Sstevel@tonic-gate } 326*0Sstevel@tonic-gate } 327*0Sstevel@tonic-gate 328*0Sstevel@tonic-gate # Otherwise run /usr/bin/file on it: 329*0Sstevel@tonic-gate if (! defined($str) && -f $filename && $filename !~ /'/) { 330*0Sstevel@tonic-gate c_locale(1); 331*0Sstevel@tonic-gate $str = `$cmd_file '$filename' 2>/dev/null`; 332*0Sstevel@tonic-gate c_locale(0); 333*0Sstevel@tonic-gate } 334*0Sstevel@tonic-gate 335*0Sstevel@tonic-gate $cmd_output_file_cache{$filename} = $str; 336*0Sstevel@tonic-gate 337*0Sstevel@tonic-gate return $str; 338*0Sstevel@tonic-gate} 339*0Sstevel@tonic-gate 340*0Sstevel@tonic-gate# 341*0Sstevel@tonic-gate# When appcert started up, it stored the /usr/ccs/bin/dump output in the 342*0Sstevel@tonic-gate# app's output directory (appcert: record_binary()). This subroutine 343*0Sstevel@tonic-gate# retrieves it. If it cannot find it, it runs the dump -Lv command 344*0Sstevel@tonic-gate# instead. The result is stored in memory in %cmd_output_dump_cache 345*0Sstevel@tonic-gate# 346*0Sstevel@tonic-gate# Returns the "dump -Lv" output. It returns the null string if it had 347*0Sstevel@tonic-gate# trouble, usually only if filename doesn't exist. 348*0Sstevel@tonic-gate# 349*0Sstevel@tonic-gatesub cmd_output_dump 350*0Sstevel@tonic-gate{ 351*0Sstevel@tonic-gate my ($filename) = @_; 352*0Sstevel@tonic-gate 353*0Sstevel@tonic-gate # Check if we have it cached: 354*0Sstevel@tonic-gate if (exists($cmd_output_dump_cache{$filename})) { 355*0Sstevel@tonic-gate return $cmd_output_dump_cache{$filename}; 356*0Sstevel@tonic-gate } 357*0Sstevel@tonic-gate 358*0Sstevel@tonic-gate # Otherwise, try to look it up in the $working_dir: 359*0Sstevel@tonic-gate my $outfile = object_to_dir_name($filename); 360*0Sstevel@tonic-gate $outfile = "$working_dir/$outfile/info.dump"; 361*0Sstevel@tonic-gate 362*0Sstevel@tonic-gate my $str; 363*0Sstevel@tonic-gate 364*0Sstevel@tonic-gate if (-f $outfile) { 365*0Sstevel@tonic-gate my $dump_cmd_fh = do { local *FH; *FH }; 366*0Sstevel@tonic-gate if (open($dump_cmd_fh, "<$outfile")) { 367*0Sstevel@tonic-gate while (<$dump_cmd_fh>) { 368*0Sstevel@tonic-gate $str .= $_; 369*0Sstevel@tonic-gate } 370*0Sstevel@tonic-gate close($dump_cmd_fh); 371*0Sstevel@tonic-gate } 372*0Sstevel@tonic-gate } 373*0Sstevel@tonic-gate 374*0Sstevel@tonic-gate # Otherwise run /usr/ccs/bin/dump -Lv on it: 375*0Sstevel@tonic-gate if (! defined($str) && -f $filename && $filename !~ /'/) { 376*0Sstevel@tonic-gate c_locale(1); 377*0Sstevel@tonic-gate $str = `$cmd_dump -Lv '$filename' 2>/dev/null`; 378*0Sstevel@tonic-gate c_locale(0); 379*0Sstevel@tonic-gate } 380*0Sstevel@tonic-gate 381*0Sstevel@tonic-gate $cmd_output_dump_cache{$filename} = $str; 382*0Sstevel@tonic-gate 383*0Sstevel@tonic-gate return $str; 384*0Sstevel@tonic-gate} 385*0Sstevel@tonic-gate 386*0Sstevel@tonic-gate# 387*0Sstevel@tonic-gate# When symprof runs it stores the /usr/bin/ldd output in the app's 388*0Sstevel@tonic-gate# output directory (symprof: dynamic_profile()). This subroutine 389*0Sstevel@tonic-gate# retrieves it. If it cannot find it, it runs the ldd command instead. 390*0Sstevel@tonic-gate# The result is stored in memory in %all_ldd_neededs_cache 391*0Sstevel@tonic-gate# 392*0Sstevel@tonic-gate# Returns a "neededs hash" as output. The keys being the things needed 393*0Sstevel@tonic-gate# (left side of " => ") and the values are the resolution (right side of 394*0Sstevel@tonic-gate# " => "). It returns the null hash if it had trouble, usually only if 395*0Sstevel@tonic-gate# filename doesn't even exist, or if the object is not dynamically 396*0Sstevel@tonic-gate# linked. 397*0Sstevel@tonic-gate# 398*0Sstevel@tonic-gatesub all_ldd_neededs 399*0Sstevel@tonic-gate{ 400*0Sstevel@tonic-gate my ($filename) = @_; 401*0Sstevel@tonic-gate 402*0Sstevel@tonic-gate my (%all_neededs); 403*0Sstevel@tonic-gate 404*0Sstevel@tonic-gate my $output; 405*0Sstevel@tonic-gate 406*0Sstevel@tonic-gate # Check if we have it cached: 407*0Sstevel@tonic-gate if (exists($all_ldd_neededs_cache{$filename})) { 408*0Sstevel@tonic-gate $output = $all_ldd_neededs_cache{$filename}; 409*0Sstevel@tonic-gate } 410*0Sstevel@tonic-gate 411*0Sstevel@tonic-gate if (! defined($output)) { 412*0Sstevel@tonic-gate # Otherwise, try to look it up in the $working_dir: 413*0Sstevel@tonic-gate my $outfile = object_to_dir_name($filename); 414*0Sstevel@tonic-gate $outfile = "$working_dir/$outfile/profile.dynamic.ldd"; 415*0Sstevel@tonic-gate 416*0Sstevel@tonic-gate if (-f $outfile) { 417*0Sstevel@tonic-gate my $all_neededs_fh = do { local *FH; *FH }; 418*0Sstevel@tonic-gate if (open($all_neededs_fh, "<$outfile")) { 419*0Sstevel@tonic-gate while (<$all_neededs_fh>) { 420*0Sstevel@tonic-gate next if (/^\s*#/); 421*0Sstevel@tonic-gate $output .= $_; 422*0Sstevel@tonic-gate } 423*0Sstevel@tonic-gate } 424*0Sstevel@tonic-gate close($all_neededs_fh); 425*0Sstevel@tonic-gate } 426*0Sstevel@tonic-gate } 427*0Sstevel@tonic-gate 428*0Sstevel@tonic-gate my ($str, $line, $l1, $l2); 429*0Sstevel@tonic-gate if (! defined($output) && -f $filename && $filename !~ /'/) { 430*0Sstevel@tonic-gate # Otherwise run /usr/bin/ldd on it: 431*0Sstevel@tonic-gate c_locale(1); 432*0Sstevel@tonic-gate $str = `$cmd_ldd '$filename' 2>/dev/null`; 433*0Sstevel@tonic-gate c_locale(0); 434*0Sstevel@tonic-gate foreach $line (split(/\n/, $str)) { 435*0Sstevel@tonic-gate $line = trim($line); 436*0Sstevel@tonic-gate $output .= "$line\n"; 437*0Sstevel@tonic-gate } 438*0Sstevel@tonic-gate } 439*0Sstevel@tonic-gate 440*0Sstevel@tonic-gate if (! defined($output)) { 441*0Sstevel@tonic-gate # 442*0Sstevel@tonic-gate # Set the output to the null string so following loop 443*0Sstevel@tonic-gate # will do nothing and thus the empty hash will be 444*0Sstevel@tonic-gate # returned. 445*0Sstevel@tonic-gate # 446*0Sstevel@tonic-gate $output = ''; 447*0Sstevel@tonic-gate } 448*0Sstevel@tonic-gate 449*0Sstevel@tonic-gate $all_ldd_neededs_cache{$filename} = $output; 450*0Sstevel@tonic-gate 451*0Sstevel@tonic-gate foreach $line (split(/\n/, $output)) { 452*0Sstevel@tonic-gate ($l1, $l2) = split(/\s*=>\s*/, $line); 453*0Sstevel@tonic-gate $l1 = trim($l1); 454*0Sstevel@tonic-gate $l2 = trim($l2); 455*0Sstevel@tonic-gate $all_neededs{$l1} = $l2; 456*0Sstevel@tonic-gate if ($l2 !~ /file not found/) { 457*0Sstevel@tonic-gate $all_neededs{$l2} = $l2; 458*0Sstevel@tonic-gate } 459*0Sstevel@tonic-gate } 460*0Sstevel@tonic-gate 461*0Sstevel@tonic-gate return %all_neededs; 462*0Sstevel@tonic-gate} 463*0Sstevel@tonic-gate 464*0Sstevel@tonic-gate# 465*0Sstevel@tonic-gate# Create a string with all of the needed objects (direct and indirect). 466*0Sstevel@tonic-gate# This is intended for object name matching. See the 'needed' MATCH 467*0Sstevel@tonic-gate# entries in etc.warn. 468*0Sstevel@tonic-gate# 469*0Sstevel@tonic-gatesub all_ldd_neededs_string 470*0Sstevel@tonic-gate{ 471*0Sstevel@tonic-gate my ($filename) = @_; 472*0Sstevel@tonic-gate my (%hash, $key); 473*0Sstevel@tonic-gate my $str = ''; 474*0Sstevel@tonic-gate %hash = all_ldd_neededs($filename); 475*0Sstevel@tonic-gate foreach $key (keys(%hash)) { 476*0Sstevel@tonic-gate $str .= "$key $hash{$key}\n"; 477*0Sstevel@tonic-gate } 478*0Sstevel@tonic-gate return $str; 479*0Sstevel@tonic-gate} 480*0Sstevel@tonic-gate 481*0Sstevel@tonic-gate# 482*0Sstevel@tonic-gate# Create a list with all of the directly bound symbols. This is 483*0Sstevel@tonic-gate# intended for symbol call matching. See the 'syms' MATCH entries in 484*0Sstevel@tonic-gate# etc.warn. 485*0Sstevel@tonic-gate# 486*0Sstevel@tonic-gatesub direct_syms 487*0Sstevel@tonic-gate{ 488*0Sstevel@tonic-gate my ($filename) = @_; 489*0Sstevel@tonic-gate # 490*0Sstevel@tonic-gate # We stored the dynamic profile output in the app's output 491*0Sstevel@tonic-gate # directory. This subroutine retrieves it, identifies the 492*0Sstevel@tonic-gate # direct bindings symbol names and places them in a newline 493*0Sstevel@tonic-gate # separated string returned to caller. 494*0Sstevel@tonic-gate # 495*0Sstevel@tonic-gate my $direct_syms = ''; 496*0Sstevel@tonic-gate 497*0Sstevel@tonic-gate my $outfile = object_to_dir_name($filename); 498*0Sstevel@tonic-gate $outfile = "$working_dir/$outfile/profile.dynamic"; 499*0Sstevel@tonic-gate 500*0Sstevel@tonic-gate my $prof_fh = do { local *FH; *FH }; 501*0Sstevel@tonic-gate if (! open($prof_fh, "<$outfile")) { 502*0Sstevel@tonic-gate exiter(nofile($outfile, $!)); 503*0Sstevel@tonic-gate } 504*0Sstevel@tonic-gate my ($app, $caller, $lib, $sym); 505*0Sstevel@tonic-gate while (<$prof_fh>) { 506*0Sstevel@tonic-gate next if (/^\s*#/); 507*0Sstevel@tonic-gate next if (/^\s*$/); 508*0Sstevel@tonic-gate chop; 509*0Sstevel@tonic-gate ($app, $caller, $lib, $sym) = split(/\|/, $_, 4); 510*0Sstevel@tonic-gate next unless ($caller eq '*DIRECT*'); 511*0Sstevel@tonic-gate $direct_syms .= "$sym\n"; 512*0Sstevel@tonic-gate } 513*0Sstevel@tonic-gate close($prof_fh); 514*0Sstevel@tonic-gate 515*0Sstevel@tonic-gate return $direct_syms; 516*0Sstevel@tonic-gate} 517*0Sstevel@tonic-gate 518*0Sstevel@tonic-gate# 519*0Sstevel@tonic-gate# Block to keep export_list private 520*0Sstevel@tonic-gate# 521*0Sstevel@tonic-gate{ 522*0Sstevel@tonic-gate my %export_list = ( 523*0Sstevel@tonic-gate 'AC_LIB_DIR', 'appcert_lib_dir', 524*0Sstevel@tonic-gate 'AC_WORKING_DIR', 'working_dir', 525*0Sstevel@tonic-gate 'AC_TMP_DIR', 'tmp_dir', 526*0Sstevel@tonic-gate 'AC_BINARY_COUNT', 'binary_count', 527*0Sstevel@tonic-gate 'AC_BLOCK_MIN', 'block_min', 528*0Sstevel@tonic-gate 'AC_BLOCK_MAX', 'block_max', 529*0Sstevel@tonic-gate 'AC_BATCH_REPORT', 'batch_report', 530*0Sstevel@tonic-gate ); 531*0Sstevel@tonic-gate 532*0Sstevel@tonic-gate 533*0Sstevel@tonic-gate # 534*0Sstevel@tonic-gate # Subroutine to read in possibly exported variables 535*0Sstevel@tonic-gate # 536*0Sstevel@tonic-gate sub import_vars_from_environment 537*0Sstevel@tonic-gate { 538*0Sstevel@tonic-gate no strict qw(refs); 539*0Sstevel@tonic-gate 540*0Sstevel@tonic-gate while (my ($evar, $pvar) = each(%export_list)) { 541*0Sstevel@tonic-gate $pvar = $export_list{$evar}; 542*0Sstevel@tonic-gate if (exists($ENV{$evar})) { 543*0Sstevel@tonic-gate $$pvar = $ENV{$evar}; 544*0Sstevel@tonic-gate } else { 545*0Sstevel@tonic-gate $$pvar = ''; 546*0Sstevel@tonic-gate } 547*0Sstevel@tonic-gate } 548*0Sstevel@tonic-gate } 549*0Sstevel@tonic-gate 550*0Sstevel@tonic-gate # 551*0Sstevel@tonic-gate # Exports the variables in %export_list to the environment. 552*0Sstevel@tonic-gate # 553*0Sstevel@tonic-gate sub export_vars_to_environment 554*0Sstevel@tonic-gate { 555*0Sstevel@tonic-gate my $pval; 556*0Sstevel@tonic-gate no strict qw(refs); 557*0Sstevel@tonic-gate 558*0Sstevel@tonic-gate while (my ($evar, $pvar) = each(%export_list)) { 559*0Sstevel@tonic-gate $pvar = $export_list{$evar}; 560*0Sstevel@tonic-gate $pval = $$pvar; 561*0Sstevel@tonic-gate if (defined($pval)) { 562*0Sstevel@tonic-gate $ENV{$evar} = $pval; 563*0Sstevel@tonic-gate } 564*0Sstevel@tonic-gate } 565*0Sstevel@tonic-gate } 566*0Sstevel@tonic-gate} 567*0Sstevel@tonic-gate 568*0Sstevel@tonic-gate# 569*0Sstevel@tonic-gate# Routine for turning on or off LC_ALL environment variable 'C'. When 570*0Sstevel@tonic-gate# we want command output that we will parse we set LC_ALL=C. On the 571*0Sstevel@tonic-gate# other hand, when we want to pass command output to the user we retain 572*0Sstevel@tonic-gate# their locale (if any). 573*0Sstevel@tonic-gate# 574*0Sstevel@tonic-gatesub c_locale 575*0Sstevel@tonic-gate{ 576*0Sstevel@tonic-gate my ($action) = @_; 577*0Sstevel@tonic-gate 578*0Sstevel@tonic-gate # 579*0Sstevel@tonic-gate # example usage: 580*0Sstevel@tonic-gate # c_locale(1); 581*0Sstevel@tonic-gate # $output = `some_cmd some_args 2>/dev/null`; 582*0Sstevel@tonic-gate # c_locale(0); 583*0Sstevel@tonic-gate # 584*0Sstevel@tonic-gate 585*0Sstevel@tonic-gate if ($action) { 586*0Sstevel@tonic-gate if (defined($ENV{'LC_ALL'})) { 587*0Sstevel@tonic-gate $LC_ALL = $ENV{'LC_ALL'}; 588*0Sstevel@tonic-gate } else { 589*0Sstevel@tonic-gate $LC_ALL = '__UNSET__'; 590*0Sstevel@tonic-gate } 591*0Sstevel@tonic-gate $ENV{'LC_ALL'} = 'C'; 592*0Sstevel@tonic-gate } else { 593*0Sstevel@tonic-gate if ($LC_ALL eq '__UNSET__') { 594*0Sstevel@tonic-gate delete $ENV{'LC_ALL'}; 595*0Sstevel@tonic-gate } else { 596*0Sstevel@tonic-gate $ENV{'LC_ALL'} = $LC_ALL; 597*0Sstevel@tonic-gate } 598*0Sstevel@tonic-gate } 599*0Sstevel@tonic-gate} 600*0Sstevel@tonic-gate 601*0Sstevel@tonic-gate# 602*0Sstevel@tonic-gate# Set or get the overall appcert result/return code. 603*0Sstevel@tonic-gate# 604*0Sstevel@tonic-gatesub overall_result_code 605*0Sstevel@tonic-gate{ 606*0Sstevel@tonic-gate my ($val) = @_; 607*0Sstevel@tonic-gate # 608*0Sstevel@tonic-gate # The code has significance (see below) and is the numerical 609*0Sstevel@tonic-gate # exit() code for the appcert script. 610*0Sstevel@tonic-gate # 611*0Sstevel@tonic-gate # Code can be number followed by 1-line description. 612*0Sstevel@tonic-gate # 613*0Sstevel@tonic-gate # 0 appcert completed OK and ZERO binaries had problems detected 614*0Sstevel@tonic-gate # and ZERO binaries had "warnings". 615*0Sstevel@tonic-gate # 1 appcert failed somehow 616*0Sstevel@tonic-gate # 2 appcert completed OK and SOME binaries had problems detected. 617*0Sstevel@tonic-gate # 3 appcert completed OK and ZERO binaries had problems detected. 618*0Sstevel@tonic-gate # and SOME binaries had "warnings". 619*0Sstevel@tonic-gate # 620*0Sstevel@tonic-gate # When called with a no arguments, only the number is returned. 621*0Sstevel@tonic-gate # When called with a non-null argument it is written to the rc file. 622*0Sstevel@tonic-gate # 623*0Sstevel@tonic-gate 624*0Sstevel@tonic-gate my ($return_code_file, $line); 625*0Sstevel@tonic-gate 626*0Sstevel@tonic-gate $return_code_file = "$working_dir/ResultCode"; 627*0Sstevel@tonic-gate 628*0Sstevel@tonic-gate my $rc_file_fh = do { local *FH; *FH }; 629*0Sstevel@tonic-gate if (! defined($val)) { 630*0Sstevel@tonic-gate if (! -f $return_code_file) { 631*0Sstevel@tonic-gate emsg("%s", nofile($return_code_file)); 632*0Sstevel@tonic-gate return 1; 633*0Sstevel@tonic-gate } 634*0Sstevel@tonic-gate open($rc_file_fh, "<$return_code_file") || 635*0Sstevel@tonic-gate exiter(nofile($return_code_file, $!)); 636*0Sstevel@tonic-gate chomp($line = <$rc_file_fh>); 637*0Sstevel@tonic-gate close($rc_file_fh); 638*0Sstevel@tonic-gate if ($line =~ /^(\d+)/) { 639*0Sstevel@tonic-gate return $1; 640*0Sstevel@tonic-gate } else { 641*0Sstevel@tonic-gate return $line; 642*0Sstevel@tonic-gate } 643*0Sstevel@tonic-gate } else { 644*0Sstevel@tonic-gate $val = trim($val); 645*0Sstevel@tonic-gate if ($val !~ /^\d+/) { 646*0Sstevel@tonic-gate $val = "1 $val"; 647*0Sstevel@tonic-gate } 648*0Sstevel@tonic-gate open($rc_file_fh, ">$return_code_file") || 649*0Sstevel@tonic-gate exiter(nofile($return_code_file, $!)); 650*0Sstevel@tonic-gate print $rc_file_fh $val, "\n"; 651*0Sstevel@tonic-gate close($rc_file_fh); 652*0Sstevel@tonic-gate return; 653*0Sstevel@tonic-gate } 654*0Sstevel@tonic-gate} 655*0Sstevel@tonic-gate 656*0Sstevel@tonic-gate# 657*0Sstevel@tonic-gate# Sorter for strings like: "something 14", sorts on count (number) 658*0Sstevel@tonic-gate# first, then by string. 659*0Sstevel@tonic-gate# 660*0Sstevel@tonic-gatesub sort_on_count 661*0Sstevel@tonic-gate{ 662*0Sstevel@tonic-gate my $soc_cmp = sub { 663*0Sstevel@tonic-gate my($n1, $n2); 664*0Sstevel@tonic-gate if ($a =~ /(\d+)\s*$/) { 665*0Sstevel@tonic-gate $n1 = $1; 666*0Sstevel@tonic-gate } else { 667*0Sstevel@tonic-gate $n1 = 0; 668*0Sstevel@tonic-gate } 669*0Sstevel@tonic-gate if ($b =~ /(\d+)\s*$/) { 670*0Sstevel@tonic-gate $n2 = $1; 671*0Sstevel@tonic-gate } else { 672*0Sstevel@tonic-gate $n2 = 0; 673*0Sstevel@tonic-gate } 674*0Sstevel@tonic-gate 675*0Sstevel@tonic-gate if ($n1 == $n2) { 676*0Sstevel@tonic-gate # if the numbers are "tied", then compare the 677*0Sstevel@tonic-gate # string portion. 678*0Sstevel@tonic-gate $a cmp $b; 679*0Sstevel@tonic-gate } else { 680*0Sstevel@tonic-gate # otherwise compare numerically: 681*0Sstevel@tonic-gate $n2 <=> $n1; 682*0Sstevel@tonic-gate } 683*0Sstevel@tonic-gate }; 684*0Sstevel@tonic-gate return sort $soc_cmp @_; 685*0Sstevel@tonic-gate} 686*0Sstevel@tonic-gate 687*0Sstevel@tonic-gate# 688*0Sstevel@tonic-gate# Trims leading and trailing whitespace from a string. 689*0Sstevel@tonic-gate# 690*0Sstevel@tonic-gatesub trim 691*0Sstevel@tonic-gate{ 692*0Sstevel@tonic-gate my ($x) = @_; 693*0Sstevel@tonic-gate if (! defined($x)) { 694*0Sstevel@tonic-gate return ''; 695*0Sstevel@tonic-gate } 696*0Sstevel@tonic-gate $x =~ s/^\s*//; 697*0Sstevel@tonic-gate $x =~ s/\s*$//; 698*0Sstevel@tonic-gate return $x; 699*0Sstevel@tonic-gate} 700*0Sstevel@tonic-gate 701*0Sstevel@tonic-gate# 702*0Sstevel@tonic-gate# Prints a line to filehandle or STDOUT. 703*0Sstevel@tonic-gate# 704*0Sstevel@tonic-gatesub print_line 705*0Sstevel@tonic-gate{ 706*0Sstevel@tonic-gate my ($fh) = @_; 707*0Sstevel@tonic-gate if (defined($fh)) { 708*0Sstevel@tonic-gate print $fh '-' x 72, "\n"; 709*0Sstevel@tonic-gate } else { 710*0Sstevel@tonic-gate print STDOUT '-' x 72, "\n"; 711*0Sstevel@tonic-gate } 712*0Sstevel@tonic-gate} 713*0Sstevel@tonic-gate 714*0Sstevel@tonic-gate# 715*0Sstevel@tonic-gate# Returns formatted output of list items that fit in 80 columns, e.g. 716*0Sstevel@tonic-gate# Gelf_got_title 1 Gelf_reloc_entry 1 717*0Sstevel@tonic-gate# Gelf_ver_def_print 1 Gelf_syminfo_entry_title 1 718*0Sstevel@tonic-gate# Gelf_sym_table_title 1 Gelf_elf_header 1 719*0Sstevel@tonic-gate# 720*0Sstevel@tonic-gatesub list_format 721*0Sstevel@tonic-gate{ 722*0Sstevel@tonic-gate my ($indent, @list) = @_; 723*0Sstevel@tonic-gate 724*0Sstevel@tonic-gate # $indent is a string which shifts everything over to the right. 725*0Sstevel@tonic-gate 726*0Sstevel@tonic-gate my $width = 0; 727*0Sstevel@tonic-gate my ($item, $len, $space); 728*0Sstevel@tonic-gate 729*0Sstevel@tonic-gate foreach $item (@list) { # find the widest list item. 730*0Sstevel@tonic-gate $len = length($item); 731*0Sstevel@tonic-gate $width = $len if ($len > $width); 732*0Sstevel@tonic-gate } 733*0Sstevel@tonic-gate $width += 2; # pad 2 spaces for each column. 734*0Sstevel@tonic-gate 735*0Sstevel@tonic-gate if ($width > (80 - length($indent))) { 736*0Sstevel@tonic-gate $width = 80 - length($indent); 737*0Sstevel@tonic-gate } 738*0Sstevel@tonic-gate 739*0Sstevel@tonic-gate # compute number of columns: 740*0Sstevel@tonic-gate my $columns = int((80 - length($indent))/$width); 741*0Sstevel@tonic-gate 742*0Sstevel@tonic-gate # initialize: 743*0Sstevel@tonic-gate my $current_column = 0; 744*0Sstevel@tonic-gate my $text = $indent; 745*0Sstevel@tonic-gate 746*0Sstevel@tonic-gate # put the items into lined up columns: 747*0Sstevel@tonic-gate foreach $item (@list) { 748*0Sstevel@tonic-gate if ($current_column >= $columns) { 749*0Sstevel@tonic-gate $text .= "\n"; 750*0Sstevel@tonic-gate $current_column = 0; 751*0Sstevel@tonic-gate $text .= $indent; 752*0Sstevel@tonic-gate } 753*0Sstevel@tonic-gate $space = $width - length($item); 754*0Sstevel@tonic-gate $text .= $item . ' ' x $space if ($space > 0); 755*0Sstevel@tonic-gate $current_column++; 756*0Sstevel@tonic-gate } 757*0Sstevel@tonic-gate $text .= "\n" if ($current_column); 758*0Sstevel@tonic-gate 759*0Sstevel@tonic-gate return $text; 760*0Sstevel@tonic-gate} 761*0Sstevel@tonic-gate 762*0Sstevel@tonic-gate# 763*0Sstevel@tonic-gate# Wrapper for STDERR messages. 764*0Sstevel@tonic-gate# 765*0Sstevel@tonic-gatesub emsg 766*0Sstevel@tonic-gate{ 767*0Sstevel@tonic-gate printf STDERR @_; 768*0Sstevel@tonic-gate} 769*0Sstevel@tonic-gate 770*0Sstevel@tonic-gate# 771*0Sstevel@tonic-gate# Wrapper for STDOUT messages. 772*0Sstevel@tonic-gate# 773*0Sstevel@tonic-gatesub pmsg 774*0Sstevel@tonic-gate{ 775*0Sstevel@tonic-gate printf STDOUT @_; 776*0Sstevel@tonic-gate} 777*0Sstevel@tonic-gate 778*0Sstevel@tonic-gate# 779*0Sstevel@tonic-gate# Error message for a failed file open. 780*0Sstevel@tonic-gate# 781*0Sstevel@tonic-gatesub nofile 782*0Sstevel@tonic-gate{ 783*0Sstevel@tonic-gate my $msg = "$command_name: "; 784*0Sstevel@tonic-gate $msg .= gettext("cannot open file: %s\n"); 785*0Sstevel@tonic-gate $msg = sprintf($msg, join(' ', @_)); 786*0Sstevel@tonic-gate 787*0Sstevel@tonic-gate return $msg; 788*0Sstevel@tonic-gate} 789*0Sstevel@tonic-gate 790*0Sstevel@tonic-gate# 791*0Sstevel@tonic-gate# Error message for an invalid file path. 792*0Sstevel@tonic-gate# 793*0Sstevel@tonic-gatesub nopathexist 794*0Sstevel@tonic-gate{ 795*0Sstevel@tonic-gate my $msg = "$command_name: "; 796*0Sstevel@tonic-gate $msg .= gettext("path does not exist: %s\n"); 797*0Sstevel@tonic-gate $msg = sprintf($msg, join(' ', @_)); 798*0Sstevel@tonic-gate 799*0Sstevel@tonic-gate return $msg; 800*0Sstevel@tonic-gate} 801*0Sstevel@tonic-gate 802*0Sstevel@tonic-gate# 803*0Sstevel@tonic-gate# Error message for a failed running of a command. 804*0Sstevel@tonic-gate# 805*0Sstevel@tonic-gatesub norunprog 806*0Sstevel@tonic-gate{ 807*0Sstevel@tonic-gate my $msg = "$command_name: "; 808*0Sstevel@tonic-gate $msg .= gettext("cannot run program: %s\n"); 809*0Sstevel@tonic-gate $msg = sprintf($msg, join(' ', @_)); 810*0Sstevel@tonic-gate 811*0Sstevel@tonic-gate return $msg; 812*0Sstevel@tonic-gate} 813*0Sstevel@tonic-gate 814*0Sstevel@tonic-gate# 815*0Sstevel@tonic-gate# Error message for a failed directory creation. 816*0Sstevel@tonic-gate# 817*0Sstevel@tonic-gatesub nocreatedir 818*0Sstevel@tonic-gate{ 819*0Sstevel@tonic-gate my $msg = "$command_name: "; 820*0Sstevel@tonic-gate $msg .= gettext("cannot create directory: %s\n"); 821*0Sstevel@tonic-gate $msg = sprintf($msg, join(' ', @_)); 822*0Sstevel@tonic-gate 823*0Sstevel@tonic-gate return $msg; 824*0Sstevel@tonic-gate} 825*0Sstevel@tonic-gate 826*0Sstevel@tonic-gate# 827*0Sstevel@tonic-gate# Error message for a failed directory opendir. 828*0Sstevel@tonic-gate# 829*0Sstevel@tonic-gatesub nodir 830*0Sstevel@tonic-gate{ 831*0Sstevel@tonic-gate my $msg = "$command_name: "; 832*0Sstevel@tonic-gate $msg .= gettext("cannot open directory: %s\n"); 833*0Sstevel@tonic-gate $msg = sprintf($msg, join(' ', @_)); 834*0Sstevel@tonic-gate 835*0Sstevel@tonic-gate return $msg; 836*0Sstevel@tonic-gate} 837*0Sstevel@tonic-gate 838*0Sstevel@tonic-gate# 839*0Sstevel@tonic-gate# exiter routine wrapper is used primarily to abort. Calls 840*0Sstevel@tonic-gate# clean_up_exit() routine if that routine is defined. Prints $msg to 841*0Sstevel@tonic-gate# STDERR and exits with exit code $status $status is 1 (aborted command) 842*0Sstevel@tonic-gate# by default. 843*0Sstevel@tonic-gate# 844*0Sstevel@tonic-gatesub exiter 845*0Sstevel@tonic-gate{ 846*0Sstevel@tonic-gate my ($msg, $status) = @_; 847*0Sstevel@tonic-gate 848*0Sstevel@tonic-gate if (defined($msg) && ! defined($status) && $msg =~ /^\d+$/) { 849*0Sstevel@tonic-gate $status = $msg; 850*0Sstevel@tonic-gate undef($msg); 851*0Sstevel@tonic-gate } 852*0Sstevel@tonic-gate if (! defined($status)) { 853*0Sstevel@tonic-gate $status = 1; 854*0Sstevel@tonic-gate } 855*0Sstevel@tonic-gate 856*0Sstevel@tonic-gate if (defined($msg)) { 857*0Sstevel@tonic-gate # 858*0Sstevel@tonic-gate # append a newline unless one is already there or string 859*0Sstevel@tonic-gate # is empty: 860*0Sstevel@tonic-gate # 861*0Sstevel@tonic-gate $msg .= "\n" unless ($msg eq '' || $msg =~ /\n$/); 862*0Sstevel@tonic-gate emsg($msg); 863*0Sstevel@tonic-gate } 864*0Sstevel@tonic-gate if (defined($clean_up_exit_routine)) { 865*0Sstevel@tonic-gate &$clean_up_exit_routine($status); 866*0Sstevel@tonic-gate } 867*0Sstevel@tonic-gate 868*0Sstevel@tonic-gate exit $status; 869*0Sstevel@tonic-gate} 870*0Sstevel@tonic-gate 871*0Sstevel@tonic-gatesub set_clean_up_exit_routine 872*0Sstevel@tonic-gate{ 873*0Sstevel@tonic-gate my($code_ref) = @_; 874*0Sstevel@tonic-gate $clean_up_exit_routine = $code_ref; 875*0Sstevel@tonic-gate} 876*0Sstevel@tonic-gate 877*0Sstevel@tonic-gate# 878*0Sstevel@tonic-gate# Generic routine for setting up signal handling. (usually just a clean 879*0Sstevel@tonic-gate# up and exit routine). 880*0Sstevel@tonic-gate# 881*0Sstevel@tonic-gate# Call with mode 'on' and the name of the handler subroutine. 882*0Sstevel@tonic-gate# Call with mode 'off' to set signal handling back to defaults 883*0Sstevel@tonic-gate# (e.g. a handler wants to call signals('off')). 884*0Sstevel@tonic-gate# Call it with 'ignore' to set them to ignore. 885*0Sstevel@tonic-gate# 886*0Sstevel@tonic-gatesub signals 887*0Sstevel@tonic-gate{ 888*0Sstevel@tonic-gate my ($mode, $handler) = @_; 889*0Sstevel@tonic-gate 890*0Sstevel@tonic-gate # List of general signals to handle: 891*0Sstevel@tonic-gate my (@sigs) = qw(INT QUIT); 892*0Sstevel@tonic-gate 893*0Sstevel@tonic-gate my $sig; 894*0Sstevel@tonic-gate 895*0Sstevel@tonic-gate # Loop through signals and set the %SIG array accordingly. 896*0Sstevel@tonic-gate 897*0Sstevel@tonic-gate if ($mode eq 'on') { 898*0Sstevel@tonic-gate foreach $sig (@sigs) { 899*0Sstevel@tonic-gate $SIG{$sig} = $handler; 900*0Sstevel@tonic-gate } 901*0Sstevel@tonic-gate } elsif ($mode eq 'off') { 902*0Sstevel@tonic-gate foreach $sig (@sigs) { 903*0Sstevel@tonic-gate $SIG{$sig} = 'DEFAULT'; 904*0Sstevel@tonic-gate } 905*0Sstevel@tonic-gate } elsif ($mode eq 'ignore') { 906*0Sstevel@tonic-gate foreach $sig (@sigs) { 907*0Sstevel@tonic-gate $SIG{$sig} = 'IGNORE'; 908*0Sstevel@tonic-gate } 909*0Sstevel@tonic-gate } 910*0Sstevel@tonic-gate} 911*0Sstevel@tonic-gate 912*0Sstevel@tonic-gate# 913*0Sstevel@tonic-gate# Creates a temporary directory with a unique name. Directory is 914*0Sstevel@tonic-gate# created and the directory name is return. On failure to create it, 915*0Sstevel@tonic-gate# null string is returned. 916*0Sstevel@tonic-gate# 917*0Sstevel@tonic-gatesub create_tmp_dir 918*0Sstevel@tonic-gate{ 919*0Sstevel@tonic-gate my ($basedir) = @_; 920*0Sstevel@tonic-gate # 921*0Sstevel@tonic-gate # If passed a prefix in $prefix, try to create a unique tmp dir 922*0Sstevel@tonic-gate # with that basedir. Otherwise, it will make a name in /tmp. 923*0Sstevel@tonic-gate # 924*0Sstevel@tonic-gate # If passed a directory that already exists, a subdir is created 925*0Sstevel@tonic-gate # with madeup basename "prefix.suffix" 926*0Sstevel@tonic-gate # 927*0Sstevel@tonic-gate 928*0Sstevel@tonic-gate my $cmd = $command_name; 929*0Sstevel@tonic-gate $cmd = 'tempdir' unless (defined($cmd) && $cmd ne ''); 930*0Sstevel@tonic-gate 931*0Sstevel@tonic-gate if (! defined($basedir) || ! -d $basedir) { 932*0Sstevel@tonic-gate $basedir = "/tmp/$cmd"; 933*0Sstevel@tonic-gate } else { 934*0Sstevel@tonic-gate $basedir = "$basedir/$cmd"; 935*0Sstevel@tonic-gate } 936*0Sstevel@tonic-gate 937*0Sstevel@tonic-gate my $suffix = $$; 938*0Sstevel@tonic-gate if ($tmp_dir_count) { 939*0Sstevel@tonic-gate $suffix .= ".$tmp_dir_count"; 940*0Sstevel@tonic-gate } 941*0Sstevel@tonic-gate my $dir = "$basedir.$suffix"; 942*0Sstevel@tonic-gate $tmp_dir_count++; 943*0Sstevel@tonic-gate if ($dir =~ m,^/tmp/,) { 944*0Sstevel@tonic-gate if (! mkpath($dir, 0, 0700) || ! -d $dir) { 945*0Sstevel@tonic-gate emsg("%s", nocreatedir($dir, $!)); 946*0Sstevel@tonic-gate return ''; 947*0Sstevel@tonic-gate } 948*0Sstevel@tonic-gate } else { 949*0Sstevel@tonic-gate if (! mkpath($dir) || ! -d $dir) { 950*0Sstevel@tonic-gate emsg("%s", nocreatedir($dir, $!)); 951*0Sstevel@tonic-gate return ''; 952*0Sstevel@tonic-gate } 953*0Sstevel@tonic-gate } 954*0Sstevel@tonic-gate return $dir; 955*0Sstevel@tonic-gate} 956*0Sstevel@tonic-gate 957*0Sstevel@tonic-gate# 958*0Sstevel@tonic-gate# Checks to see if a directory is empty. Returns 1 if the directory is. 959*0Sstevel@tonic-gate# returns 0 if it is not or if directory does not exist. 960*0Sstevel@tonic-gate# 961*0Sstevel@tonic-gatesub dir_is_empty 962*0Sstevel@tonic-gate{ 963*0Sstevel@tonic-gate my ($dir) = @_; 964*0Sstevel@tonic-gate 965*0Sstevel@tonic-gate return 0 if (! -d $dir); 966*0Sstevel@tonic-gate 967*0Sstevel@tonic-gate my $is_empty = 1; 968*0Sstevel@tonic-gate 969*0Sstevel@tonic-gate my $dir_is_empty_dh = do { local *FH; *FH }; 970*0Sstevel@tonic-gate if (opendir($dir_is_empty_dh, $dir)) { 971*0Sstevel@tonic-gate my $subdir; 972*0Sstevel@tonic-gate foreach $subdir (readdir($dir_is_empty_dh)) { 973*0Sstevel@tonic-gate if ($subdir ne '.' && $subdir ne '..') { 974*0Sstevel@tonic-gate $is_empty = 0; 975*0Sstevel@tonic-gate last; 976*0Sstevel@tonic-gate } 977*0Sstevel@tonic-gate } 978*0Sstevel@tonic-gate close($dir_is_empty_dh); 979*0Sstevel@tonic-gate } else { 980*0Sstevel@tonic-gate return 0; 981*0Sstevel@tonic-gate } 982*0Sstevel@tonic-gate 983*0Sstevel@tonic-gate return $is_empty; 984*0Sstevel@tonic-gate} 985*0Sstevel@tonic-gate 986*0Sstevel@tonic-gate# 987*0Sstevel@tonic-gate# Follows a symbolic link until it points to a non-symbolic link. If 988*0Sstevel@tonic-gate# $file is not a symlink but rather a file, returns $file. Returns null 989*0Sstevel@tonic-gate# if what is pointed to does not exist. 990*0Sstevel@tonic-gate# 991*0Sstevel@tonic-gatesub follow_symlink 992*0Sstevel@tonic-gate{ 993*0Sstevel@tonic-gate my ($file) = @_; 994*0Sstevel@tonic-gate 995*0Sstevel@tonic-gate if (! -e $file) { 996*0Sstevel@tonic-gate # We will never find anything: 997*0Sstevel@tonic-gate return ''; 998*0Sstevel@tonic-gate } 999*0Sstevel@tonic-gate 1000*0Sstevel@tonic-gate if (! -l $file) { 1001*0Sstevel@tonic-gate # Not a symlink: 1002*0Sstevel@tonic-gate return $file; 1003*0Sstevel@tonic-gate } 1004*0Sstevel@tonic-gate 1005*0Sstevel@tonic-gate my ($tmp1, $tmp2); 1006*0Sstevel@tonic-gate 1007*0Sstevel@tonic-gate $tmp1 = $file; 1008*0Sstevel@tonic-gate 1009*0Sstevel@tonic-gate while ($tmp2 = readlink($tmp1)) { 1010*0Sstevel@tonic-gate 1011*0Sstevel@tonic-gate if ($tmp2 !~ m,^/,) { 1012*0Sstevel@tonic-gate $tmp2 = dirname($tmp1) . "/" . $tmp2; 1013*0Sstevel@tonic-gate } 1014*0Sstevel@tonic-gate 1015*0Sstevel@tonic-gate $tmp1 = $tmp2; # 1016*0Sstevel@tonic-gate $tmp1 =~ s,/+,/,g; # get rid of //// 1017*0Sstevel@tonic-gate $tmp1 =~ s,^\./,,g; # remove leading ./ 1018*0Sstevel@tonic-gate $tmp1 =~ s,/\./,/,g; # remove /./ 1019*0Sstevel@tonic-gate $tmp1 =~ s,/+,/,g; # get rid of //// again 1020*0Sstevel@tonic-gate $tmp1 =~ s,/[^/]+/\.\./,/,g; # remove "abc/.." 1021*0Sstevel@tonic-gate # 1022*0Sstevel@tonic-gate 1023*0Sstevel@tonic-gate if (! -e $tmp1) { 1024*0Sstevel@tonic-gate $tmp1 = $tmp2; 1025*0Sstevel@tonic-gate } 1026*0Sstevel@tonic-gate if (! -e $tmp1) { 1027*0Sstevel@tonic-gate return ''; 1028*0Sstevel@tonic-gate } 1029*0Sstevel@tonic-gate } 1030*0Sstevel@tonic-gate 1031*0Sstevel@tonic-gate return $tmp1; 1032*0Sstevel@tonic-gate} 1033*0Sstevel@tonic-gate 1034*0Sstevel@tonic-gate# 1035*0Sstevel@tonic-gate# Examines if the file is statically linked. Can be called on any file, 1036*0Sstevel@tonic-gate# but it is preferable to run it on things known to be executables or 1037*0Sstevel@tonic-gate# libraries. 1038*0Sstevel@tonic-gate# 1039*0Sstevel@tonic-gate# Returns 0 if not statically linked. Otherwise, returns 1. 1040*0Sstevel@tonic-gate# 1041*0Sstevel@tonic-gatesub is_statically_linked 1042*0Sstevel@tonic-gate{ 1043*0Sstevel@tonic-gate my ($file) = @_; 1044*0Sstevel@tonic-gate 1045*0Sstevel@tonic-gate my $tmp; 1046*0Sstevel@tonic-gate my $file_cmd_output; 1047*0Sstevel@tonic-gate $file_cmd_output = cmd_output_file($file); 1048*0Sstevel@tonic-gate 1049*0Sstevel@tonic-gate if ($file_cmd_output eq '') { 1050*0Sstevel@tonic-gate return 1; 1051*0Sstevel@tonic-gate } 1052*0Sstevel@tonic-gate 1053*0Sstevel@tonic-gate if ($file_cmd_output =~ /[:\s](.*)$/) { 1054*0Sstevel@tonic-gate $tmp = $1; 1055*0Sstevel@tonic-gate if ($tmp =~ /ELF.*statically linked/) { 1056*0Sstevel@tonic-gate return 1; 1057*0Sstevel@tonic-gate } elsif ($tmp =~ /Sun demand paged/) { 1058*0Sstevel@tonic-gate if ($tmp !~ /dynamically linked/) { 1059*0Sstevel@tonic-gate return 1; 1060*0Sstevel@tonic-gate } 1061*0Sstevel@tonic-gate } 1062*0Sstevel@tonic-gate } 1063*0Sstevel@tonic-gate 1064*0Sstevel@tonic-gate return 0; 1065*0Sstevel@tonic-gate} 1066*0Sstevel@tonic-gate 1067*0Sstevel@tonic-gate# 1068*0Sstevel@tonic-gate# Examines first 4 bytes of file. Returns 1 if they are "\x7fELF". 1069*0Sstevel@tonic-gate# Otherwise, returns 0. 1070*0Sstevel@tonic-gate# 1071*0Sstevel@tonic-gatesub is_elf 1072*0Sstevel@tonic-gate{ 1073*0Sstevel@tonic-gate my ($file) = @_; 1074*0Sstevel@tonic-gate 1075*0Sstevel@tonic-gate my ($buf, $n); 1076*0Sstevel@tonic-gate my $cmp = "\x7fELF"; 1077*0Sstevel@tonic-gate if (! -r $file) { 1078*0Sstevel@tonic-gate return 0; 1079*0Sstevel@tonic-gate } 1080*0Sstevel@tonic-gate 1081*0Sstevel@tonic-gate my $is_elf_fh = do { local *FH; *FH }; 1082*0Sstevel@tonic-gate if (open($is_elf_fh, "<$file")) { 1083*0Sstevel@tonic-gate $n = read($is_elf_fh, $buf, 4); 1084*0Sstevel@tonic-gate close($is_elf_fh); 1085*0Sstevel@tonic-gate if ($n != 4) { 1086*0Sstevel@tonic-gate return 0; 1087*0Sstevel@tonic-gate } 1088*0Sstevel@tonic-gate if ($buf eq $cmp) { 1089*0Sstevel@tonic-gate return 1; 1090*0Sstevel@tonic-gate } 1091*0Sstevel@tonic-gate } 1092*0Sstevel@tonic-gate return 0; 1093*0Sstevel@tonic-gate} 1094*0Sstevel@tonic-gate 1095*0Sstevel@tonic-gate# 1096*0Sstevel@tonic-gate# Returns 1 if $file is a shared object (i.e. ELF shared library) 1097*0Sstevel@tonic-gate# Returns 0 if it is not. 1098*0Sstevel@tonic-gate# 1099*0Sstevel@tonic-gate# Routine uses the dump -Lv output to determine this. Failing that, it 1100*0Sstevel@tonic-gate# examines the /usr/bin/file output. 1101*0Sstevel@tonic-gate# 1102*0Sstevel@tonic-gatesub is_shared_object 1103*0Sstevel@tonic-gate{ 1104*0Sstevel@tonic-gate my ($file) = @_; 1105*0Sstevel@tonic-gate 1106*0Sstevel@tonic-gate return 0 unless (-f $file); 1107*0Sstevel@tonic-gate 1108*0Sstevel@tonic-gate my ($on, $line, $is_shared_object); 1109*0Sstevel@tonic-gate my ($n, $tag, $val); 1110*0Sstevel@tonic-gate 1111*0Sstevel@tonic-gate $on = 0; 1112*0Sstevel@tonic-gate $is_shared_object = 0; 1113*0Sstevel@tonic-gate 1114*0Sstevel@tonic-gate foreach $line (split(/\n/, cmd_output_dump($file))) { 1115*0Sstevel@tonic-gate 1116*0Sstevel@tonic-gate if ($line =~ /^\[INDEX\]/) { 1117*0Sstevel@tonic-gate $on = 1; 1118*0Sstevel@tonic-gate next; 1119*0Sstevel@tonic-gate } 1120*0Sstevel@tonic-gate next unless ($on); 1121*0Sstevel@tonic-gate ($n, $tag, $val) = split(/\s+/, trim($line)); 1122*0Sstevel@tonic-gate if ($tag eq "SONAME") { 1123*0Sstevel@tonic-gate $is_shared_object = 1; 1124*0Sstevel@tonic-gate last; 1125*0Sstevel@tonic-gate } 1126*0Sstevel@tonic-gate } 1127*0Sstevel@tonic-gate 1128*0Sstevel@tonic-gate if (! $is_shared_object) { 1129*0Sstevel@tonic-gate # If it is ELF, file output will say "dynamic lib": 1130*0Sstevel@tonic-gate $line = cmd_output_file($file); 1131*0Sstevel@tonic-gate if ($line =~ /ELF.* dynamic lib /) { 1132*0Sstevel@tonic-gate $is_shared_object = 1; 1133*0Sstevel@tonic-gate } 1134*0Sstevel@tonic-gate } 1135*0Sstevel@tonic-gate 1136*0Sstevel@tonic-gate return $is_shared_object; 1137*0Sstevel@tonic-gate} 1138*0Sstevel@tonic-gate 1139*0Sstevel@tonic-gate# 1140*0Sstevel@tonic-gate# Used for the a.out warning in etc.warn. Examines first 4 bytes of 1141*0Sstevel@tonic-gate# file, and returns 1 if SunOS 4.x a.out binary 0 otherwise. 1142*0Sstevel@tonic-gate# 1143*0Sstevel@tonic-gatesub is_aout 1144*0Sstevel@tonic-gate{ 1145*0Sstevel@tonic-gate my ($file) = @_; 1146*0Sstevel@tonic-gate 1147*0Sstevel@tonic-gate my ($buf, $n); 1148*0Sstevel@tonic-gate my $cmp1 = "\001\013"; 1149*0Sstevel@tonic-gate my $cmp2 = "\001\010"; 1150*0Sstevel@tonic-gate my $cmp3 = "\001\007"; 1151*0Sstevel@tonic-gate if (! -r $file) { 1152*0Sstevel@tonic-gate return 0; 1153*0Sstevel@tonic-gate } 1154*0Sstevel@tonic-gate 1155*0Sstevel@tonic-gate my $is_aout_fh = do { local *FH; *FH }; 1156*0Sstevel@tonic-gate if (open($is_aout_fh, "<$file")) { 1157*0Sstevel@tonic-gate $n = read($is_aout_fh, $buf, 4); 1158*0Sstevel@tonic-gate close($is_aout_fh); 1159*0Sstevel@tonic-gate if ($n != 4) { 1160*0Sstevel@tonic-gate return 0; 1161*0Sstevel@tonic-gate } 1162*0Sstevel@tonic-gate $buf = substr($buf, 2); 1163*0Sstevel@tonic-gate if ($buf eq $cmp1) { 1164*0Sstevel@tonic-gate return 1; 1165*0Sstevel@tonic-gate } 1166*0Sstevel@tonic-gate if ($buf eq $cmp2) { 1167*0Sstevel@tonic-gate return 1; 1168*0Sstevel@tonic-gate } 1169*0Sstevel@tonic-gate if ($buf eq $cmp3) { 1170*0Sstevel@tonic-gate return 1; 1171*0Sstevel@tonic-gate } 1172*0Sstevel@tonic-gate } 1173*0Sstevel@tonic-gate return 0; 1174*0Sstevel@tonic-gate} 1175*0Sstevel@tonic-gate 1176*0Sstevel@tonic-gate# 1177*0Sstevel@tonic-gate# is_suid 1178*0Sstevel@tonic-gate# Returns 1 if $file is a set user ID file. 1179*0Sstevel@tonic-gate# Returns 2 if $file otherwise is a set group ID (but not suid). 1180*0Sstevel@tonic-gate# Returns 0 if it is neither or file does not exist. 1181*0Sstevel@tonic-gate# 1182*0Sstevel@tonic-gatesub is_suid 1183*0Sstevel@tonic-gate{ 1184*0Sstevel@tonic-gate my ($file) = @_; 1185*0Sstevel@tonic-gate 1186*0Sstevel@tonic-gate return 0 unless (-f $file); 1187*0Sstevel@tonic-gate 1188*0Sstevel@tonic-gate my ($mask, $mode, $test); 1189*0Sstevel@tonic-gate my @is_suid_masks = (04000, 02010, 02030, 02050, 02070); 1190*0Sstevel@tonic-gate 1191*0Sstevel@tonic-gate $mode = (stat($file))[2]; 1192*0Sstevel@tonic-gate 1193*0Sstevel@tonic-gate foreach $mask (@is_suid_masks) { 1194*0Sstevel@tonic-gate $test = $mode & $mask; 1195*0Sstevel@tonic-gate if ($test == $mask) { 1196*0Sstevel@tonic-gate if ($mask == $is_suid_masks[0]) { 1197*0Sstevel@tonic-gate return 1; 1198*0Sstevel@tonic-gate } else { 1199*0Sstevel@tonic-gate return 2; 1200*0Sstevel@tonic-gate } 1201*0Sstevel@tonic-gate } 1202*0Sstevel@tonic-gate } 1203*0Sstevel@tonic-gate return 0; 1204*0Sstevel@tonic-gate} 1205*0Sstevel@tonic-gate 1206*0Sstevel@tonic-gate# 1207*0Sstevel@tonic-gate# Returns a list of (abi, [ELF|a.out], wordsize, endianness) 1208*0Sstevel@tonic-gate# 1209*0Sstevel@tonic-gatesub bin_type 1210*0Sstevel@tonic-gate{ 1211*0Sstevel@tonic-gate my ($filename) = @_; 1212*0Sstevel@tonic-gate 1213*0Sstevel@tonic-gate my ($abi, $e_machine, $type, $wordsize, $endian, $rest); 1214*0Sstevel@tonic-gate 1215*0Sstevel@tonic-gate $abi = 'unknown'; 1216*0Sstevel@tonic-gate $e_machine = 'unknown'; 1217*0Sstevel@tonic-gate $type = 'unknown'; 1218*0Sstevel@tonic-gate $wordsize = 'unknown'; 1219*0Sstevel@tonic-gate $endian = 'unknown'; 1220*0Sstevel@tonic-gate 1221*0Sstevel@tonic-gate # Try to look it up in the $working_dir: 1222*0Sstevel@tonic-gate my $outfile = object_to_dir_name($filename); 1223*0Sstevel@tonic-gate $outfile = "$working_dir/$outfile/info.arch"; 1224*0Sstevel@tonic-gate 1225*0Sstevel@tonic-gate if (-f $outfile) { 1226*0Sstevel@tonic-gate my $arch_info_fh = do { local *FH; *FH }; 1227*0Sstevel@tonic-gate if (open($arch_info_fh, "<$outfile")) { 1228*0Sstevel@tonic-gate while (<$arch_info_fh>) { 1229*0Sstevel@tonic-gate chomp; 1230*0Sstevel@tonic-gate if (/^ARCH:\s*(\S.*)$/) { 1231*0Sstevel@tonic-gate $abi = $1; 1232*0Sstevel@tonic-gate } elsif (/^TYPE:\s*(\S.*)$/) { 1233*0Sstevel@tonic-gate $type = $1; 1234*0Sstevel@tonic-gate } elsif (/^WORDSIZE:\s*(\S.*)$/) { 1235*0Sstevel@tonic-gate $wordsize = $1; 1236*0Sstevel@tonic-gate } elsif (/^BYTEORDER:\s*(\S.*)$/) { 1237*0Sstevel@tonic-gate $endian = $1; 1238*0Sstevel@tonic-gate } 1239*0Sstevel@tonic-gate } 1240*0Sstevel@tonic-gate close($arch_info_fh); 1241*0Sstevel@tonic-gate } 1242*0Sstevel@tonic-gate return ($abi, $type, $wordsize, $endian); 1243*0Sstevel@tonic-gate } 1244*0Sstevel@tonic-gate 1245*0Sstevel@tonic-gate # Otherwise, process /usr/bin/file output: 1246*0Sstevel@tonic-gate my $file_output; 1247*0Sstevel@tonic-gate $file_output = cmd_output_file($filename); 1248*0Sstevel@tonic-gate 1249*0Sstevel@tonic-gate if ($file_output =~ /Sun demand paged SPARC|pure SPARC/) { 1250*0Sstevel@tonic-gate $type = 'a.out'; 1251*0Sstevel@tonic-gate $abi = 'sparc'; 1252*0Sstevel@tonic-gate $e_machine = 'SPARC'; 1253*0Sstevel@tonic-gate $wordsize = '32'; 1254*0Sstevel@tonic-gate $endian = 'MSB'; 1255*0Sstevel@tonic-gate } elsif ($file_output =~ /ELF\s+/) { 1256*0Sstevel@tonic-gate $type = 'ELF'; 1257*0Sstevel@tonic-gate $rest = $'; 1258*0Sstevel@tonic-gate if ($rest =~ /^(\d+)-bit\s+/) { 1259*0Sstevel@tonic-gate $wordsize = $1; 1260*0Sstevel@tonic-gate $rest = $'; 1261*0Sstevel@tonic-gate } 1262*0Sstevel@tonic-gate if ($rest =~ /^(LSB|MSB)\s+/) { 1263*0Sstevel@tonic-gate $endian = $1; 1264*0Sstevel@tonic-gate $rest = $'; 1265*0Sstevel@tonic-gate } 1266*0Sstevel@tonic-gate if ($rest =~ /SPARC/) { 1267*0Sstevel@tonic-gate if ($rest =~ /\bSPARC\b/) { 1268*0Sstevel@tonic-gate $abi = 'sparc'; 1269*0Sstevel@tonic-gate $e_machine = 'SPARC'; 1270*0Sstevel@tonic-gate } elsif ($rest =~ /\bSPARC32PLUS\b/) { 1271*0Sstevel@tonic-gate $abi = 'sparc'; 1272*0Sstevel@tonic-gate $e_machine = 'SPARC32PLUS'; 1273*0Sstevel@tonic-gate } elsif ($rest =~ /\bSPARCV9\b/) { 1274*0Sstevel@tonic-gate $abi = 'sparcv9'; 1275*0Sstevel@tonic-gate $e_machine = 'SPARCV9'; 1276*0Sstevel@tonic-gate } 1277*0Sstevel@tonic-gate } else { 1278*0Sstevel@tonic-gate if ($rest =~ /\bAMD64\b/ || 1279*0Sstevel@tonic-gate $wordsize == 64 && $endian eq 'LSB') { 1280*0Sstevel@tonic-gate $abi = 'amd64'; 1281*0Sstevel@tonic-gate $e_machine = 'AMD64'; 1282*0Sstevel@tonic-gate } elsif ($rest =~ /\b80386\b/) { 1283*0Sstevel@tonic-gate $abi = 'i386'; 1284*0Sstevel@tonic-gate $e_machine = '80386'; 1285*0Sstevel@tonic-gate } 1286*0Sstevel@tonic-gate } 1287*0Sstevel@tonic-gate } 1288*0Sstevel@tonic-gate return ($abi, $type, $wordsize, $endian, $e_machine); 1289*0Sstevel@tonic-gate} 1290*0Sstevel@tonic-gate 1291*0Sstevel@tonic-gate# 1292*0Sstevel@tonic-gate# Compares two files to see if they are the same. First tries some 1293*0Sstevel@tonic-gate# string comparisons. Then, if $fast is not true, attempts an inode 1294*0Sstevel@tonic-gate# comparison. 1295*0Sstevel@tonic-gate# 1296*0Sstevel@tonic-gatesub files_equal 1297*0Sstevel@tonic-gate{ 1298*0Sstevel@tonic-gate my ($file1, $file2, $fast) = @_; 1299*0Sstevel@tonic-gate 1300*0Sstevel@tonic-gate my ($f1, $f2); 1301*0Sstevel@tonic-gate 1302*0Sstevel@tonic-gate # 1303*0Sstevel@tonic-gate # If they are the same string, we say they are equal without 1304*0Sstevel@tonic-gate # checking if they do exist. 1305*0Sstevel@tonic-gate # 1306*0Sstevel@tonic-gate 1307*0Sstevel@tonic-gate if ($file1 eq $file2) { 1308*0Sstevel@tonic-gate return 1; 1309*0Sstevel@tonic-gate } 1310*0Sstevel@tonic-gate 1311*0Sstevel@tonic-gate # Try trimming off any leading "./" 1312*0Sstevel@tonic-gate $f1 = $file1; 1313*0Sstevel@tonic-gate $f2 = $file2; 1314*0Sstevel@tonic-gate 1315*0Sstevel@tonic-gate $f1 =~ s,^\./+,,; 1316*0Sstevel@tonic-gate $f2 =~ s,^\./+,,; 1317*0Sstevel@tonic-gate 1318*0Sstevel@tonic-gate if ($f1 eq $f2) { 1319*0Sstevel@tonic-gate return 1; 1320*0Sstevel@tonic-gate } 1321*0Sstevel@tonic-gate 1322*0Sstevel@tonic-gate # That is all we do if doing a fast compare. 1323*0Sstevel@tonic-gate return 0 if ($fast); 1324*0Sstevel@tonic-gate 1325*0Sstevel@tonic-gate # Otherwise, resort to the file system: 1326*0Sstevel@tonic-gate 1327*0Sstevel@tonic-gate my ($inode1, $inode2); 1328*0Sstevel@tonic-gate $inode1 = file_inode($file1); 1329*0Sstevel@tonic-gate $inode2 = file_inode($file2); 1330*0Sstevel@tonic-gate 1331*0Sstevel@tonic-gate if (! defined($inode1) || ! defined($inode2) || 1332*0Sstevel@tonic-gate $inode1 < 0 || $inode2 < 0) { 1333*0Sstevel@tonic-gate return 0; 1334*0Sstevel@tonic-gate } elsif ($inode1 eq $inode2) { 1335*0Sstevel@tonic-gate return 1; 1336*0Sstevel@tonic-gate } 1337*0Sstevel@tonic-gate return 0; 1338*0Sstevel@tonic-gate} 1339*0Sstevel@tonic-gate 1340*0Sstevel@tonic-gate# 1341*0Sstevel@tonic-gate# Utility to return the inode of a file. Used to determine if two 1342*0Sstevel@tonic-gate# different paths or a path + symlink point to the same actual file. 1343*0Sstevel@tonic-gate# 1344*0Sstevel@tonic-gatesub file_inode 1345*0Sstevel@tonic-gate{ 1346*0Sstevel@tonic-gate my ($file) = @_; 1347*0Sstevel@tonic-gate 1348*0Sstevel@tonic-gate my $inode; 1349*0Sstevel@tonic-gate if (exists($file_inode_cache{$file})) { 1350*0Sstevel@tonic-gate return $file_inode_cache{$file}; 1351*0Sstevel@tonic-gate } 1352*0Sstevel@tonic-gate 1353*0Sstevel@tonic-gate if (! file_exists($file)) { 1354*0Sstevel@tonic-gate $file_inode_cache{$file} = -1; 1355*0Sstevel@tonic-gate return -1; 1356*0Sstevel@tonic-gate } 1357*0Sstevel@tonic-gate 1358*0Sstevel@tonic-gate $inode = (stat($file))[1]; 1359*0Sstevel@tonic-gate 1360*0Sstevel@tonic-gate if (! defined($inode) || $inode !~ /^\d+$/) { 1361*0Sstevel@tonic-gate $inode = -1; 1362*0Sstevel@tonic-gate } 1363*0Sstevel@tonic-gate 1364*0Sstevel@tonic-gate $file_inode_cache{$file} = $inode; 1365*0Sstevel@tonic-gate return $inode; 1366*0Sstevel@tonic-gate} 1367*0Sstevel@tonic-gate 1368*0Sstevel@tonic-gate# 1369*0Sstevel@tonic-gate# Existence test for files. Caches the results for speed. 1370*0Sstevel@tonic-gate# 1371*0Sstevel@tonic-gatesub file_exists 1372*0Sstevel@tonic-gate{ 1373*0Sstevel@tonic-gate my ($file) = @_; 1374*0Sstevel@tonic-gate 1375*0Sstevel@tonic-gate if (exists($file_exists_cache{$file})) { 1376*0Sstevel@tonic-gate return $file_exists_cache{$file}; 1377*0Sstevel@tonic-gate } 1378*0Sstevel@tonic-gate 1379*0Sstevel@tonic-gate my $x; 1380*0Sstevel@tonic-gate if (-e $file) { 1381*0Sstevel@tonic-gate $x = 1; 1382*0Sstevel@tonic-gate } else { 1383*0Sstevel@tonic-gate $x = 0; 1384*0Sstevel@tonic-gate } 1385*0Sstevel@tonic-gate $file_exists_cache{$file} = $x; 1386*0Sstevel@tonic-gate 1387*0Sstevel@tonic-gate return $x; 1388*0Sstevel@tonic-gate} 1389*0Sstevel@tonic-gate 1390*0Sstevel@tonic-gate# 1391*0Sstevel@tonic-gate# This routine deletes the caches we store information (e.g. cmd output) 1392*0Sstevel@tonic-gate# in to improve performance. It is called when the caches are suspected 1393*0Sstevel@tonic-gate# to be too large. 1394*0Sstevel@tonic-gate# 1395*0Sstevel@tonic-gatesub purge_caches 1396*0Sstevel@tonic-gate{ 1397*0Sstevel@tonic-gate undef %file_exists_cache; 1398*0Sstevel@tonic-gate undef %file_inode_cache; 1399*0Sstevel@tonic-gate undef %filter_lib_cache; 1400*0Sstevel@tonic-gate undef %cmd_output_file_cache; 1401*0Sstevel@tonic-gate undef %cmd_output_dump_cache; 1402*0Sstevel@tonic-gate undef %all_ldd_neededs_cache; 1403*0Sstevel@tonic-gate} 1404*0Sstevel@tonic-gate 1405*0Sstevel@tonic-gate# 1406*0Sstevel@tonic-gate# Given a filter library, this routine tries to determine if it is a 1407*0Sstevel@tonic-gate# STANDARD filter or an AUXILIARY filter. This is done by running dump 1408*0Sstevel@tonic-gate# -Lv on the filter library. Results are cached in the global 1409*0Sstevel@tonic-gate# filter_lib_cache to avoid calling dump many times on the same library 1410*0Sstevel@tonic-gate# (e.g. libc.so.1). 1411*0Sstevel@tonic-gate# 1412*0Sstevel@tonic-gatesub filter_lib_type 1413*0Sstevel@tonic-gate{ 1414*0Sstevel@tonic-gate my ($filter) = @_; 1415*0Sstevel@tonic-gate 1416*0Sstevel@tonic-gate my $type = 'unknown'; 1417*0Sstevel@tonic-gate 1418*0Sstevel@tonic-gate if (exists($filter_lib_cache{$filter})) { 1419*0Sstevel@tonic-gate return $filter_lib_cache{$filter}; 1420*0Sstevel@tonic-gate } 1421*0Sstevel@tonic-gate 1422*0Sstevel@tonic-gate if (! -f $filter) { 1423*0Sstevel@tonic-gate $filter_lib_cache{$filter} = 'unknown'; 1424*0Sstevel@tonic-gate return 'unknown'; 1425*0Sstevel@tonic-gate } 1426*0Sstevel@tonic-gate 1427*0Sstevel@tonic-gate my $dump_output; 1428*0Sstevel@tonic-gate $dump_output = cmd_output_dump($filter); 1429*0Sstevel@tonic-gate 1430*0Sstevel@tonic-gate if (! $dump_output) { 1431*0Sstevel@tonic-gate emsg(gettext("could not determine library filter type: %s\n"), 1432*0Sstevel@tonic-gate $filter); 1433*0Sstevel@tonic-gate $filter_lib_cache{$filter} = 'unknown'; 1434*0Sstevel@tonic-gate 1435*0Sstevel@tonic-gate } else { 1436*0Sstevel@tonic-gate my ($line, $dump, $idx, $tag, $val); 1437*0Sstevel@tonic-gate my ($saw_filter, $saw_aux); 1438*0Sstevel@tonic-gate $saw_filter = 0; 1439*0Sstevel@tonic-gate $saw_aux = 0; 1440*0Sstevel@tonic-gate foreach $line (split(/\n/, $dump_output)) { 1441*0Sstevel@tonic-gate next unless ($line =~ /^\[\d+\]/); 1442*0Sstevel@tonic-gate $dump = trim($line); 1443*0Sstevel@tonic-gate ($idx, $tag, $val) = split(/\s+/, $dump); 1444*0Sstevel@tonic-gate # detect both names used for each filter type: 1445*0Sstevel@tonic-gate if ($tag eq 'FILTER' || $tag eq 'SUNW_FILTER') { 1446*0Sstevel@tonic-gate $type = 'STD'; 1447*0Sstevel@tonic-gate $saw_filter = 1; 1448*0Sstevel@tonic-gate } elsif ($tag eq 'AUXILIARY' || $tag eq 1449*0Sstevel@tonic-gate 'SUNW_AUXILIARY') { 1450*0Sstevel@tonic-gate $type = 'AUX'; 1451*0Sstevel@tonic-gate $saw_aux = 1; 1452*0Sstevel@tonic-gate } 1453*0Sstevel@tonic-gate } 1454*0Sstevel@tonic-gate if ($saw_filter && $saw_aux) { 1455*0Sstevel@tonic-gate $type = 'AUX'; 1456*0Sstevel@tonic-gate } 1457*0Sstevel@tonic-gate $filter_lib_cache{$filter} = $type; 1458*0Sstevel@tonic-gate } 1459*0Sstevel@tonic-gate return $filter_lib_cache{$filter}; 1460*0Sstevel@tonic-gate} 1461*0Sstevel@tonic-gate 1462*0Sstevel@tonic-gate# 1463*0Sstevel@tonic-gate# Calls "abi_index" to dynamically create the list of Solaris libraries 1464*0Sstevel@tonic-gate# and their characteristics. 1465*0Sstevel@tonic-gate# 1466*0Sstevel@tonic-gatesub load_model_index 1467*0Sstevel@tonic-gate{ 1468*0Sstevel@tonic-gate my $dir = "auto"; # all model indexes are created automatically 1469*0Sstevel@tonic-gate 1470*0Sstevel@tonic-gate if (exists($lib_index_loaded{$dir})) { 1471*0Sstevel@tonic-gate if ($lib_index_loaded{$dir} == -1) { 1472*0Sstevel@tonic-gate return 0; 1473*0Sstevel@tonic-gate } else { 1474*0Sstevel@tonic-gate return 1; 1475*0Sstevel@tonic-gate } 1476*0Sstevel@tonic-gate } 1477*0Sstevel@tonic-gate 1478*0Sstevel@tonic-gate my ($lib, $lib2, $def, $cnt, $link_cnt, $all_links); 1479*0Sstevel@tonic-gate my ($key, $base); 1480*0Sstevel@tonic-gate 1481*0Sstevel@tonic-gate my $reading_cache_file; 1482*0Sstevel@tonic-gate 1483*0Sstevel@tonic-gate $link_cnt = 0; 1484*0Sstevel@tonic-gate my $cache_file = "$working_dir/AbiIndex"; 1485*0Sstevel@tonic-gate my $index_fh = do { local *FH; *FH }; 1486*0Sstevel@tonic-gate my $cache_fh = do { local *FH; *FH }; 1487*0Sstevel@tonic-gate if (-f $cache_file) { 1488*0Sstevel@tonic-gate open($index_fh, "<$cache_file") || 1489*0Sstevel@tonic-gate exiter(nofile($cache_file, $!)); 1490*0Sstevel@tonic-gate $reading_cache_file = 1; 1491*0Sstevel@tonic-gate } else { 1492*0Sstevel@tonic-gate if (! open($index_fh, 1493*0Sstevel@tonic-gate "$appcert_lib_dir/abi_index 2>/dev/null |")) { 1494*0Sstevel@tonic-gate exiter(noprogrun("abi_index", $!)); 1495*0Sstevel@tonic-gate } 1496*0Sstevel@tonic-gate if (! open($cache_fh, ">$cache_file")) { 1497*0Sstevel@tonic-gate exiter(nofile($cache_file, $!)); 1498*0Sstevel@tonic-gate } 1499*0Sstevel@tonic-gate $reading_cache_file = 0; 1500*0Sstevel@tonic-gate } 1501*0Sstevel@tonic-gate 1502*0Sstevel@tonic-gate if (! $reading_cache_file) { 1503*0Sstevel@tonic-gate emsg("\n"); 1504*0Sstevel@tonic-gate emsg(gettext("determining list of Solaris libraries")); 1505*0Sstevel@tonic-gate emsg(" ...\n"); 1506*0Sstevel@tonic-gate } 1507*0Sstevel@tonic-gate 1508*0Sstevel@tonic-gate my $abi; 1509*0Sstevel@tonic-gate while (<$index_fh>) { 1510*0Sstevel@tonic-gate next if (/^\s*#/); 1511*0Sstevel@tonic-gate next if (/^\s*$/); 1512*0Sstevel@tonic-gate print $cache_fh $_ if (! $reading_cache_file); 1513*0Sstevel@tonic-gate chomp; 1514*0Sstevel@tonic-gate 1515*0Sstevel@tonic-gate ($abi, $lib, $def, $cnt, $all_links) = split(/\|/, $_, 5); 1516*0Sstevel@tonic-gate 1517*0Sstevel@tonic-gate next if (! -f $lib); 1518*0Sstevel@tonic-gate 1519*0Sstevel@tonic-gate $abi = 'any' if ($abi eq 'unknown'); 1520*0Sstevel@tonic-gate 1521*0Sstevel@tonic-gate # note if $all_links is empty, we still get the base lib. 1522*0Sstevel@tonic-gate foreach $lib2 ($lib, split(/:/, $all_links)) { 1523*0Sstevel@tonic-gate $key = "$dir|$lib2|$abi"; 1524*0Sstevel@tonic-gate $lib_index_definition{$key} = $def; 1525*0Sstevel@tonic-gate 1526*0Sstevel@tonic-gate $base = basename($lib2); 1527*0Sstevel@tonic-gate # 1528*0Sstevel@tonic-gate # store an index of lib basenames to be used for 1529*0Sstevel@tonic-gate # libfoo.so* matching. 1530*0Sstevel@tonic-gate # 1531*0Sstevel@tonic-gate $shared_object_index{$base}++; 1532*0Sstevel@tonic-gate $lib_index{$base}++ if ($base =~ /^lib/); 1533*0Sstevel@tonic-gate 1534*0Sstevel@tonic-gate $link_cnt++; 1535*0Sstevel@tonic-gate } 1536*0Sstevel@tonic-gate # 1537*0Sstevel@tonic-gate # record the device/inode too, used to avoid confusion due 1538*0Sstevel@tonic-gate # to symlinks between *directories* instead of files. E.g.: 1539*0Sstevel@tonic-gate # /usr/lib/64 -> /usr/lib/sparcv9 1540*0Sstevel@tonic-gate # under some crle(1) configurations this can be 1541*0Sstevel@tonic-gate # particularly problematic. 1542*0Sstevel@tonic-gate # 1543*0Sstevel@tonic-gate if (-e $lib) { 1544*0Sstevel@tonic-gate my ($device, $inode) = (stat($lib))[0,1]; 1545*0Sstevel@tonic-gate if (defined($device) && defined($inode)) { 1546*0Sstevel@tonic-gate $key = "$dir|$device/$inode|$abi"; 1547*0Sstevel@tonic-gate $lib_index_definition{$key} = $def; 1548*0Sstevel@tonic-gate } 1549*0Sstevel@tonic-gate } 1550*0Sstevel@tonic-gate } 1551*0Sstevel@tonic-gate close($index_fh); 1552*0Sstevel@tonic-gate close($cache_fh) if (! $reading_cache_file); 1553*0Sstevel@tonic-gate 1554*0Sstevel@tonic-gate # return 1 if library links were loaded. 0 indicates a failure. 1555*0Sstevel@tonic-gate push(@lib_index_loaded, $dir); 1556*0Sstevel@tonic-gate if ($link_cnt) { 1557*0Sstevel@tonic-gate $lib_index_loaded{$dir} = $link_cnt; 1558*0Sstevel@tonic-gate return 1; 1559*0Sstevel@tonic-gate } else { 1560*0Sstevel@tonic-gate $lib_index_loaded{$dir} = -1; 1561*0Sstevel@tonic-gate return 0; 1562*0Sstevel@tonic-gate } 1563*0Sstevel@tonic-gate} 1564*0Sstevel@tonic-gate 1565*0Sstevel@tonic-gate# 1566*0Sstevel@tonic-gate# Returns a list of Solaris library basenames matching a pattern. If a 1567*0Sstevel@tonic-gate# directory name is in $pattern, it will be prepended to each item. 1568*0Sstevel@tonic-gate# 1569*0Sstevel@tonic-gatesub lib_match 1570*0Sstevel@tonic-gate{ 1571*0Sstevel@tonic-gate my ($pattern, $return_something) = @_; 1572*0Sstevel@tonic-gate 1573*0Sstevel@tonic-gate if ($pattern eq '*') { 1574*0Sstevel@tonic-gate # special case '*' 1575*0Sstevel@tonic-gate return $pattern; 1576*0Sstevel@tonic-gate } 1577*0Sstevel@tonic-gate 1578*0Sstevel@tonic-gate # 1579*0Sstevel@tonic-gate # $return_something = 1 means if there was nothing matched, 1580*0Sstevel@tonic-gate # return $pattern to the caller. 1581*0Sstevel@tonic-gate # 1582*0Sstevel@tonic-gate # This sub should only be called to initialize things since it 1583*0Sstevel@tonic-gate # is very slow. (runs the regex over all libraries) Do not call 1584*0Sstevel@tonic-gate # it in a loop over, say, application binaries. Rather, call it 1585*0Sstevel@tonic-gate # before the loop and make note of all the discrete cases. 1586*0Sstevel@tonic-gate # 1587*0Sstevel@tonic-gate 1588*0Sstevel@tonic-gate # To handle libfoo.so* matching, we need the Index file loaded: 1589*0Sstevel@tonic-gate if (! $lib_match_initialized) { 1590*0Sstevel@tonic-gate load_model_index(); 1591*0Sstevel@tonic-gate $lib_match_initialized = 1; 1592*0Sstevel@tonic-gate } 1593*0Sstevel@tonic-gate 1594*0Sstevel@tonic-gate my (@list, @libs, $lib, $id, $patt0, $dir0); 1595*0Sstevel@tonic-gate 1596*0Sstevel@tonic-gate # if empty, set it to "0" for the $id key. 1597*0Sstevel@tonic-gate $return_something = 0 if ($return_something eq ''); 1598*0Sstevel@tonic-gate $id = "$pattern|$return_something"; 1599*0Sstevel@tonic-gate 1600*0Sstevel@tonic-gate if (defined($lib_match_cache{$id})) { 1601*0Sstevel@tonic-gate # If we have already found it, return the cached result. 1602*0Sstevel@tonic-gate return split(/\|/, $lib_match_cache{$id}); 1603*0Sstevel@tonic-gate } 1604*0Sstevel@tonic-gate 1605*0Sstevel@tonic-gate $patt0 = $pattern; 1606*0Sstevel@tonic-gate # extract dirname, if any. 1607*0Sstevel@tonic-gate if ($pattern =~ m,/,) { 1608*0Sstevel@tonic-gate $dir0 = dirname($pattern); 1609*0Sstevel@tonic-gate $pattern = basename($pattern); 1610*0Sstevel@tonic-gate } else { 1611*0Sstevel@tonic-gate $dir0 = ''; 1612*0Sstevel@tonic-gate } 1613*0Sstevel@tonic-gate 1614*0Sstevel@tonic-gate # turn the matching pattern into a regex: 1615*0Sstevel@tonic-gate $pattern =~ s/\./\\./g; # protect .'s 1616*0Sstevel@tonic-gate $pattern =~ s/\*/.*/g; # * -> .* 1617*0Sstevel@tonic-gate $pattern =~ s,/,\\/,g; # protect /'s (see below) 1618*0Sstevel@tonic-gate 1619*0Sstevel@tonic-gate # 1620*0Sstevel@tonic-gate # create a little code to check the match, since there will be a 1621*0Sstevel@tonic-gate # big loop of checks: note the anchoring /^...$/ 1622*0Sstevel@tonic-gate # 1623*0Sstevel@tonic-gate my $regex = qr/^$pattern$/; 1624*0Sstevel@tonic-gate 1625*0Sstevel@tonic-gate if ($pattern =~ /^lib/) { 1626*0Sstevel@tonic-gate # for a bit of speed, the lib* set is much smaller, so use it: 1627*0Sstevel@tonic-gate @libs = keys(%lib_index); 1628*0Sstevel@tonic-gate } else { 1629*0Sstevel@tonic-gate # this is the full list: 1630*0Sstevel@tonic-gate @libs = keys(%shared_object_index); 1631*0Sstevel@tonic-gate } 1632*0Sstevel@tonic-gate 1633*0Sstevel@tonic-gate # now try all libs for a match, and store in @list. 1634*0Sstevel@tonic-gate foreach $lib (@libs) { 1635*0Sstevel@tonic-gate if ($lib =~ /$regex/) { 1636*0Sstevel@tonic-gate if ($dir0 ne '') { 1637*0Sstevel@tonic-gate # put back the dirname: 1638*0Sstevel@tonic-gate $lib = "$dir0/$lib"; 1639*0Sstevel@tonic-gate } 1640*0Sstevel@tonic-gate push(@list, $lib); 1641*0Sstevel@tonic-gate } 1642*0Sstevel@tonic-gate } 1643*0Sstevel@tonic-gate 1644*0Sstevel@tonic-gate # return list and cache result: 1645*0Sstevel@tonic-gate if ($return_something && ! @list) { 1646*0Sstevel@tonic-gate $lib_match_cache{$id} = $patt0; 1647*0Sstevel@tonic-gate return $patt0; 1648*0Sstevel@tonic-gate } else { 1649*0Sstevel@tonic-gate $lib_match_cache{$id} = join('|', @list); 1650*0Sstevel@tonic-gate return @list; 1651*0Sstevel@tonic-gate } 1652*0Sstevel@tonic-gate} 1653*0Sstevel@tonic-gate 1654*0Sstevel@tonic-gate# 1655*0Sstevel@tonic-gate# Expand the matches in a etc.warn MATCH expression. 1656*0Sstevel@tonic-gate# returns subroutine code for the comparison. 1657*0Sstevel@tonic-gate# 1658*0Sstevel@tonic-gatesub expand_expr 1659*0Sstevel@tonic-gate{ 1660*0Sstevel@tonic-gate my($expr) = @_; 1661*0Sstevel@tonic-gate my $code = 'my($fn) = @_; '; 1662*0Sstevel@tonic-gate $expr =~ s/\bfile\s*\=\~\s*/ cmd_output_file(\$fn) =~ /g; 1663*0Sstevel@tonic-gate $expr =~ s/\bdump\s*\=\~\s*/ cmd_output_dump(\$fn) =~ /g; 1664*0Sstevel@tonic-gate $expr =~ s/\bneeded\s*\=\~\s*/ all_ldd_neededs_string(\$fn) =~ /g; 1665*0Sstevel@tonic-gate $expr =~ s/\bsyms\s*\=\~\s*/ direct_syms(\$fn) =~ /g; 1666*0Sstevel@tonic-gate 1667*0Sstevel@tonic-gate $code .= "if ($expr) {return 1;} else {return 0;}"; 1668*0Sstevel@tonic-gate return $code; 1669*0Sstevel@tonic-gate} 1670*0Sstevel@tonic-gate 1671*0Sstevel@tonic-gate# 1672*0Sstevel@tonic-gate# Loads the binary stability information contained in the 1673*0Sstevel@tonic-gate# /usr/lib/abi/appcert/etc.* files. 1674*0Sstevel@tonic-gate# 1675*0Sstevel@tonic-gatesub load_misc_check_databases 1676*0Sstevel@tonic-gate{ 1677*0Sstevel@tonic-gate my $etc_dir = "$appcert_lib_dir"; 1678*0Sstevel@tonic-gate 1679*0Sstevel@tonic-gate my ($etc_file, $line); 1680*0Sstevel@tonic-gate 1681*0Sstevel@tonic-gate my (@etcs) = <$etc_dir/etc.*>; 1682*0Sstevel@tonic-gate 1683*0Sstevel@tonic-gate # 1684*0Sstevel@tonic-gate # Event(etc.) types to handle: 1685*0Sstevel@tonic-gate # 1686*0Sstevel@tonic-gate # SCOPED_SYMBOL|<release>|<lib>|<sym> 1687*0Sstevel@tonic-gate # MODEL_TWEAK|<library>|<abi1,...>|<symbol>|<classification> 1688*0Sstevel@tonic-gate # REMOVED_SYMBOL|<release>|<lib>|<sym> 1689*0Sstevel@tonic-gate # 1690*0Sstevel@tonic-gate 1691*0Sstevel@tonic-gate my ($tag, $rel, $lib, $sym, $rest); 1692*0Sstevel@tonic-gate my ($abis, $class, $tmp, $gather); 1693*0Sstevel@tonic-gate 1694*0Sstevel@tonic-gate # Read in and process all the etc files: 1695*0Sstevel@tonic-gate my $count = 0; 1696*0Sstevel@tonic-gate foreach $etc_file (@etcs) { 1697*0Sstevel@tonic-gate my $etc_fh = do { local *FH; *FH }; 1698*0Sstevel@tonic-gate if (! open($etc_fh, "<$etc_file")) { 1699*0Sstevel@tonic-gate exiter(nofile($etc_file, $!)); 1700*0Sstevel@tonic-gate } 1701*0Sstevel@tonic-gate while (<$etc_fh>) { 1702*0Sstevel@tonic-gate # read each line: 1703*0Sstevel@tonic-gate chomp($line = $_); 1704*0Sstevel@tonic-gate 1705*0Sstevel@tonic-gate # gather lines continued with "\" at end: 1706*0Sstevel@tonic-gate while ($line =~ /\\$/) { 1707*0Sstevel@tonic-gate chomp($line); 1708*0Sstevel@tonic-gate last if (eof($etc_fh)); 1709*0Sstevel@tonic-gate chomp($tmp = <$etc_fh>); 1710*0Sstevel@tonic-gate # handle "-" ... "-" style text blocks. 1711*0Sstevel@tonic-gate if ($tmp eq '-') { 1712*0Sstevel@tonic-gate # 1713*0Sstevel@tonic-gate # gather everything until the 1714*0Sstevel@tonic-gate # next '-' line. 1715*0Sstevel@tonic-gate # 1716*0Sstevel@tonic-gate $gather = ''; 1717*0Sstevel@tonic-gate while (1) { 1718*0Sstevel@tonic-gate last if (eof($etc_fh)); 1719*0Sstevel@tonic-gate chomp($tmp = <$etc_fh>); 1720*0Sstevel@tonic-gate last if ($tmp eq '-'); 1721*0Sstevel@tonic-gate $gather .= "|$tmp"; 1722*0Sstevel@tonic-gate } 1723*0Sstevel@tonic-gate $line .= $gather; 1724*0Sstevel@tonic-gate } else { 1725*0Sstevel@tonic-gate $line .= " " . $tmp; 1726*0Sstevel@tonic-gate } 1727*0Sstevel@tonic-gate } 1728*0Sstevel@tonic-gate 1729*0Sstevel@tonic-gate # 1730*0Sstevel@tonic-gate # skip blank lines or lines (including continued lines) 1731*0Sstevel@tonic-gate # beginning with "#" 1732*0Sstevel@tonic-gate # 1733*0Sstevel@tonic-gate next if ($line =~ /^\s*#/); 1734*0Sstevel@tonic-gate next if ($line =~ /^\s*$/); 1735*0Sstevel@tonic-gate 1736*0Sstevel@tonic-gate my $lib2; 1737*0Sstevel@tonic-gate 1738*0Sstevel@tonic-gate # Case statement for all the types: 1739*0Sstevel@tonic-gate if ($line =~ /^SCOPED_SYMBOL/) { 1740*0Sstevel@tonic-gate ($tag, $rel, $lib, $sym, $rest) = 1741*0Sstevel@tonic-gate split(/\|/, $line, 5); 1742*0Sstevel@tonic-gate # 1743*0Sstevel@tonic-gate # current implementation uses library basename. 1744*0Sstevel@tonic-gate # 1745*0Sstevel@tonic-gate # We may also want to split this value 1746*0Sstevel@tonic-gate # into a hash or two, e.g. 1747*0Sstevel@tonic-gate # Scope_Symbol_Release, etc.. 1748*0Sstevel@tonic-gate # 1749*0Sstevel@tonic-gate # No lib_match wild-carding done for this case. 1750*0Sstevel@tonic-gate # 1751*0Sstevel@tonic-gate $scoped_symbol{"$lib|$sym"} .= 1752*0Sstevel@tonic-gate "$rel|$lib|$sym,"; 1753*0Sstevel@tonic-gate $scoped_symbol_all{"$sym"} .= 1754*0Sstevel@tonic-gate "$rel|$lib|$sym,"; 1755*0Sstevel@tonic-gate } elsif ($line =~ /^SKIP_SYMBOL/) { 1756*0Sstevel@tonic-gate # 1757*0Sstevel@tonic-gate # These are low-level, e.g. C runtime 1758*0Sstevel@tonic-gate # we always want to skip. 1759*0Sstevel@tonic-gate # 1760*0Sstevel@tonic-gate ($tag, $sym) = split(/\|/, $line, 2); 1761*0Sstevel@tonic-gate $skip_symbols{$sym} = 1; 1762*0Sstevel@tonic-gate 1763*0Sstevel@tonic-gate } elsif ($line =~ /^MODEL_TWEAK/) { 1764*0Sstevel@tonic-gate # 1765*0Sstevel@tonic-gate # These are direct edits of symbol 1766*0Sstevel@tonic-gate # public/private database. 1767*0Sstevel@tonic-gate # 1768*0Sstevel@tonic-gate ($tag, $lib, $abis, $sym, $class) = 1769*0Sstevel@tonic-gate split(/\|/, $line, 5); 1770*0Sstevel@tonic-gate # change arch sep from "," to "%" 1771*0Sstevel@tonic-gate $abis =~ s/,/%/g; 1772*0Sstevel@tonic-gate 1773*0Sstevel@tonic-gate my (@libs, $lib64, @tmp); 1774*0Sstevel@tonic-gate if ($lib =~ /\*/) { 1775*0Sstevel@tonic-gate @libs = lib_match($lib, 1); 1776*0Sstevel@tonic-gate } else { 1777*0Sstevel@tonic-gate push(@libs, $lib); 1778*0Sstevel@tonic-gate } 1779*0Sstevel@tonic-gate if ($abis eq '*') { 1780*0Sstevel@tonic-gate # 1781*0Sstevel@tonic-gate # '*' means all ABIs, so we modify 1782*0Sstevel@tonic-gate # pathnames to reflect the 64 bit 1783*0Sstevel@tonic-gate # versions. If these exists on the 1784*0Sstevel@tonic-gate # system, we append them to the list 1785*0Sstevel@tonic-gate # for this tweak. 1786*0Sstevel@tonic-gate # 1787*0Sstevel@tonic-gate @tmp = @libs; 1788*0Sstevel@tonic-gate foreach $lib2 (@tmp) { 1789*0Sstevel@tonic-gate if ($lib2 !~ m,/lib/,) { 1790*0Sstevel@tonic-gate next; 1791*0Sstevel@tonic-gate } 1792*0Sstevel@tonic-gate # 1793*0Sstevel@tonic-gate # check for existence of sparc 1794*0Sstevel@tonic-gate # and x86 64 bit versions. 1795*0Sstevel@tonic-gate # 1796*0Sstevel@tonic-gate $lib64 = $lib2; 1797*0Sstevel@tonic-gate $lib64 =~ 1798*0Sstevel@tonic-gate s,/lib/,/lib/sparcv9/,; 1799*0Sstevel@tonic-gate if (-e $lib64) { 1800*0Sstevel@tonic-gate push(@libs, $lib64); 1801*0Sstevel@tonic-gate } 1802*0Sstevel@tonic-gate $lib64 = $lib2; 1803*0Sstevel@tonic-gate $lib64 =~ s,/lib/,/lib/amd64/,; 1804*0Sstevel@tonic-gate if (-e $lib64) { 1805*0Sstevel@tonic-gate push(@libs, $lib64); 1806*0Sstevel@tonic-gate } 1807*0Sstevel@tonic-gate $lib64 = $lib2; 1808*0Sstevel@tonic-gate $lib64 =~ s,/lib/,/lib/64/,; 1809*0Sstevel@tonic-gate if (-e $lib64) { 1810*0Sstevel@tonic-gate push(@libs, $lib64); 1811*0Sstevel@tonic-gate } 1812*0Sstevel@tonic-gate } 1813*0Sstevel@tonic-gate } 1814*0Sstevel@tonic-gate 1815*0Sstevel@tonic-gate @tmp = @libs; 1816*0Sstevel@tonic-gate foreach $lib2 (@tmp) { 1817*0Sstevel@tonic-gate if ($lib2 !~ m,/, || ! -e $lib2) { 1818*0Sstevel@tonic-gate next; 1819*0Sstevel@tonic-gate } 1820*0Sstevel@tonic-gate # 1821*0Sstevel@tonic-gate # if it exists on the system, 1822*0Sstevel@tonic-gate # store info wrt inode as well: 1823*0Sstevel@tonic-gate # 1824*0Sstevel@tonic-gate my ($device, $inode); 1825*0Sstevel@tonic-gate ($device, $inode) = (stat($lib2))[0,1]; 1826*0Sstevel@tonic-gate if ($device ne '' && $inode ne '') { 1827*0Sstevel@tonic-gate push(@libs, "$device/$inode"); 1828*0Sstevel@tonic-gate } 1829*0Sstevel@tonic-gate } 1830*0Sstevel@tonic-gate 1831*0Sstevel@tonic-gate # 1832*0Sstevel@tonic-gate # now store the tweak info for all associated 1833*0Sstevel@tonic-gate # libraries. 1834*0Sstevel@tonic-gate # 1835*0Sstevel@tonic-gate foreach $lib2 (@libs) { 1836*0Sstevel@tonic-gate $model_tweak{$lib2} .= 1837*0Sstevel@tonic-gate "$sym|$abis|$class,"; 1838*0Sstevel@tonic-gate } 1839*0Sstevel@tonic-gate 1840*0Sstevel@tonic-gate } elsif ($line =~ /^WARNING:/) { 1841*0Sstevel@tonic-gate # 1842*0Sstevel@tonic-gate # Extra warnings for miscellaneous problems. 1843*0Sstevel@tonic-gate # 1844*0Sstevel@tonic-gate my $cnt = 0; 1845*0Sstevel@tonic-gate my ($warn, $tag, $desc, $bindings); 1846*0Sstevel@tonic-gate my ($bind, $text); 1847*0Sstevel@tonic-gate ($warn, $tag, $desc, $bindings, $text) = 1848*0Sstevel@tonic-gate split(/:/, $line, 5); 1849*0Sstevel@tonic-gate 1850*0Sstevel@tonic-gate # trim any leading spaces: 1851*0Sstevel@tonic-gate $tag =~ s/^\s*//; 1852*0Sstevel@tonic-gate $desc =~ s/^\s*//; 1853*0Sstevel@tonic-gate $bindings =~ s/^\s*//; 1854*0Sstevel@tonic-gate $text =~ s/^\s*//; 1855*0Sstevel@tonic-gate 1856*0Sstevel@tonic-gate $tag =~ s,[\s/;]+,_,g; 1857*0Sstevel@tonic-gate 1858*0Sstevel@tonic-gate # 1859*0Sstevel@tonic-gate # desc lists will be ";" delimited, so 1860*0Sstevel@tonic-gate # replace any found in the text. 1861*0Sstevel@tonic-gate # 1862*0Sstevel@tonic-gate $desc =~ s/;/,/g; 1863*0Sstevel@tonic-gate $desc = trim($desc); 1864*0Sstevel@tonic-gate 1865*0Sstevel@tonic-gate 1866*0Sstevel@tonic-gate # Store info in %Warnings_* hashes: 1867*0Sstevel@tonic-gate 1868*0Sstevel@tonic-gate $warnings_desc{$tag} = $desc; 1869*0Sstevel@tonic-gate 1870*0Sstevel@tonic-gate $warnings_match{$tag} = ''; 1871*0Sstevel@tonic-gate 1872*0Sstevel@tonic-gate if ($bindings =~ /^MATCH\s*(\S.*)$/) { 1873*0Sstevel@tonic-gate # 1874*0Sstevel@tonic-gate # Handle the pattern MATCH 1875*0Sstevel@tonic-gate # case. Note there there is no 1876*0Sstevel@tonic-gate # libfoo.so.* matching here. 1877*0Sstevel@tonic-gate # 1878*0Sstevel@tonic-gate my $expr = $1; 1879*0Sstevel@tonic-gate my $code; 1880*0Sstevel@tonic-gate 1881*0Sstevel@tonic-gate # 1882*0Sstevel@tonic-gate # For efficiency we will create 1883*0Sstevel@tonic-gate # a subroutine for each case. 1884*0Sstevel@tonic-gate # 1885*0Sstevel@tonic-gate 1886*0Sstevel@tonic-gate # get subref code: 1887*0Sstevel@tonic-gate $code = expand_expr($expr); 1888*0Sstevel@tonic-gate 1889*0Sstevel@tonic-gate # define the subroutine: 1890*0Sstevel@tonic-gate 1891*0Sstevel@tonic-gate my $subref; 1892*0Sstevel@tonic-gate eval "\$subref = sub { $code };"; 1893*0Sstevel@tonic-gate if ("$@" eq "" && $subref) { 1894*0Sstevel@tonic-gate $warnings_match{$tag} = $subref; 1895*0Sstevel@tonic-gate } 1896*0Sstevel@tonic-gate } else { 1897*0Sstevel@tonic-gate # 1898*0Sstevel@tonic-gate # Otherwise, it is a 1899*0Sstevel@tonic-gate # lib|sym|caller type match 1900*0Sstevel@tonic-gate # 1901*0Sstevel@tonic-gate my ($lib, $sym, $rest); 1902*0Sstevel@tonic-gate foreach $bind (split(/,/, $bindings)) { 1903*0Sstevel@tonic-gate # 1904*0Sstevel@tonic-gate # Create pseudo tag, 1905*0Sstevel@tonic-gate # "tag|N", for each 1906*0Sstevel@tonic-gate # binding. 1907*0Sstevel@tonic-gate # 1908*0Sstevel@tonic-gate $bind = trim($bind); 1909*0Sstevel@tonic-gate ($lib, $sym, $rest) = 1910*0Sstevel@tonic-gate split(/\|/, $bind, 3); 1911*0Sstevel@tonic-gate foreach $lib2 1912*0Sstevel@tonic-gate (lib_match($lib, 1)) { 1913*0Sstevel@tonic-gate $tmp = "$tag|$cnt"; 1914*0Sstevel@tonic-gate $warnings_bind{$tmp} = 1915*0Sstevel@tonic-gate "$lib2|$sym|$rest"; 1916*0Sstevel@tonic-gate $warnings_desc{$tmp} = 1917*0Sstevel@tonic-gate $desc; 1918*0Sstevel@tonic-gate $cnt++; 1919*0Sstevel@tonic-gate } 1920*0Sstevel@tonic-gate } 1921*0Sstevel@tonic-gate } 1922*0Sstevel@tonic-gate } 1923*0Sstevel@tonic-gate } 1924*0Sstevel@tonic-gate $count++; 1925*0Sstevel@tonic-gate close($etc_fh); 1926*0Sstevel@tonic-gate } 1927*0Sstevel@tonic-gate 1928*0Sstevel@tonic-gate # Trim any trailing "," separators from the last append: 1929*0Sstevel@tonic-gate 1930*0Sstevel@tonic-gate my $key; 1931*0Sstevel@tonic-gate foreach $key (keys(%scoped_symbol)) { 1932*0Sstevel@tonic-gate $scoped_symbol{$key} =~ s/,+$//; 1933*0Sstevel@tonic-gate } 1934*0Sstevel@tonic-gate foreach $key (keys(%scoped_symbol_all)) { 1935*0Sstevel@tonic-gate $scoped_symbol_all{$key} =~ s/,+$//; 1936*0Sstevel@tonic-gate } 1937*0Sstevel@tonic-gate foreach $key (keys(%model_tweak)) { 1938*0Sstevel@tonic-gate $model_tweak{$key} =~ s/,+$//; 1939*0Sstevel@tonic-gate # 1940*0Sstevel@tonic-gate # make sure tweak is associated with device/inode to aid not 1941*0Sstevel@tonic-gate # getting tricked by symlinks under crle, LD_LIBRARY_PATH, etc. 1942*0Sstevel@tonic-gate # 1943*0Sstevel@tonic-gate my ($device, $inode) = (stat($key))[0,1]; 1944*0Sstevel@tonic-gate if (defined($device) && defined($inode)) { 1945*0Sstevel@tonic-gate $model_tweak{"$device/$inode"} = $model_tweak{$key}; 1946*0Sstevel@tonic-gate } 1947*0Sstevel@tonic-gate } 1948*0Sstevel@tonic-gate return $count; 1949*0Sstevel@tonic-gate} 1950*0Sstevel@tonic-gate 1951*0Sstevel@tonic-gate1; 1952