1*7c478bd9Sstevel@tonic-gate#!/usr/perl5/bin/perl -w
2*7c478bd9Sstevel@tonic-gate#
3*7c478bd9Sstevel@tonic-gate# CDDL HEADER START
4*7c478bd9Sstevel@tonic-gate#
5*7c478bd9Sstevel@tonic-gate# The contents of this file are subject to the terms of the
6*7c478bd9Sstevel@tonic-gate# Common Development and Distribution License, Version 1.0 only
7*7c478bd9Sstevel@tonic-gate# (the "License").  You may not use this file except in compliance
8*7c478bd9Sstevel@tonic-gate# with the License.
9*7c478bd9Sstevel@tonic-gate#
10*7c478bd9Sstevel@tonic-gate# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
11*7c478bd9Sstevel@tonic-gate# or http://www.opensolaris.org/os/licensing.
12*7c478bd9Sstevel@tonic-gate# See the License for the specific language governing permissions
13*7c478bd9Sstevel@tonic-gate# and limitations under the License.
14*7c478bd9Sstevel@tonic-gate#
15*7c478bd9Sstevel@tonic-gate# When distributing Covered Code, include this CDDL HEADER in each
16*7c478bd9Sstevel@tonic-gate# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17*7c478bd9Sstevel@tonic-gate# If applicable, add the following below this CDDL HEADER, with the
18*7c478bd9Sstevel@tonic-gate# fields enclosed by brackets "[]" replaced with your own identifying
19*7c478bd9Sstevel@tonic-gate# information: Portions Copyright [yyyy] [name of copyright owner]
20*7c478bd9Sstevel@tonic-gate#
21*7c478bd9Sstevel@tonic-gate# CDDL HEADER END
22*7c478bd9Sstevel@tonic-gate#
23*7c478bd9Sstevel@tonic-gate#
24*7c478bd9Sstevel@tonic-gate# ident	"%Z%%M%	%I%	%E% SMI"
25*7c478bd9Sstevel@tonic-gate#
26*7c478bd9Sstevel@tonic-gate# Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
27*7c478bd9Sstevel@tonic-gate# Use is subject to license terms.
28*7c478bd9Sstevel@tonic-gate#
29*7c478bd9Sstevel@tonic-gate
30*7c478bd9Sstevel@tonic-gate#
31*7c478bd9Sstevel@tonic-gate# This is the top level script for performing the appcert checks.  It
32*7c478bd9Sstevel@tonic-gate# reads the command line options, determines list of binaries to check,
33*7c478bd9Sstevel@tonic-gate# and then calls symprof (the raw symbol profiler), symcheck (that
34*7c478bd9Sstevel@tonic-gate# checks for unstable behavior), and symreport (that constructs and
35*7c478bd9Sstevel@tonic-gate# outputs a rollup report)
36*7c478bd9Sstevel@tonic-gate#
37*7c478bd9Sstevel@tonic-gate
38*7c478bd9Sstevel@tonic-gaterequire 5.005;
39*7c478bd9Sstevel@tonic-gateuse strict;
40*7c478bd9Sstevel@tonic-gateuse locale;
41*7c478bd9Sstevel@tonic-gateuse Getopt::Std;
42*7c478bd9Sstevel@tonic-gateuse POSIX qw(locale_h);
43*7c478bd9Sstevel@tonic-gateuse Sun::Solaris::Utils qw(textdomain gettext);
44*7c478bd9Sstevel@tonic-gateuse File::Basename;
45*7c478bd9Sstevel@tonic-gateuse File::Path;
46*7c478bd9Sstevel@tonic-gate
47*7c478bd9Sstevel@tonic-gateuse lib qw(/usr/lib/abi/appcert);
48*7c478bd9Sstevel@tonic-gateuse AppcertUtil;
49*7c478bd9Sstevel@tonic-gate
50*7c478bd9Sstevel@tonic-gatesetlocale(LC_ALL, "");
51*7c478bd9Sstevel@tonic-gatetextdomain(TEXT_DOMAIN);
52*7c478bd9Sstevel@tonic-gate
53*7c478bd9Sstevel@tonic-gateuse vars qw(
54*7c478bd9Sstevel@tonic-gate	@item_list
55*7c478bd9Sstevel@tonic-gate	$file_list
56*7c478bd9Sstevel@tonic-gate	$do_not_follow_symlinks
57*7c478bd9Sstevel@tonic-gate	$modify_ld_path
58*7c478bd9Sstevel@tonic-gate	$append_solaris_dirs_to_ld_path
59*7c478bd9Sstevel@tonic-gate	$skipped_count
60*7c478bd9Sstevel@tonic-gate);
61*7c478bd9Sstevel@tonic-gate
62*7c478bd9Sstevel@tonic-gatemy $caught_signal = 0;
63*7c478bd9Sstevel@tonic-gatemy $record_binary_call_count = 0;
64*7c478bd9Sstevel@tonic-gate
65*7c478bd9Sstevel@tonic-gate# The directory where the appcert specific scripts and data reside:
66*7c478bd9Sstevel@tonic-gate$appcert_lib_dir = "/usr/lib/abi/appcert";
67*7c478bd9Sstevel@tonic-gate
68*7c478bd9Sstevel@tonic-gateset_clean_up_exit_routine(\&clean_up_exit);
69*7c478bd9Sstevel@tonic-gate
70*7c478bd9Sstevel@tonic-gatesignals('on', \&interrupted);
71*7c478bd9Sstevel@tonic-gate
72*7c478bd9Sstevel@tonic-gateget_options();
73*7c478bd9Sstevel@tonic-gate
74*7c478bd9Sstevel@tonic-gate@item_list = @ARGV;		# List of directories and/or objects to check.
75*7c478bd9Sstevel@tonic-gatecheck_item_list();
76*7c478bd9Sstevel@tonic-gate
77*7c478bd9Sstevel@tonic-gateset_working_dir();
78*7c478bd9Sstevel@tonic-gate
79*7c478bd9Sstevel@tonic-gatefind_binaries();		# Records all of the binary objects to check.
80*7c478bd9Sstevel@tonic-gate
81*7c478bd9Sstevel@tonic-gatesupplement_ld_library_path();
82*7c478bd9Sstevel@tonic-gate
83*7c478bd9Sstevel@tonic-gateexport_vars_to_environment();	# Exports info for our child scripts to use.
84*7c478bd9Sstevel@tonic-gate
85*7c478bd9Sstevel@tonic-gaterun_profiler();			# Run the script symprof.
86*7c478bd9Sstevel@tonic-gate
87*7c478bd9Sstevel@tonic-gaterun_checker();			# Run script symcheck.
88*7c478bd9Sstevel@tonic-gate
89*7c478bd9Sstevel@tonic-gaterun_report_generator();		# Run the script symreport.
90*7c478bd9Sstevel@tonic-gate
91*7c478bd9Sstevel@tonic-gatemy $rc = overall_result_code();
92*7c478bd9Sstevel@tonic-gate
93*7c478bd9Sstevel@tonic-gateclean_up();
94*7c478bd9Sstevel@tonic-gate
95*7c478bd9Sstevel@tonic-gateexit $rc;
96*7c478bd9Sstevel@tonic-gate
97*7c478bd9Sstevel@tonic-gate
98*7c478bd9Sstevel@tonic-gate#
99*7c478bd9Sstevel@tonic-gate# This subroutine calls getopts() and sets up variables reflecting how
100*7c478bd9Sstevel@tonic-gate# we were called.
101*7c478bd9Sstevel@tonic-gate#
102*7c478bd9Sstevel@tonic-gatesub get_options
103*7c478bd9Sstevel@tonic-gate{
104*7c478bd9Sstevel@tonic-gate	my %opt;
105*7c478bd9Sstevel@tonic-gate
106*7c478bd9Sstevel@tonic-gate	getopts('?hnLBSw:f:', \%opt) || (show_usage() && exiter(2));
107*7c478bd9Sstevel@tonic-gate
108*7c478bd9Sstevel@tonic-gate	if (exists($opt{'?'}) || exists($opt{'h'})) {
109*7c478bd9Sstevel@tonic-gate		show_usage();
110*7c478bd9Sstevel@tonic-gate		exiter(2);
111*7c478bd9Sstevel@tonic-gate	}
112*7c478bd9Sstevel@tonic-gate
113*7c478bd9Sstevel@tonic-gate	if (exists($opt{'f'})) {
114*7c478bd9Sstevel@tonic-gate		$file_list = $opt{'f'};
115*7c478bd9Sstevel@tonic-gate	} else {
116*7c478bd9Sstevel@tonic-gate		$file_list = '';
117*7c478bd9Sstevel@tonic-gate	}
118*7c478bd9Sstevel@tonic-gate
119*7c478bd9Sstevel@tonic-gate	if (exists($opt{'w'})) {
120*7c478bd9Sstevel@tonic-gate		$working_dir = $opt{'w'};
121*7c478bd9Sstevel@tonic-gate	} else {
122*7c478bd9Sstevel@tonic-gate		$working_dir = '';
123*7c478bd9Sstevel@tonic-gate	}
124*7c478bd9Sstevel@tonic-gate	if ($working_dir =~ /'/) {
125*7c478bd9Sstevel@tonic-gate		#
126*7c478bd9Sstevel@tonic-gate		# This character will ultimately cause problems with
127*7c478bd9Sstevel@tonic-gate		# system() and pipelines so we exit now.
128*7c478bd9Sstevel@tonic-gate		#
129*7c478bd9Sstevel@tonic-gate		exiter(sprintf(gettext(
130*7c478bd9Sstevel@tonic-gate		    "directory contains the single-quote character ': %s\n"),
131*7c478bd9Sstevel@tonic-gate		    $working_dir));
132*7c478bd9Sstevel@tonic-gate	}
133*7c478bd9Sstevel@tonic-gate
134*7c478bd9Sstevel@tonic-gate	if (defined($opt{'B'})) {
135*7c478bd9Sstevel@tonic-gate		$batch_report = 1;
136*7c478bd9Sstevel@tonic-gate	} else {
137*7c478bd9Sstevel@tonic-gate		$batch_report = 0;
138*7c478bd9Sstevel@tonic-gate	}
139*7c478bd9Sstevel@tonic-gate
140*7c478bd9Sstevel@tonic-gate	if (defined($opt{'n'})) {
141*7c478bd9Sstevel@tonic-gate		$do_not_follow_symlinks = 1;
142*7c478bd9Sstevel@tonic-gate	} else {
143*7c478bd9Sstevel@tonic-gate		$do_not_follow_symlinks = 0;
144*7c478bd9Sstevel@tonic-gate	}
145*7c478bd9Sstevel@tonic-gate
146*7c478bd9Sstevel@tonic-gate	if (defined($opt{'L'})) {
147*7c478bd9Sstevel@tonic-gate		$modify_ld_path = 0;
148*7c478bd9Sstevel@tonic-gate	} else {
149*7c478bd9Sstevel@tonic-gate		$modify_ld_path = 1;
150*7c478bd9Sstevel@tonic-gate	}
151*7c478bd9Sstevel@tonic-gate
152*7c478bd9Sstevel@tonic-gate	if (defined($opt{'S'})) {
153*7c478bd9Sstevel@tonic-gate		$append_solaris_dirs_to_ld_path = 1;
154*7c478bd9Sstevel@tonic-gate	} else {
155*7c478bd9Sstevel@tonic-gate		$append_solaris_dirs_to_ld_path = 0;
156*7c478bd9Sstevel@tonic-gate	}
157*7c478bd9Sstevel@tonic-gate}
158*7c478bd9Sstevel@tonic-gate
159*7c478bd9Sstevel@tonic-gate#
160*7c478bd9Sstevel@tonic-gate# Performs an initial check to see if the user supplied anything at all
161*7c478bd9Sstevel@tonic-gate# to check.  Also reads in the file list if the user supplied one via -f <file>
162*7c478bd9Sstevel@tonic-gate#
163*7c478bd9Sstevel@tonic-gatesub check_item_list
164*7c478bd9Sstevel@tonic-gate{
165*7c478bd9Sstevel@tonic-gate	# Add the items if the -f flag was used.
166*7c478bd9Sstevel@tonic-gate	if ($file_list) {
167*7c478bd9Sstevel@tonic-gate		my $file;
168*7c478bd9Sstevel@tonic-gate		my $list_fh = do { local *FH; *FH };
169*7c478bd9Sstevel@tonic-gate		if (-f $file_list && open($list_fh, "<$file_list")) {
170*7c478bd9Sstevel@tonic-gate			while (<$list_fh>) {
171*7c478bd9Sstevel@tonic-gate				chomp($file = $_);
172*7c478bd9Sstevel@tonic-gate				push(@item_list, $file);
173*7c478bd9Sstevel@tonic-gate			}
174*7c478bd9Sstevel@tonic-gate			close($list_fh);
175*7c478bd9Sstevel@tonic-gate		} else {
176*7c478bd9Sstevel@tonic-gate			exiter(nofile($file_list, $!));
177*7c478bd9Sstevel@tonic-gate		}
178*7c478bd9Sstevel@tonic-gate	}
179*7c478bd9Sstevel@tonic-gate
180*7c478bd9Sstevel@tonic-gate	return if (@item_list);
181*7c478bd9Sstevel@tonic-gate
182*7c478bd9Sstevel@tonic-gate	emsg("$command_name: " . gettext(
183*7c478bd9Sstevel@tonic-gate	    "at least one file or directory to check must be specified.") .
184*7c478bd9Sstevel@tonic-gate	    "\n\n");
185*7c478bd9Sstevel@tonic-gate
186*7c478bd9Sstevel@tonic-gate	show_usage();
187*7c478bd9Sstevel@tonic-gate	exiter(3);
188*7c478bd9Sstevel@tonic-gate}
189*7c478bd9Sstevel@tonic-gate
190*7c478bd9Sstevel@tonic-gate#
191*7c478bd9Sstevel@tonic-gate# This subroutine sets up the working directory, the default something
192*7c478bd9Sstevel@tonic-gate# like: /tmp/appcert.<PID>
193*7c478bd9Sstevel@tonic-gate#
194*7c478bd9Sstevel@tonic-gatesub set_working_dir
195*7c478bd9Sstevel@tonic-gate{
196*7c478bd9Sstevel@tonic-gate	if ($working_dir) {
197*7c478bd9Sstevel@tonic-gate		# working_dir has been set in get_options().
198*7c478bd9Sstevel@tonic-gate		if (! -d $working_dir) {
199*7c478bd9Sstevel@tonic-gate			if (! mkpath($working_dir) || ! -d $working_dir) {
200*7c478bd9Sstevel@tonic-gate				exiter(nocreatedir($working_dir, $!));
201*7c478bd9Sstevel@tonic-gate			}
202*7c478bd9Sstevel@tonic-gate		} else {
203*7c478bd9Sstevel@tonic-gate			if (! dir_is_empty($working_dir)) {
204*7c478bd9Sstevel@tonic-gate				# create a subdir of it for our use.
205*7c478bd9Sstevel@tonic-gate				$working_dir = create_tmp_dir($working_dir);
206*7c478bd9Sstevel@tonic-gate			}
207*7c478bd9Sstevel@tonic-gate		}
208*7c478bd9Sstevel@tonic-gate	} else {
209*7c478bd9Sstevel@tonic-gate		# Default case: will create, e.g., /tmp/appcert.12345
210*7c478bd9Sstevel@tonic-gate		$working_dir = create_tmp_dir();
211*7c478bd9Sstevel@tonic-gate	}
212*7c478bd9Sstevel@tonic-gate
213*7c478bd9Sstevel@tonic-gate	if (! -d $working_dir) {
214*7c478bd9Sstevel@tonic-gate		# We have no working directory.
215*7c478bd9Sstevel@tonic-gate		exiter(nocreatedir($working_dir));
216*7c478bd9Sstevel@tonic-gate	}
217*7c478bd9Sstevel@tonic-gate
218*7c478bd9Sstevel@tonic-gate	#
219*7c478bd9Sstevel@tonic-gate	# Create a subdirectory of working_dir that will contain all of
220*7c478bd9Sstevel@tonic-gate	# the object subdirs.
221*7c478bd9Sstevel@tonic-gate	#
222*7c478bd9Sstevel@tonic-gate	my $dir = "$working_dir/$object_dir";
223*7c478bd9Sstevel@tonic-gate	if (! mkpath($dir) || ! -d $dir) {
224*7c478bd9Sstevel@tonic-gate		exiter(nocreatedir($dir, $!));
225*7c478bd9Sstevel@tonic-gate	}
226*7c478bd9Sstevel@tonic-gate	#
227*7c478bd9Sstevel@tonic-gate	# Make a tmp subdirectory for small temporary work. It is
228*7c478bd9Sstevel@tonic-gate	# preferred to have it on tmpfs (especially not NFS) for
229*7c478bd9Sstevel@tonic-gate	# performance reasons.
230*7c478bd9Sstevel@tonic-gate	#
231*7c478bd9Sstevel@tonic-gate	$tmp_dir = "/tmp/${command_name}_tmp.$$";
232*7c478bd9Sstevel@tonic-gate	if (-d $tmp_dir) {
233*7c478bd9Sstevel@tonic-gate		exiter(nocreatedir("$tmp_dir", $!));
234*7c478bd9Sstevel@tonic-gate	}
235*7c478bd9Sstevel@tonic-gate	if (! mkpath($tmp_dir, 0, 0700) || ! -d $tmp_dir) {
236*7c478bd9Sstevel@tonic-gate		emsg("%s", nocreatedir($tmp_dir, $!));
237*7c478bd9Sstevel@tonic-gate		# fall back to our output dir (which could have slow access)
238*7c478bd9Sstevel@tonic-gate		$tmp_dir = "$working_dir/tmp";
239*7c478bd9Sstevel@tonic-gate		if (! mkpath($tmp_dir)) {
240*7c478bd9Sstevel@tonic-gate			exiter(nocreatedir($tmp_dir, $!));
241*7c478bd9Sstevel@tonic-gate		}
242*7c478bd9Sstevel@tonic-gate	}
243*7c478bd9Sstevel@tonic-gate
244*7c478bd9Sstevel@tonic-gate	if (! -d $tmp_dir) {
245*7c478bd9Sstevel@tonic-gate		exiter(nocreatedir($tmp_dir, $!));
246*7c478bd9Sstevel@tonic-gate	}
247*7c478bd9Sstevel@tonic-gate}
248*7c478bd9Sstevel@tonic-gate
249*7c478bd9Sstevel@tonic-gate#
250*7c478bd9Sstevel@tonic-gate# Top level function to find all the binaries to be checked.  Calls
251*7c478bd9Sstevel@tonic-gate# record_binary() to do the actual deciding and recording.
252*7c478bd9Sstevel@tonic-gate#
253*7c478bd9Sstevel@tonic-gate# The array @item_list contains all the items to find.
254*7c478bd9Sstevel@tonic-gate#
255*7c478bd9Sstevel@tonic-gatesub find_binaries
256*7c478bd9Sstevel@tonic-gate{
257*7c478bd9Sstevel@tonic-gate	$binary_count = 0;
258*7c478bd9Sstevel@tonic-gate
259*7c478bd9Sstevel@tonic-gate	my $skipped_file = "$working_dir/Skipped";
260*7c478bd9Sstevel@tonic-gate	my $skipped_fh = do { local *FH; *FH };
261*7c478bd9Sstevel@tonic-gate	open($skipped_fh, ">$skipped_file") ||
262*7c478bd9Sstevel@tonic-gate	    exiter(nofile($skipped_file, $!));
263*7c478bd9Sstevel@tonic-gate
264*7c478bd9Sstevel@tonic-gate	$skipped_count = 0;
265*7c478bd9Sstevel@tonic-gate
266*7c478bd9Sstevel@tonic-gate	my ($item, $args, $file);
267*7c478bd9Sstevel@tonic-gate	emsg("\n" .  gettext(
268*7c478bd9Sstevel@tonic-gate	    "finding executables and shared libraries to check") . " ...\n");
269*7c478bd9Sstevel@tonic-gate
270*7c478bd9Sstevel@tonic-gate	$args = '';
271*7c478bd9Sstevel@tonic-gate	$args .= '-follow ' unless ($do_not_follow_symlinks);
272*7c478bd9Sstevel@tonic-gate	$args .= '-type f -print';
273*7c478bd9Sstevel@tonic-gate
274*7c478bd9Sstevel@tonic-gate	my $quote_fmt = gettext(
275*7c478bd9Sstevel@tonic-gate	    "skipping:  item contains the single-quote character ': %s\n");
276*7c478bd9Sstevel@tonic-gate
277*7c478bd9Sstevel@tonic-gate	foreach $item (@item_list) {
278*7c478bd9Sstevel@tonic-gate		if (! -e $item) {
279*7c478bd9Sstevel@tonic-gate			emsg(gettext("skipping:  %s: %s\n"), $item, $!);
280*7c478bd9Sstevel@tonic-gate			print $skipped_fh "$item: no_exist\n";
281*7c478bd9Sstevel@tonic-gate			$skipped_count++;
282*7c478bd9Sstevel@tonic-gate			next;
283*7c478bd9Sstevel@tonic-gate		} elsif ($item =~ /'/)  {
284*7c478bd9Sstevel@tonic-gate			emsg($quote_fmt, $item);
285*7c478bd9Sstevel@tonic-gate			print $skipped_fh "$item: item_has_bad_char\n";
286*7c478bd9Sstevel@tonic-gate			$skipped_count++;
287*7c478bd9Sstevel@tonic-gate			next;
288*7c478bd9Sstevel@tonic-gate		}
289*7c478bd9Sstevel@tonic-gate		# note that $item does not contain a single-quote.
290*7c478bd9Sstevel@tonic-gate		my $find_fh = do { local *FH; *FH };
291*7c478bd9Sstevel@tonic-gate		open($find_fh, "$cmd_find '$item' $args|") ||
292*7c478bd9Sstevel@tonic-gate		    exiter(norunprog("$cmd_find '$item' $args", $!));
293*7c478bd9Sstevel@tonic-gate
294*7c478bd9Sstevel@tonic-gate		while (<$find_fh>) {
295*7c478bd9Sstevel@tonic-gate			chomp($file = $_);
296*7c478bd9Sstevel@tonic-gate			#
297*7c478bd9Sstevel@tonic-gate			# We are free to remove leading "./". This will
298*7c478bd9Sstevel@tonic-gate			# minimize directory names we create that would
299*7c478bd9Sstevel@tonic-gate			# start with a dot.
300*7c478bd9Sstevel@tonic-gate			#
301*7c478bd9Sstevel@tonic-gate			$file =~ s,^\./,,;
302*7c478bd9Sstevel@tonic-gate
303*7c478bd9Sstevel@tonic-gate			next if ($file eq '');
304*7c478bd9Sstevel@tonic-gate
305*7c478bd9Sstevel@tonic-gate			record_binary($file, $skipped_fh);
306*7c478bd9Sstevel@tonic-gate		}
307*7c478bd9Sstevel@tonic-gate		close($find_fh);
308*7c478bd9Sstevel@tonic-gate	}
309*7c478bd9Sstevel@tonic-gate
310*7c478bd9Sstevel@tonic-gate	if ($binary_count == 0) {
311*7c478bd9Sstevel@tonic-gate		exiter("$command_name: " . gettext(
312*7c478bd9Sstevel@tonic-gate		    "no checkable binary objects were found."), 3);
313*7c478bd9Sstevel@tonic-gate	}
314*7c478bd9Sstevel@tonic-gate
315*7c478bd9Sstevel@tonic-gate	if ($skipped_count == 0) {
316*7c478bd9Sstevel@tonic-gate		print $skipped_fh "# NO_FILES_WERE_SKIPPED\n";
317*7c478bd9Sstevel@tonic-gate	}
318*7c478bd9Sstevel@tonic-gate	close($skipped_fh);
319*7c478bd9Sstevel@tonic-gate}
320*7c478bd9Sstevel@tonic-gate
321*7c478bd9Sstevel@tonic-gate#
322*7c478bd9Sstevel@tonic-gate# This subroutine will determine if a binary is checkable.
323*7c478bd9Sstevel@tonic-gate#
324*7c478bd9Sstevel@tonic-gate# If so, it will reserve a directory for its output in the $working_dir
325*7c478bd9Sstevel@tonic-gate# location, and store the output of a number of commands there.
326*7c478bd9Sstevel@tonic-gate#
327*7c478bd9Sstevel@tonic-gatesub record_binary
328*7c478bd9Sstevel@tonic-gate{
329*7c478bd9Sstevel@tonic-gate	my ($file, $skipped_fh) = @_;
330*7c478bd9Sstevel@tonic-gate
331*7c478bd9Sstevel@tonic-gate	if ((++$record_binary_call_count % 500) == 0) {
332*7c478bd9Sstevel@tonic-gate		#
333*7c478bd9Sstevel@tonic-gate		# This indicates are being called many times for a large
334*7c478bd9Sstevel@tonic-gate		# product.  Clear out our caches.
335*7c478bd9Sstevel@tonic-gate		#
336*7c478bd9Sstevel@tonic-gate		purge_caches();
337*7c478bd9Sstevel@tonic-gate	}
338*7c478bd9Sstevel@tonic-gate
339*7c478bd9Sstevel@tonic-gate	#
340*7c478bd9Sstevel@tonic-gate	# Check if the object exists and is regular file.  Note that
341*7c478bd9Sstevel@tonic-gate	# this test also passes a symlink as long as that symlink
342*7c478bd9Sstevel@tonic-gate	# ultimately refers to a regular file.
343*7c478bd9Sstevel@tonic-gate	#
344*7c478bd9Sstevel@tonic-gate	if (! -f $file) {
345*7c478bd9Sstevel@tonic-gate		emsg(gettext("skipping:  not a file: %s\n"), $file);
346*7c478bd9Sstevel@tonic-gate		print $skipped_fh "$file: not_a_file\n";
347*7c478bd9Sstevel@tonic-gate		$skipped_count++;
348*7c478bd9Sstevel@tonic-gate		return 0;
349*7c478bd9Sstevel@tonic-gate	}
350*7c478bd9Sstevel@tonic-gate
351*7c478bd9Sstevel@tonic-gate	# Check if it is readable:
352*7c478bd9Sstevel@tonic-gate	if (! -r $file) {
353*7c478bd9Sstevel@tonic-gate		emsg(gettext("skipping:  cannot read: %s\n"), $file);
354*7c478bd9Sstevel@tonic-gate		print $skipped_fh "$file: unreadable\n";
355*7c478bd9Sstevel@tonic-gate		$skipped_count++;
356*7c478bd9Sstevel@tonic-gate		return 0;
357*7c478bd9Sstevel@tonic-gate	}
358*7c478bd9Sstevel@tonic-gate
359*7c478bd9Sstevel@tonic-gate	#
360*7c478bd9Sstevel@tonic-gate	# Since the filename will be used as operands passed to utility
361*7c478bd9Sstevel@tonic-gate	# commands via the shell, we exclude at the outset certain meta
362*7c478bd9Sstevel@tonic-gate	# characters in the filenames.
363*7c478bd9Sstevel@tonic-gate	#
364*7c478bd9Sstevel@tonic-gate	my $quote_fmt = gettext(
365*7c478bd9Sstevel@tonic-gate	    "skipping:  filename contains the single-quote character: ': %s\n");
366*7c478bd9Sstevel@tonic-gate	if ($file =~ /'/) {
367*7c478bd9Sstevel@tonic-gate		emsg($quote_fmt, $file);
368*7c478bd9Sstevel@tonic-gate		print $skipped_fh "$file: filename_has_bad_char\n";
369*7c478bd9Sstevel@tonic-gate		$skipped_count++;
370*7c478bd9Sstevel@tonic-gate		return 0;
371*7c478bd9Sstevel@tonic-gate	}
372*7c478bd9Sstevel@tonic-gate
373*7c478bd9Sstevel@tonic-gate	my $newline_fmt = gettext(
374*7c478bd9Sstevel@tonic-gate	    "skipping:  filename contains the newline character: \\n: %s\n");
375*7c478bd9Sstevel@tonic-gate	if ($file =~ /\n/) {
376*7c478bd9Sstevel@tonic-gate		emsg($newline_fmt, $file);
377*7c478bd9Sstevel@tonic-gate		print $skipped_fh "$file: filename_has_bad_char\n";
378*7c478bd9Sstevel@tonic-gate		$skipped_count++;
379*7c478bd9Sstevel@tonic-gate		return 0;
380*7c478bd9Sstevel@tonic-gate	}
381*7c478bd9Sstevel@tonic-gate
382*7c478bd9Sstevel@tonic-gate	my $pipe_fmt = gettext(
383*7c478bd9Sstevel@tonic-gate	    "skipping:  filename contains the pipe character: \|: %s\n");
384*7c478bd9Sstevel@tonic-gate	if ($file =~ /\|/) {
385*7c478bd9Sstevel@tonic-gate		emsg($pipe_fmt, $file);
386*7c478bd9Sstevel@tonic-gate		print $skipped_fh "$file: filename_has_bad_char\n";
387*7c478bd9Sstevel@tonic-gate		$skipped_count++;
388*7c478bd9Sstevel@tonic-gate		return 0;
389*7c478bd9Sstevel@tonic-gate	}
390*7c478bd9Sstevel@tonic-gate
391*7c478bd9Sstevel@tonic-gate	my $file_output;
392*7c478bd9Sstevel@tonic-gate
393*7c478bd9Sstevel@tonic-gate	# Run the file(1) command on it.
394*7c478bd9Sstevel@tonic-gate
395*7c478bd9Sstevel@tonic-gate	c_locale(1);
396*7c478bd9Sstevel@tonic-gate	# note that $file does not contain a single-quote.
397*7c478bd9Sstevel@tonic-gate	$file_output = `$cmd_file '$file' 2>/dev/null`;
398*7c478bd9Sstevel@tonic-gate	c_locale(0);
399*7c478bd9Sstevel@tonic-gate
400*7c478bd9Sstevel@tonic-gate	if ($file_output =~ /script$/) {
401*7c478bd9Sstevel@tonic-gate		$file_output =~ s/:\s+/: /;
402*7c478bd9Sstevel@tonic-gate		$file_output =~ s/: /: script /;
403*7c478bd9Sstevel@tonic-gate		print $skipped_fh "$file_output";
404*7c478bd9Sstevel@tonic-gate
405*7c478bd9Sstevel@tonic-gate		#
406*7c478bd9Sstevel@tonic-gate		# again now without the c_locale() setting:
407*7c478bd9Sstevel@tonic-gate		# note that $file does not contain a single-quote.
408*7c478bd9Sstevel@tonic-gate		#
409*7c478bd9Sstevel@tonic-gate		$file_output = `$cmd_file '$file' 2>/dev/null`;
410*7c478bd9Sstevel@tonic-gate		$file_output =~ s/:\s+/: /;
411*7c478bd9Sstevel@tonic-gate		emsg(gettext("skipping:  %s"), $file_output);
412*7c478bd9Sstevel@tonic-gate		$skipped_count++;
413*7c478bd9Sstevel@tonic-gate		return 0;
414*7c478bd9Sstevel@tonic-gate	}
415*7c478bd9Sstevel@tonic-gate
416*7c478bd9Sstevel@tonic-gate	# create ELF and a.out matching regex:
417*7c478bd9Sstevel@tonic-gate	my $object_match =
418*7c478bd9Sstevel@tonic-gate	    'ELF.*executable.*dynamically' . '|' .
419*7c478bd9Sstevel@tonic-gate	    'ELF.*dynamic lib' . '|' .
420*7c478bd9Sstevel@tonic-gate	    'ELF.*executable.*statically' . '|' .
421*7c478bd9Sstevel@tonic-gate	    'Sun demand paged SPARC.*dynamically linked' . '|' .
422*7c478bd9Sstevel@tonic-gate	    'Sun demand paged SPARC executable' . '|' .
423*7c478bd9Sstevel@tonic-gate	    'pure SPARC executable' . '|' .
424*7c478bd9Sstevel@tonic-gate	    'impure SPARC executable';
425*7c478bd9Sstevel@tonic-gate
426*7c478bd9Sstevel@tonic-gate	#
427*7c478bd9Sstevel@tonic-gate	# Note that we let the "statically linked" binaries through
428*7c478bd9Sstevel@tonic-gate	# here, but will catch them later in the profiler and checker.
429*7c478bd9Sstevel@tonic-gate	#
430*7c478bd9Sstevel@tonic-gate
431*7c478bd9Sstevel@tonic-gate	if ($file_output !~ /$object_match/io) {
432*7c478bd9Sstevel@tonic-gate		# it is not an ELF object file and so does not interest us.
433*7c478bd9Sstevel@tonic-gate		return 0;
434*7c478bd9Sstevel@tonic-gate	}
435*7c478bd9Sstevel@tonic-gate
436*7c478bd9Sstevel@tonic-gate	my $exec_fmt = gettext(
437*7c478bd9Sstevel@tonic-gate	    "skipping:  must have exec permission to be checked: %s\n");
438*7c478bd9Sstevel@tonic-gate	if (! -x $file) {
439*7c478bd9Sstevel@tonic-gate		#
440*7c478bd9Sstevel@tonic-gate		# It interests us, but the execute bit not set.  Shared
441*7c478bd9Sstevel@tonic-gate		# objects will be let through here since ldd will still
442*7c478bd9Sstevel@tonic-gate		# work on them (since it uses lddstub).  Otherwise, we
443*7c478bd9Sstevel@tonic-gate		# cannot check it.
444*7c478bd9Sstevel@tonic-gate		#
445*7c478bd9Sstevel@tonic-gate		if (! is_shared_object($file)) {
446*7c478bd9Sstevel@tonic-gate			# warn the user exec bit should be set:
447*7c478bd9Sstevel@tonic-gate			emsg($exec_fmt, $file);
448*7c478bd9Sstevel@tonic-gate			print $skipped_fh "$file: no_exec_permission\n";
449*7c478bd9Sstevel@tonic-gate			$skipped_count++;
450*7c478bd9Sstevel@tonic-gate			return 0;
451*7c478bd9Sstevel@tonic-gate		}
452*7c478bd9Sstevel@tonic-gate	}
453*7c478bd9Sstevel@tonic-gate
454*7c478bd9Sstevel@tonic-gate	#
455*7c478bd9Sstevel@tonic-gate	# Rather than let ldd fail later on in symprof, we check the
456*7c478bd9Sstevel@tonic-gate	# arch here to make sure it matches $uname_p.  If it does not
457*7c478bd9Sstevel@tonic-gate	# match, we anticipate a 64-bit application and so we
458*7c478bd9Sstevel@tonic-gate	# immediately test how ldd will handle it (kernel might be
459*7c478bd9Sstevel@tonic-gate	# 32-bit, etc).
460*7c478bd9Sstevel@tonic-gate	#
461*7c478bd9Sstevel@tonic-gate	my ($arch, $type, $wordsize, $endian, $e_machine) = bin_type($file);
462*7c478bd9Sstevel@tonic-gate
463*7c478bd9Sstevel@tonic-gate	if ($arch !~ /^${uname_p}$/io) {
464*7c478bd9Sstevel@tonic-gate		my ($ldd_output, $ldd_output2);
465*7c478bd9Sstevel@tonic-gate
466*7c478bd9Sstevel@tonic-gate		#
467*7c478bd9Sstevel@tonic-gate		# Now run ldd on it to see how things would go.  If it
468*7c478bd9Sstevel@tonic-gate		# fails we must skip it.
469*7c478bd9Sstevel@tonic-gate		#
470*7c478bd9Sstevel@tonic-gate		c_locale(1);
471*7c478bd9Sstevel@tonic-gate		# note that $file does not contain single-quote
472*7c478bd9Sstevel@tonic-gate		$ldd_output = `$cmd_ldd '$file' 2>&1 1>/dev/null`;
473*7c478bd9Sstevel@tonic-gate		c_locale(0);
474*7c478bd9Sstevel@tonic-gate		if ($? != 0) {
475*7c478bd9Sstevel@tonic-gate			# note that $file does not contain a single-quote
476*7c478bd9Sstevel@tonic-gate			$ldd_output2 = `$cmd_ldd '$file' 2>&1 1>/dev/null`;
477*7c478bd9Sstevel@tonic-gate			$ldd_output	=~ s/\n.*$//;
478*7c478bd9Sstevel@tonic-gate			$ldd_output2	=~ s/\n.*$//;
479*7c478bd9Sstevel@tonic-gate			if ($ldd_output !~ /wrong class/) {
480*7c478bd9Sstevel@tonic-gate				$ldd_output = "$file: " . sprintf(
481*7c478bd9Sstevel@tonic-gate				    gettext("ldd failed for arch: %s"), $arch);
482*7c478bd9Sstevel@tonic-gate				$ldd_output2 = $ldd_output;
483*7c478bd9Sstevel@tonic-gate			} else {
484*7c478bd9Sstevel@tonic-gate				$ldd_output	.= " ($arch)";
485*7c478bd9Sstevel@tonic-gate				$ldd_output2	.= " ($arch)";
486*7c478bd9Sstevel@tonic-gate			}
487*7c478bd9Sstevel@tonic-gate			$ldd_output	=~ s/:\s+/: /;
488*7c478bd9Sstevel@tonic-gate			$ldd_output2	=~ s/:\s+/: /;
489*7c478bd9Sstevel@tonic-gate			emsg(gettext("skipping:  %s\n"), $ldd_output2);
490*7c478bd9Sstevel@tonic-gate			$ldd_output =~ s/: /: ldd_failed /;
491*7c478bd9Sstevel@tonic-gate			print $skipped_fh "$ldd_output\n";
492*7c478bd9Sstevel@tonic-gate			$skipped_count++;
493*7c478bd9Sstevel@tonic-gate			return 0;
494*7c478bd9Sstevel@tonic-gate		}
495*7c478bd9Sstevel@tonic-gate	}
496*7c478bd9Sstevel@tonic-gate
497*7c478bd9Sstevel@tonic-gate	# From this point on, object is one we decided to check.
498*7c478bd9Sstevel@tonic-gate
499*7c478bd9Sstevel@tonic-gate	# Create the directory name for this object:
500*7c478bd9Sstevel@tonic-gate	my $dirname = object_to_dir_name($file);
501*7c478bd9Sstevel@tonic-gate	my $dirpath = "$working_dir/$dirname";
502*7c478bd9Sstevel@tonic-gate	my $early_fmt = gettext(
503*7c478bd9Sstevel@tonic-gate	    "skipping:  %s referenced earlier on the command line\n");
504*7c478bd9Sstevel@tonic-gate	if (-e $dirpath) {
505*7c478bd9Sstevel@tonic-gate		#
506*7c478bd9Sstevel@tonic-gate		# Directory already exists.  We assume this means the
507*7c478bd9Sstevel@tonic-gate		# user listed it twice (possibly indirectly via "find").
508*7c478bd9Sstevel@tonic-gate		#
509*7c478bd9Sstevel@tonic-gate		emsg($early_fmt, $file);
510*7c478bd9Sstevel@tonic-gate		return 0;
511*7c478bd9Sstevel@tonic-gate	}
512*7c478bd9Sstevel@tonic-gate
513*7c478bd9Sstevel@tonic-gate	if (! mkdir($dirpath, 0777)) {
514*7c478bd9Sstevel@tonic-gate		exiter(nocreatedir($dirpath, $!));
515*7c478bd9Sstevel@tonic-gate	}
516*7c478bd9Sstevel@tonic-gate
517*7c478bd9Sstevel@tonic-gate	$binary_count++;
518*7c478bd9Sstevel@tonic-gate
519*7c478bd9Sstevel@tonic-gate	# Record binary object's location:
520*7c478bd9Sstevel@tonic-gate	my $path_fh = do { local *FH; *FH };
521*7c478bd9Sstevel@tonic-gate	open($path_fh, ">$dirpath/info.path") ||
522*7c478bd9Sstevel@tonic-gate	    exiter(nofile("$dirpath/info.path", $!));
523*7c478bd9Sstevel@tonic-gate	print $path_fh $file, "\n";
524*7c478bd9Sstevel@tonic-gate	close($path_fh);
525*7c478bd9Sstevel@tonic-gate
526*7c478bd9Sstevel@tonic-gate	#
527*7c478bd9Sstevel@tonic-gate	# Record /usr/bin/file output.  Note that the programmatical way
528*7c478bd9Sstevel@tonic-gate	# to access this info is through the command cmd_output_file().
529*7c478bd9Sstevel@tonic-gate	#
530*7c478bd9Sstevel@tonic-gate	my $file_fh = do { local *FH; *FH };
531*7c478bd9Sstevel@tonic-gate	open($file_fh, ">$dirpath/info.file") ||
532*7c478bd9Sstevel@tonic-gate	    exiter(nofile("$dirpath/info.file", $!));
533*7c478bd9Sstevel@tonic-gate	print $file_fh $file_output;
534*7c478bd9Sstevel@tonic-gate	close($file_fh);
535*7c478bd9Sstevel@tonic-gate
536*7c478bd9Sstevel@tonic-gate	#
537*7c478bd9Sstevel@tonic-gate	# Record dump -Lv output.  Note that the programmatical way to
538*7c478bd9Sstevel@tonic-gate	# access this info is through the command cmd_output_dump().
539*7c478bd9Sstevel@tonic-gate	#
540*7c478bd9Sstevel@tonic-gate	my $dump_fh = do { local *FH; *FH };
541*7c478bd9Sstevel@tonic-gate	open($dump_fh, ">$dirpath/info.dump") ||
542*7c478bd9Sstevel@tonic-gate	    exiter(nofile("$dirpath/info.dump", $!));
543*7c478bd9Sstevel@tonic-gate
544*7c478bd9Sstevel@tonic-gate	my $dump_output;
545*7c478bd9Sstevel@tonic-gate	c_locale(1);
546*7c478bd9Sstevel@tonic-gate	# note that $file does not contain a single-quote
547*7c478bd9Sstevel@tonic-gate	$dump_output = `$cmd_dump -Lv '$file' 2>&1`;
548*7c478bd9Sstevel@tonic-gate	c_locale(0);
549*7c478bd9Sstevel@tonic-gate	print $dump_fh $dump_output;
550*7c478bd9Sstevel@tonic-gate	close($dump_fh);
551*7c478bd9Sstevel@tonic-gate
552*7c478bd9Sstevel@tonic-gate	#
553*7c478bd9Sstevel@tonic-gate	# Record arch and etc binary type.
554*7c478bd9Sstevel@tonic-gate	#
555*7c478bd9Sstevel@tonic-gate	my $arch_fh = do { local *FH; *FH };
556*7c478bd9Sstevel@tonic-gate	open($arch_fh, ">$dirpath/info.arch") ||
557*7c478bd9Sstevel@tonic-gate	    exiter(nofile("$dirpath/info.arch", $!));
558*7c478bd9Sstevel@tonic-gate
559*7c478bd9Sstevel@tonic-gate	if ($arch eq 'unknown') {
560*7c478bd9Sstevel@tonic-gate		my $tmp = $file_output;
561*7c478bd9Sstevel@tonic-gate		chomp($tmp);
562*7c478bd9Sstevel@tonic-gate		emsg(gettext("warning:   cannot determine arch: %s\n"), $tmp);
563*7c478bd9Sstevel@tonic-gate	}
564*7c478bd9Sstevel@tonic-gate
565*7c478bd9Sstevel@tonic-gate	print $arch_fh "ARCH: $arch\n";
566*7c478bd9Sstevel@tonic-gate	print $arch_fh "TYPE: $type\n";
567*7c478bd9Sstevel@tonic-gate	print $arch_fh "WORDSIZE: $wordsize\n";
568*7c478bd9Sstevel@tonic-gate	print $arch_fh "BYTEORDER: $endian\n";
569*7c478bd9Sstevel@tonic-gate	print $arch_fh "E_MACHINE: $e_machine\n";
570*7c478bd9Sstevel@tonic-gate	close($arch_fh);
571*7c478bd9Sstevel@tonic-gate
572*7c478bd9Sstevel@tonic-gate	# Record the file -> directory name mapping in the index file.
573*7c478bd9Sstevel@tonic-gate	my $index_file   = "$working_dir/Index";
574*7c478bd9Sstevel@tonic-gate	my $index_fh = do { local *FH; *FH };
575*7c478bd9Sstevel@tonic-gate	open($index_fh, ">>$index_file") ||
576*7c478bd9Sstevel@tonic-gate	    exiter(nofile($index_file, $!));
577*7c478bd9Sstevel@tonic-gate	print $index_fh "$file => $dirname\n";
578*7c478bd9Sstevel@tonic-gate	close($index_fh);
579*7c478bd9Sstevel@tonic-gate
580*7c478bd9Sstevel@tonic-gate	return 1;
581*7c478bd9Sstevel@tonic-gate}
582*7c478bd9Sstevel@tonic-gate
583*7c478bd9Sstevel@tonic-gate#
584*7c478bd9Sstevel@tonic-gate# Prints the usage statement to standard out.
585*7c478bd9Sstevel@tonic-gate#
586*7c478bd9Sstevel@tonic-gatesub show_usage
587*7c478bd9Sstevel@tonic-gate{
588*7c478bd9Sstevel@tonic-gate	emsg(gettext(
589*7c478bd9Sstevel@tonic-gate	"usage:	appcert [ -nBLS ] [ -f file ] [ -w dir ] { obj | dir } ...\n" .
590*7c478bd9Sstevel@tonic-gate	"	Examine binary object files for use of private Solaris\n" .
591*7c478bd9Sstevel@tonic-gate	"	interfaces, unstable use of static linking, and other\n" .
592*7c478bd9Sstevel@tonic-gate	"	unstable practices.\n")
593*7c478bd9Sstevel@tonic-gate	);
594*7c478bd9Sstevel@tonic-gate}
595*7c478bd9Sstevel@tonic-gate
596*7c478bd9Sstevel@tonic-gate#
597*7c478bd9Sstevel@tonic-gate# Examines the set of binaries to be checked and notes which ones are
598*7c478bd9Sstevel@tonic-gate# shared libraries. Constructs a LD_LIBRARY_PATH that would find ALL of
599*7c478bd9Sstevel@tonic-gate# these shared objects. The new directories are placed at the END of the
600*7c478bd9Sstevel@tonic-gate# current LD_LIBRARY_PATH (if any).
601*7c478bd9Sstevel@tonic-gate#
602*7c478bd9Sstevel@tonic-gatesub supplement_ld_library_path
603*7c478bd9Sstevel@tonic-gate{
604*7c478bd9Sstevel@tonic-gate	my (@orig, @add_product, @add_solaris, %ldpath);
605*7c478bd9Sstevel@tonic-gate
606*7c478bd9Sstevel@tonic-gate	# First, note the current LD_LIBRARY_PATH parts:
607*7c478bd9Sstevel@tonic-gate
608*7c478bd9Sstevel@tonic-gate	my $dirname;
609*7c478bd9Sstevel@tonic-gate	if (defined($ENV{'LD_LIBRARY_PATH'})) {
610*7c478bd9Sstevel@tonic-gate		foreach $dirname (split(/:/, $ENV{'LD_LIBRARY_PATH'})) {
611*7c478bd9Sstevel@tonic-gate			if (! exists($ldpath{$dirname})) {
612*7c478bd9Sstevel@tonic-gate				push(@orig, $dirname);
613*7c478bd9Sstevel@tonic-gate				$ldpath{$dirname} = 1;
614*7c478bd9Sstevel@tonic-gate			}
615*7c478bd9Sstevel@tonic-gate		}
616*7c478bd9Sstevel@tonic-gate	}
617*7c478bd9Sstevel@tonic-gate
618*7c478bd9Sstevel@tonic-gate	# Next, search for ELF shared objects.
619*7c478bd9Sstevel@tonic-gate	my ($dir, $path);
620*7c478bd9Sstevel@tonic-gate
621*7c478bd9Sstevel@tonic-gate	if ($modify_ld_path) {
622*7c478bd9Sstevel@tonic-gate		while (defined($dir = next_dir_name())) {
623*7c478bd9Sstevel@tonic-gate			$path = dir_name_to_path($dir);
624*7c478bd9Sstevel@tonic-gate
625*7c478bd9Sstevel@tonic-gate			$dirname = dirname($path);
626*7c478bd9Sstevel@tonic-gate			next if (exists($ldpath{$dirname}));
627*7c478bd9Sstevel@tonic-gate
628*7c478bd9Sstevel@tonic-gate			#
629*7c478bd9Sstevel@tonic-gate			# A colon ":" in directory name is cannot be
630*7c478bd9Sstevel@tonic-gate			# accepted because that is the LD_LIBRARY_PATH
631*7c478bd9Sstevel@tonic-gate			# separator.
632*7c478bd9Sstevel@tonic-gate			#
633*7c478bd9Sstevel@tonic-gate			next if ($dirname =~ /:/);
634*7c478bd9Sstevel@tonic-gate
635*7c478bd9Sstevel@tonic-gate			if (is_shared_object($path)) {
636*7c478bd9Sstevel@tonic-gate				if (! exists($ldpath{$dirname})) {
637*7c478bd9Sstevel@tonic-gate					push(@add_product, $dirname);
638*7c478bd9Sstevel@tonic-gate					$ldpath{$dirname} = 1;
639*7c478bd9Sstevel@tonic-gate				}
640*7c478bd9Sstevel@tonic-gate			}
641*7c478bd9Sstevel@tonic-gate		}
642*7c478bd9Sstevel@tonic-gate	}
643*7c478bd9Sstevel@tonic-gate
644*7c478bd9Sstevel@tonic-gate	if ($append_solaris_dirs_to_ld_path) {
645*7c478bd9Sstevel@tonic-gate		foreach $dirname (split(/:/, $solaris_library_ld_path)) {
646*7c478bd9Sstevel@tonic-gate			if (! exists($ldpath{$dirname})) {
647*7c478bd9Sstevel@tonic-gate				push(@add_solaris, $dirname);
648*7c478bd9Sstevel@tonic-gate				$ldpath{$dirname} = 1;
649*7c478bd9Sstevel@tonic-gate			}
650*7c478bd9Sstevel@tonic-gate		}
651*7c478bd9Sstevel@tonic-gate	}
652*7c478bd9Sstevel@tonic-gate
653*7c478bd9Sstevel@tonic-gate	# modify the LD_LIBRARY_PATH:
654*7c478bd9Sstevel@tonic-gate	if (@add_product || @add_solaris) {
655*7c478bd9Sstevel@tonic-gate		$ENV{'LD_LIBRARY_PATH'} =
656*7c478bd9Sstevel@tonic-gate		    join(':', (@orig, @add_product, @add_solaris));
657*7c478bd9Sstevel@tonic-gate	}
658*7c478bd9Sstevel@tonic-gate
659*7c478bd9Sstevel@tonic-gate	emsg("\n");
660*7c478bd9Sstevel@tonic-gate	if (@add_product) {
661*7c478bd9Sstevel@tonic-gate		emsg(gettext(
662*7c478bd9Sstevel@tonic-gate		    "Shared libraries were found in the application and the\n" .
663*7c478bd9Sstevel@tonic-gate		    "following directories are appended to LD_LIBRARY_PATH:\n"
664*7c478bd9Sstevel@tonic-gate		    ) . "\n");
665*7c478bd9Sstevel@tonic-gate
666*7c478bd9Sstevel@tonic-gate		foreach $dir (@add_product) {
667*7c478bd9Sstevel@tonic-gate			$dir = "./$dir" unless ($dir =~ m,^/,);
668*7c478bd9Sstevel@tonic-gate			emsg("   $dir\n");
669*7c478bd9Sstevel@tonic-gate		}
670*7c478bd9Sstevel@tonic-gate		emsg("\n");
671*7c478bd9Sstevel@tonic-gate	}
672*7c478bd9Sstevel@tonic-gate
673*7c478bd9Sstevel@tonic-gate	if (@add_solaris) {
674*7c478bd9Sstevel@tonic-gate		emsg(gettext(
675*7c478bd9Sstevel@tonic-gate		    "These Solaris library directories are being appended\n" .
676*7c478bd9Sstevel@tonic-gate		    "to LD_LIBRARY_PATH:\n") . "\n");
677*7c478bd9Sstevel@tonic-gate
678*7c478bd9Sstevel@tonic-gate		foreach $dir (@add_solaris) {
679*7c478bd9Sstevel@tonic-gate			emsg("   $dir\n");
680*7c478bd9Sstevel@tonic-gate		}
681*7c478bd9Sstevel@tonic-gate		emsg("\n");
682*7c478bd9Sstevel@tonic-gate	}
683*7c478bd9Sstevel@tonic-gate}
684*7c478bd9Sstevel@tonic-gate
685*7c478bd9Sstevel@tonic-gate#
686*7c478bd9Sstevel@tonic-gate# Everything is correctly exported by now, and so we just run "symprof".
687*7c478bd9Sstevel@tonic-gate# It is run in batches of $block_size binaries to minimize the effect of
688*7c478bd9Sstevel@tonic-gate# memory usage caused by huge binaries in the product to be checked.
689*7c478bd9Sstevel@tonic-gate#
690*7c478bd9Sstevel@tonic-gatesub run_profiler
691*7c478bd9Sstevel@tonic-gate{
692*7c478bd9Sstevel@tonic-gate	my $block_size = 20;
693*7c478bd9Sstevel@tonic-gate
694*7c478bd9Sstevel@tonic-gate	my $i = 0;
695*7c478bd9Sstevel@tonic-gate
696*7c478bd9Sstevel@tonic-gate	# record old values of the blocks (if any)
697*7c478bd9Sstevel@tonic-gate	my $env_min = $ENV{'AC_BLOCK_MIN'};
698*7c478bd9Sstevel@tonic-gate	my $env_max = $ENV{'AC_BLOCK_MAX'};
699*7c478bd9Sstevel@tonic-gate
700*7c478bd9Sstevel@tonic-gate	while ($i < $binary_count) { # do each block
701*7c478bd9Sstevel@tonic-gate		# export our symprof values of the block limits
702*7c478bd9Sstevel@tonic-gate		$ENV{'AC_BLOCK_MIN'} = $i;
703*7c478bd9Sstevel@tonic-gate		$ENV{'AC_BLOCK_MAX'} = $i + $block_size;
704*7c478bd9Sstevel@tonic-gate
705*7c478bd9Sstevel@tonic-gate		run_symprof();
706*7c478bd9Sstevel@tonic-gate
707*7c478bd9Sstevel@tonic-gate		$i += $block_size;
708*7c478bd9Sstevel@tonic-gate	}
709*7c478bd9Sstevel@tonic-gate
710*7c478bd9Sstevel@tonic-gate	# restore old values of the blocks (if any)
711*7c478bd9Sstevel@tonic-gate	if (defined($env_min)) {
712*7c478bd9Sstevel@tonic-gate		$ENV{'AC_BLOCK_MIN'} = $env_min;
713*7c478bd9Sstevel@tonic-gate	} else {
714*7c478bd9Sstevel@tonic-gate		delete $ENV{'AC_BLOCK_MIN'};
715*7c478bd9Sstevel@tonic-gate	}
716*7c478bd9Sstevel@tonic-gate	if (defined($env_max)) {
717*7c478bd9Sstevel@tonic-gate		$ENV{'AC_BLOCK_MAX'} = $env_max;
718*7c478bd9Sstevel@tonic-gate	} else {
719*7c478bd9Sstevel@tonic-gate		delete $ENV{'AC_BLOCK_MAX'};
720*7c478bd9Sstevel@tonic-gate	}
721*7c478bd9Sstevel@tonic-gate}
722*7c478bd9Sstevel@tonic-gate
723*7c478bd9Sstevel@tonic-gate#
724*7c478bd9Sstevel@tonic-gate# Sub that actually runs "symprof".
725*7c478bd9Sstevel@tonic-gate#
726*7c478bd9Sstevel@tonic-gatesub run_symprof
727*7c478bd9Sstevel@tonic-gate{
728*7c478bd9Sstevel@tonic-gate	system("$appcert_lib_dir/symprof");
729*7c478bd9Sstevel@tonic-gate	if ($? != 0) {
730*7c478bd9Sstevel@tonic-gate		emsg("%s", utilityfailed("symprof"));
731*7c478bd9Sstevel@tonic-gate		clean_up_exit(1);
732*7c478bd9Sstevel@tonic-gate	}
733*7c478bd9Sstevel@tonic-gate}
734*7c478bd9Sstevel@tonic-gate
735*7c478bd9Sstevel@tonic-gate#
736*7c478bd9Sstevel@tonic-gate# Sub to run "symcheck".
737*7c478bd9Sstevel@tonic-gate#
738*7c478bd9Sstevel@tonic-gatesub run_checker
739*7c478bd9Sstevel@tonic-gate{
740*7c478bd9Sstevel@tonic-gate	system("$appcert_lib_dir/symcheck");
741*7c478bd9Sstevel@tonic-gate	if ($? != 0) {
742*7c478bd9Sstevel@tonic-gate		emsg("%s", utilityfailed("symcheck"));
743*7c478bd9Sstevel@tonic-gate		clean_up_exit(1);
744*7c478bd9Sstevel@tonic-gate	}
745*7c478bd9Sstevel@tonic-gate}
746*7c478bd9Sstevel@tonic-gate
747*7c478bd9Sstevel@tonic-gate#
748*7c478bd9Sstevel@tonic-gate# Sub to run "symreport".
749*7c478bd9Sstevel@tonic-gate#
750*7c478bd9Sstevel@tonic-gatesub run_report_generator
751*7c478bd9Sstevel@tonic-gate{
752*7c478bd9Sstevel@tonic-gate	system("$appcert_lib_dir/symreport");
753*7c478bd9Sstevel@tonic-gate	if ($? != 0) {
754*7c478bd9Sstevel@tonic-gate		emsg("%s", utilityfailed("symreport"));
755*7c478bd9Sstevel@tonic-gate		clean_up_exit(1);
756*7c478bd9Sstevel@tonic-gate	}
757*7c478bd9Sstevel@tonic-gate}
758*7c478bd9Sstevel@tonic-gate
759*7c478bd9Sstevel@tonic-gate#
760*7c478bd9Sstevel@tonic-gate# General routine to be called if one of our utility programs (symprof,
761*7c478bd9Sstevel@tonic-gate# symcheck, symreport) failed (that is, return != 0).  returns the
762*7c478bd9Sstevel@tonic-gate# formatted error message string to pass to the user.
763*7c478bd9Sstevel@tonic-gate#
764*7c478bd9Sstevel@tonic-gatesub utilityfailed
765*7c478bd9Sstevel@tonic-gate{
766*7c478bd9Sstevel@tonic-gate	my ($prog) = @_;
767*7c478bd9Sstevel@tonic-gate	my $fmt;
768*7c478bd9Sstevel@tonic-gate	$fmt = "\n *** " . gettext("utility program failed: %s\n");
769*7c478bd9Sstevel@tonic-gate	return sprintf($fmt, $prog);
770*7c478bd9Sstevel@tonic-gate}
771*7c478bd9Sstevel@tonic-gate
772*7c478bd9Sstevel@tonic-gate#
773*7c478bd9Sstevel@tonic-gate# Does the cleanup and then exits with return code $rc.  The utility
774*7c478bd9Sstevel@tonic-gate# subroutine exiter() will call this subroutine.  No general cleanup is
775*7c478bd9Sstevel@tonic-gate# performed if exiting with error ($rc > 0) so that the user can examine
776*7c478bd9Sstevel@tonic-gate# at the output files, etc.
777*7c478bd9Sstevel@tonic-gate#
778*7c478bd9Sstevel@tonic-gatesub clean_up_exit
779*7c478bd9Sstevel@tonic-gate{
780*7c478bd9Sstevel@tonic-gate	my ($rc) = @_;
781*7c478bd9Sstevel@tonic-gate
782*7c478bd9Sstevel@tonic-gate	if ($rc != 0) {
783*7c478bd9Sstevel@tonic-gate		working_dir_msg();
784*7c478bd9Sstevel@tonic-gate	} else {
785*7c478bd9Sstevel@tonic-gate		clean_up();
786*7c478bd9Sstevel@tonic-gate	}
787*7c478bd9Sstevel@tonic-gate
788*7c478bd9Sstevel@tonic-gate	exit $rc;
789*7c478bd9Sstevel@tonic-gate}
790*7c478bd9Sstevel@tonic-gate
791*7c478bd9Sstevel@tonic-gate#
792*7c478bd9Sstevel@tonic-gate# General cleanup routine.
793*7c478bd9Sstevel@tonic-gate#
794*7c478bd9Sstevel@tonic-gatesub clean_up
795*7c478bd9Sstevel@tonic-gate{
796*7c478bd9Sstevel@tonic-gate	if (-d $tmp_dir && ($tmp_dir !~ m,^/+$,)) {
797*7c478bd9Sstevel@tonic-gate		rmdir($tmp_dir);
798*7c478bd9Sstevel@tonic-gate	}
799*7c478bd9Sstevel@tonic-gate}
800*7c478bd9Sstevel@tonic-gate
801*7c478bd9Sstevel@tonic-gate#
802*7c478bd9Sstevel@tonic-gate# Routine that is called when an error has occurred.  It indicates to
803*7c478bd9Sstevel@tonic-gate# user where the working and/or temporary directory is and that they are
804*7c478bd9Sstevel@tonic-gate# not being removed.
805*7c478bd9Sstevel@tonic-gate#
806*7c478bd9Sstevel@tonic-gatesub working_dir_msg
807*7c478bd9Sstevel@tonic-gate{
808*7c478bd9Sstevel@tonic-gate
809*7c478bd9Sstevel@tonic-gate	my @dirlist;
810*7c478bd9Sstevel@tonic-gate	emsg("\n");
811*7c478bd9Sstevel@tonic-gate	if (defined($working_dir) && -d $working_dir) {
812*7c478bd9Sstevel@tonic-gate		push(@dirlist, $working_dir);
813*7c478bd9Sstevel@tonic-gate	}
814*7c478bd9Sstevel@tonic-gate	if (defined($tmp_dir) && -d $tmp_dir) {
815*7c478bd9Sstevel@tonic-gate		push(@dirlist, $tmp_dir);
816*7c478bd9Sstevel@tonic-gate	}
817*7c478bd9Sstevel@tonic-gate
818*7c478bd9Sstevel@tonic-gate	return if (! @dirlist);
819*7c478bd9Sstevel@tonic-gate
820*7c478bd9Sstevel@tonic-gate	emsg(gettext(
821*7c478bd9Sstevel@tonic-gate	    "Note that the temporary working directories still exist:") .
822*7c478bd9Sstevel@tonic-gate	    "\n\n");
823*7c478bd9Sstevel@tonic-gate
824*7c478bd9Sstevel@tonic-gate	my $dir;
825*7c478bd9Sstevel@tonic-gate	# show the user explicitly which directories remains:
826*7c478bd9Sstevel@tonic-gate	foreach $dir (@dirlist) {
827*7c478bd9Sstevel@tonic-gate		system($cmd_ls, '-ld', $dir);
828*7c478bd9Sstevel@tonic-gate	}
829*7c478bd9Sstevel@tonic-gate
830*7c478bd9Sstevel@tonic-gate	emsg("\n");
831*7c478bd9Sstevel@tonic-gate}
832*7c478bd9Sstevel@tonic-gate
833*7c478bd9Sstevel@tonic-gate#
834*7c478bd9Sstevel@tonic-gate# Signal handler for interruptions (E.g. Ctrl-C SIGINT).
835*7c478bd9Sstevel@tonic-gate#
836*7c478bd9Sstevel@tonic-gatesub interrupted
837*7c478bd9Sstevel@tonic-gate{
838*7c478bd9Sstevel@tonic-gate	$SIG{$_[0]} = 'IGNORE';
839*7c478bd9Sstevel@tonic-gate
840*7c478bd9Sstevel@tonic-gate	exit 1 if ($caught_signal);
841*7c478bd9Sstevel@tonic-gate	$caught_signal = 1;
842*7c478bd9Sstevel@tonic-gate
843*7c478bd9Sstevel@tonic-gate	signals('off');
844*7c478bd9Sstevel@tonic-gate	emsg("\n** " . gettext("interrupted") . " **\n");
845*7c478bd9Sstevel@tonic-gate
846*7c478bd9Sstevel@tonic-gate	clean_up_exit(1);
847*7c478bd9Sstevel@tonic-gate}
848