xref: /onnv-gate/usr/src/cmd/abi/appcert/scripts/AppcertUtil.pm (revision 0:68f95e015346)
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