diff options
Diffstat (limited to 'sys-apps/yard/files/yard-2.2/sbin')
-rw-r--r-- | sys-apps/yard/files/yard-2.2/sbin/check_root_fs | 605 | ||||
-rw-r--r-- | sys-apps/yard/files/yard-2.2/sbin/make_root_fs | 829 | ||||
-rw-r--r-- | sys-apps/yard/files/yard-2.2/sbin/mklibs.sh | 863 | ||||
-rw-r--r-- | sys-apps/yard/files/yard-2.2/sbin/reduce_libs_root_fs | 8 | ||||
-rw-r--r-- | sys-apps/yard/files/yard-2.2/sbin/write_rescue_disk | 566 |
5 files changed, 2871 insertions, 0 deletions
diff --git a/sys-apps/yard/files/yard-2.2/sbin/check_root_fs b/sys-apps/yard/files/yard-2.2/sbin/check_root_fs new file mode 100644 index 000000000000..f4e89900bf54 --- /dev/null +++ b/sys-apps/yard/files/yard-2.2/sbin/check_root_fs @@ -0,0 +1,605 @@ +#! /usr/bin/perl +# -*- Mode: Perl -*- +# This script created automatically from scripts/check_root_fs.in +# $Header: /var/cvsroot/gentoo-x86/sys-apps/yard/files/yard-2.2/sbin/check_root_fs,v 1.1 2002/08/25 20:25:33 aliz Exp $ +############################################################################## +## +## CHECK_ROOT_FS +## Copyright (C) 1996,1997,1998 Tom Fawcett (fawcett@croftj.net) +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +## +## +############################################################################## +use strict; +use File::Basename; +use File::Path; +use FileHandle; +use English; +use lib "/etc/yard", "/usr/lib/yard"; +use yardconfig; +use File::Find; + +BEGIN { require "yard_utils.pl" } +require "Config.pl"; + +### GLOBAL VARIABLES +my(%Termcap); # Defs from /etc/termcap +my($checked_for_getty_files); # Scalar -- have we checked getty files yet? +my(%checked); # Hash table of files we've already checked +# This is a little crude. Technically we should read /etc/conf.getty +# to make sure we're not supposed to be using a different login binary. +my($login_binary) = "$CFG::mount_point/bin/login"; + + +STDOUT->autoflush(1); + +start_logging_output(); +info 0, "check_root_fs 2.0\n"; + +mount_device_if_necessary(); + +# This goes first so we define %Termcap for use in children +check_termcap(); + +##### Here are the tests. +fork_chroot_and(\&check_fstab); +fork_chroot_and(\&check_inittab); +fork_chroot_and(\&check_scripts); +check_links(); +check_passwd(); +check_pam(); +check_nss(); + +info 0, "All done.\n"; +info 0, "If this is acceptable, continue with write_rescue_disk\n"; +exit; + + +############################################################################## +sub warning { + info 0, "\n", @_; +# $::Warnings++; +} + + +# This takes a procedure call, forks off a subprocess, chroots to +# $CFG::mount_point and runs the procedure. +sub fork_chroot_and { + my($call) = @_; + + my($Godot) = fork; + die "Can't fork: $!" unless defined $Godot; + + if (!$Godot) { + # Child process + chdir($CFG::mount_point); + chroot($CFG::mount_point); ##### chroot to the root filesystem + &$call; + exit; + + } else { + # Parent here + waitpid($Godot, 0); + } +} + + +sub check_fstab { + my($FSTAB) = "/etc/fstab"; + my($proc_seen); + + open(FSTAB, "<$FSTAB") or error "$FSTAB: $!"; + info 0, "\nChecking $FSTAB\n"; + + while (<FSTAB>) { + chomp; + next if /^\#/ or /^\s*$/; + + my($dev, $mp, $type, $opts) = split; + next if $mp eq 'none' or $type eq 'swap'; + next if $dev eq 'none'; + + if (!-e $mp) { + info 0, "$FSTAB($.): $_\n\tCreating $mp on root filesystem\n"; + mkpath($mp); + } + + if ($dev !~ /:/ and !-e $dev) { + warning "$FSTAB($.): $_\n\tDevice $dev does not exist " + . "on root filesystem\n"; + } + + ##### If you use the file created by create_fstab, these tests + ##### are superfluous. + + if ($dev =~ m|^/dev/hd| and $opts !~ /noauto/) { + warning "\t($.): You probably should include \"noauto\" option\n", + "\tin the fstab entry of a hard disk. When the rescue floppy\n", + "\tboots, the \"mount -a\" will try to mount $dev\n"; + + } elsif ($dev eq $CFG::floppy and $type ne 'ext2' and $type ne 'auto') { + warning "\t($.): You've declared your floppy drive $CFG::floppy", + " to hold\n", + "\ta $type filesystem, which is not ext2. The rescue floppy\n", + "\tis ext2, which may confuse 'mount -a' during boot.\n"; + + } elsif ($type eq 'proc') { + $proc_seen = 1; + + } + } + close(FSTAB); + warning "\tNo /proc filesystem defined.\n" unless $proc_seen; + info 0, "Done with $FSTAB\n"; +} + + +sub check_inittab { + my($INITTAB) = "/etc/inittab"; + info 0, "\nChecking $INITTAB\n"; + + if (!open(INITTAB, "<$INITTAB")) { + warning "$INITTAB: $!\n"; + return + } + + my($default_rl, $saw_line_for_default_rl); + + while (<INITTAB>) { + chomp; + my($line) = $_; # Copy for errors + s/\#.*$//; # Delete comments + next if /^\s*$/; # Skip empty lines + + my($code, $runlevels, $action, $command) = split(':'); + + if ($action eq 'initdefault') { ##### The initdefault runlevel + $default_rl = $runlevels; + next; + } + if ($runlevels =~ /$default_rl/) { + $saw_line_for_default_rl = 1; + } + if ($command) { + my($exec, @args) = split(' ', $command); + + if (!-f $exec) { + warning "$INITTAB($.): $line\n", + "\t$exec: non-existent or non-executable\n"; + + } elsif (!-x $exec) { + info 0, "$INITTAB($.): $line\n", + info 0, "\tMaking $exec executable\n"; + chmod(0777, $exec) or error "chmod failed: $!"; + + } else { + ##### executable but not binary ==> script + scan_command_file($exec, @args) if !-B $exec; + } + + if ($exec =~ m|getty|) { # matches *getty* call + check_getty_type_call($exec, @args); + } + } + } + close(INITTAB) or error "close(INITTAB): $!"; + + if (!$saw_line_for_default_rl) { + warning "\tDefault runlevel is $default_rl, but no entry for it.\n"; + } + info 0, "Done with $INITTAB\n"; +} + + +##### This could be made much more complete, but for typical rc type +##### files it seems to catch the common problems. +sub scan_command_file { + my($cmdfile, @args) = @_; + my(%warned, $line); + + return if $checked{$cmdfile}; + info 0, "\nScanning $cmdfile\n"; + open(CMDFILE, "<$cmdfile") or error "$cmdfile: $!"; + + while ($line = <CMDFILE>) { + chomp($line); + next if $line =~ /^\#/ or /^\s*$/; + + next if $line =~ /^\w+=/; + + while ($line =~ m!(/(usr|var|bin|sbin|etc|dev)/\S+)(\s|$)!g) { + my($abs_file) = $1; + # next if $abs_file =~ m/[*?]/; # Skip meta chars - we don't trust glob + next if $warned{$abs_file}; # Only warn once per file + if (!-e $abs_file) { + warning "$cmdfile($.): $line\n\t$1: missing on root filesystem\n"; + $warned{$abs_file} = 1; + } + } + } + close(CMDFILE) or error "close($cmdfile): $!"; + + $checked{$cmdfile} = 1; + info 0, "Done scanning $cmdfile\n"; +} + + +##### Check_passwd is NOT run under chroot. +sub check_passwd { + my($passwd_file) = "$CFG::mount_point/etc/passwd"; + open(PASSWD, "<$passwd_file") or error "Can't read passwd file: $!\n"; + info 0, "\nChecking passwd file $passwd_file\n"; + + while (<PASSWD>) { + chomp; + next if /^\s*$/; # Skip blank/empty lines + my($line) = $_; + my($login_name, $passwd, $UID, $GID, $user_name, $home, $shell) = + split(':'); + + next if $passwd eq "*"; # Skip warnings if user can't login + + -d ($CFG::mount_point . $home) or + warning "$passwd_file($.): $line\n", + "\tHome directory of $login_name ($CFG::mount_point$home) is missing\n"; + -e ($CFG::mount_point . $shell) or + warning "$passwd_file($.): $line\n", + "\tShell of $login_name ($CFG::mount_point$shell) doesn't exist\n"; + + check_init_files($login_name, $home, $shell); + } + close(PASSWD); + info 0, "Done checking $passwd_file\n"; +} + + +##### Simple PAM configuration checks. +##### Tests whether PAM is needed, and whether the configuration libraries exist. +##### Check_pam is NOT run under chroot. +sub check_pam { + my($pam_configured) = 0; # Have we seen some pam config file yet? + info 0, "Checking for PAM\n"; + + my($pamd_dir) = "$CFG::mount_point/etc/pam.d"; + my($pam_conf) = "$CFG::mount_point/etc/pam.conf"; + + if (-e $pam_conf) { + info 0, "Checking $pam_conf\n"; + $pam_configured = 1; + open(PAM, $pam_conf) or error "Can't open pam.conf: $!\n"; + while (<PAM>) { + chomp; + next if /^\#/ or /^\s*$/; # Skip comments and empty lines + my($file) = (split)[3]; # Get fourth field + if (!-e "$CFG::mount_point/$file") { + warning "$pam_conf($.): $_\n", + "\tLibrary $file does not exist on root fs\n"; + } + # That's all we check for now + } + close(PAM) or die "Closing PAM: $!"; + info 0, "Done with $pam_conf\n"; + } + + + if (-e $pamd_dir) { + info 0, "Checking files in $pamd_dir\n"; + opendir(PAMD, $pamd_dir) or error "Can't open $pamd_dir: $!"; + my($file); + while (defined($file = readdir(PAMD))) { + my($file2) = "$pamd_dir/$file"; + next unless -f $file2; # Skip directories, etc. + open(PF, $file2) or error "$file2: $!"; + while (<PF>) { + chomp; + next if /^\#/ or /^\s*$/; # Skip comments and empty lines + my($file) = (split)[3]; # Get fourth field + $pam_configured = 1; + if (!-e "$CFG::mount_point/$file") { + warning "$file2($.): $_\n", + "\tLibrary $file does not exist on root fs\n"; + } + } + close(PF); + } + closedir(PAMD); + } + + # Finally, see whether PAM configuration is needed + if (!$pam_configured and -e $login_binary) { + my($dependencies) = scalar(`ldd $login_binary`); + if (defined($dependencies) and $dependencies =~ /libpam/) { + warning "Warning: login ($login_binary) needs PAM, but you haven't\n", + "\tconfigured it (in /etc/pam.conf or /etc/pam.d/)\n", + "\tYou probably won't be able to login.\n"; + } + } + info 0, "Done with PAM\n"; +} + + + +##### Basic checks for nsswitch.conf. +##### check_nss is NOT run under chroot. +##### From the nsswitch.conf(5) manpage: +##### For glibc, you must have a file called /lib/libnss_SERVICE.so.X for +##### every SERVICE you are using. On a standard installation, you could +##### use `files', `db', `nis' and `nisplus'. For hosts, you could specify +##### `dns' as extra service, for passwd, group and shadow `compat'. These +##### services will not be used by libc5 with NYS. The version number X +##### is 1 for glibc 2.0 and 2 for glibc 2.1. + +sub check_nss { + my($nss_conf) = "$CFG::mount_point/etc/nsswitch.conf"; + info 0, "Checking for NSS\n"; + + my($libc) = yard_glob("$CFG::mount_point/lib/libc-2*"); + my($libc_version) = $libc =~ m|/lib/libc-2.(\d)|; + if (!defined($libc_version)) { + warning "Can't determine your libc version\n"; + } else { + info 0, "You're using $libc\n"; + } + my($X) = $libc_version; + + if (-e $nss_conf) { + open(NSS, "<$nss_conf") or die "open($nss_conf): $!"; + + my($line); + while (defined($line = <NSS>)) { + chomp $line; + next if $line =~ /^\#/; + next if $line =~ /^\s*$/; + my($db, $entries) = $line =~ m/^(\w+):\s*(.+)$/; + # Remove bracketed expressions (action specifiers) + $entries =~ s/\[[^\]]*\]//g; + my(@entries) = split(' ', $entries); + my($entry); + for $entry (@entries) { + next if $entry =~ /^\[/; # ignore action specifiers + my($lib) = "$CFG::mount_point/lib/libnss_${entry}.so.${X}"; + if (!-e $lib) { + warning "$nss_conf($.):\n$line\n", + "\tRoot filesystem needs $lib to support $entry\n"; + } + } + } + + } else { + # No nsswitch.conf is present, figure out if maybe there should be one. + if (-e $login_binary) { + my($dependencies) = scalar(`ldd $login_binary`); + my($libc_version) = ($dependencies =~ /libc\.so\.(\d+)/m); + if ($libc_version > 5) { + # Needs libc 6 or greater + warning "Warning: $login_binary on rescue disk needs libc.so.$libc_version,\n" + . "\tbut there is no NSS configuration file ($nss_conf)\n" + . "\ton root filesystem.\n"; + } + } + } + info 0, "Done with NSS\n"; +} + + + +sub check_links { + info 0, "\nChecking links relative to $CFG::mount_point\n"; + + sub wanted { + if (-l $File::Find::name) { + local($::raw_link) = readlink($File::Find::name); + local($::target) = make_link_absolute($File::Find::name, $::raw_link); + + # I added this next test for /dev/stdout link hair. + # This really should be more complicated to handle link chains, + # but as a hack this works for three. + if (onto_proc_filesystem($File::Find::name)) { + + } elsif (-l $::target) { + chase_link($::target, 16); + + } elsif (!-e $::target) { + warning "Warning: Unresolved link: $File::Find::name -> $::raw_link\n"; + } + } + }; + + finddepth(\&wanted, $CFG::mount_point); +} + + +sub chase_link { + my($file, $link_depth) = @_; + + if ($link_depth == 0) { + warning "Warning: Probable link circularity involving $file\n"; + + } elsif (-l $file) { + chase_link(make_link_absolute($file, readlink($file)), + $link_depth-1); + } +} + + +sub check_scripts { + info 0, "\nChecking script interpreters\n"; + local($::prog); + + sub check_interpreter { + if (-x $File::Find::name and -f _ and -T _) { + open(SCRIPT, $File::Find::name) or error "$File::Find::name: $!"; + my($prog, $firstline); + chomp($firstline = <SCRIPT>); + if (($prog) = $firstline =~ /^\#!\s*(\S+)/) { + if (!-e $prog) { + warning "Warning: $File::Find::name needs $prog, which is missing\n"; + } elsif (!-x $prog) { + warning "Warning: $File::Find::name needs $prog, " . + "which is not executable.\n"; + } + } + close(SCRIPT); + } + }; # End of sub check_interpreter + + find(\&check_interpreter, "/"); +} + +sub check_getty_type_call { + my($prog, @args) = @_; + + if ($prog eq 'getty') { + my($tty, $speed, $type) = @args; + + if (!-e "$CFG::mount_point/dev/$tty") { + warning "\tLine $.: $prog for $tty, but /dev/$tty doesn't exist.\n"; + } + if (!defined($Termcap{$type})) { + warning "\tLine $.: Type $type not defined in termcap\n"; + } + } + ## If getty or getty_ps, look for /etc/gettydefs, /etc/issue + ## Check that term type matches one in termcap db. + + if ($prog =~ /^getty/) { + if (!$checked_for_getty_files) { + warning "\tLine $.: $prog expects /etc/gettydefs, which is missing.\n" + unless -e "$CFG::mount_point/etc/gettydefs"; + warning "\tLine $.: $prog expects /etc/issue, which is missing.\n" + unless -e "$CFG::mount_point/etc/issue"; + $checked_for_getty_files = 1; + } + } +} + + +### +### NB. This is *not* run under chroot +### +sub check_init_files { + my($user, $home, $shell) = @_; + + info 0, "Checking init files of $user (homedir= $home)\n"; + + my($shellname) = basename($shell); + my @init_files; + + ##### Try to infer the list of init files to be run for the shell + ##### of this user. Order is somewhat important here because of + ##### the search path. + + if ($shellname =~ /^(bash|sh)$/) { + @init_files = ("/etc/profile", "/etc/bashrc", + "$home/.profile", "$home/.bash_login", "$home/.bashrc", + "$home/.shrc"); + + } elsif ($shellname eq "ash") { + @init_files = ("/etc/profile", "$home/.profile"); + + } elsif ($shellname =~ /^(tcsh|csh)$/) { + @init_files = ("/etc/csh.cshrc", "/etc/.cshrc", "/etc/csh.login", + "$home/.cshrc", "$home/.tcshrc", "$home/.login"); + } + + ##### The path to be searched. This may be error prone. + my(@path) = (); + my($init_file); + + foreach $init_file (@init_files) { + $init_file = $CFG::mount_point . $init_file; + + next if $checked{$init_file} or !-r $init_file; + + info 0, "Checking $init_file\n"; + + open(INITF, "<$init_file") or die "$init_file: $!"; + + while (<INITF>) { + chomp; + next if /^\#/ or /^\s*$/; # Skip comments, whitespace + + my($var, $val); + if (($var, $val) = /^\s*(\w+)\s*=\s*(.*)\s*$/) { # Variable assignment + ##### Look for PATH assignment + if ($var eq "PATH") { + $val =~ s/^[\"\'](.*)[\"\']$/$1/; # Strip quotes + @path = split(':', $val); + info 1, "Using PATH: ", join(':', @path), "\n"; + } else { + next; # Skip other assignments + } + } + + my($cmd, $hd_abs); + + ##### Check for commands that aren't present + ($cmd) = /^(\w+)\b/; # Pick up cmd name + if ($cmd and ($hd_abs = find_file_in_path($cmd, @path))) { + # If it's here, see if it's on the rescue disk + if (!(-e "$CFG::mount_point/$hd_abs" and -x _)) { + warning "$init_file($.): $_\n\t\t$cmd looks like a command but\n", + "\t\tdoes not exist on the root filesystem.\n"; + } + } + + # Check for commands in backticks that aren't present + ($cmd) = /\`(\w+)\b/; + if ($cmd and ($hd_abs=find_file_in_path($cmd))) { + # If it's here, see if it's on the rescue disk + # Note that this could mislead if the user moved it to a different + # dir on the root fs. + if (!-e "$CFG::mount_point/$hd_abs") { + warning "${init_file}($.): $_\n\t$cmd: missing from root fs.\n"; + } elsif (!-x _) { + warning "$init_file($.): $_\n\t$cmd: not executable on root fs.\n"; + } + } + } + close(INITF); + info 0, "Done with $init_file\n"; + $checked{$init_file} = 1; + } # end of foreach +} + + + +sub check_termcap { + open(TERMCAP, "<$CFG::mount_point/etc/termcap") or + warning "No file $CFG::mount_point/etc/termcap"; + while (<TERMCAP>) { + chomp; + next unless $_; + next if /^\#/; # Skip comments + next if /^\s+/; # Skip non-head lines + + ##### Get complete logical line + my($def) = $_; + while (/\\$/) { # Trailing backslash => continued + chomp($def); # Discard backslash + chomp($_ = <TERMCAP>); # Get a line, w/o newline char + $def .= $_; + } + + ##### Extract terminal names from line + my($names) = $def =~ /^([^:]+):/; + my(@terms) = split(/\|/, $names); + @Termcap{@terms} = (1) x ($#terms + 1); + } + close(TERMCAP); +} + +##### END OF CHECK_ROOT_FS diff --git a/sys-apps/yard/files/yard-2.2/sbin/make_root_fs b/sys-apps/yard/files/yard-2.2/sbin/make_root_fs new file mode 100644 index 000000000000..2f58ad7c5eed --- /dev/null +++ b/sys-apps/yard/files/yard-2.2/sbin/make_root_fs @@ -0,0 +1,829 @@ +#! /usr/bin/perl +# -*- Mode: Perl -*- +# This script created automatically from scripts/make_root_fs.in +# $Header: /var/cvsroot/gentoo-x86/sys-apps/yard/files/yard-2.2/sbin/make_root_fs,v 1.1 2002/08/25 20:25:33 aliz Exp $ +############################################################################## +## +## MAKE_ROOT_FS +## Copyright (C) 1996,1997,1998 Tom Fawcett (fawcett@croftj.net) +## +## This program is free software; you may redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +## +##### +## +## This is the first script of the Yard suite for creating custom +## rescue disks. +############################################################################## +use strict; +use File::Basename; +use File::Path; +use FileHandle; +use Cwd; +use English; +use lib "/etc/yard", "/usr/lib/yard"; +use yardconfig; + +BEGIN { require "yard_utils.pl"; } +require "Config.pl"; + +STDOUT->autoflush(1); + +start_logging_output(); +info 0, "make_root_fs 2.0\n"; +info 1, "(running under Perl $PERL_VERSION)\n"; + +my($objcopy) = $_path{'objcopy'}; # Define objcopy path if executable exists + +my($Warnings) = 0; +sub warning { + info 0, "Warning: ", @_; + $Warnings++; +} + +############################################################################## +##### Check some basic things before starting. +##### There's probably a more graceful way to maintain and check +##### a set of user options (via a Perl module), but I'm too lazy +##### to track it down. +############################################################################## +if ($REAL_USER_ID != 0) { + error "This script must be run as root\n"; +} + +if (!defined($CFG::device) and !defined($CFG::mount_point)) { + error "Nothing defined in CFG package. You probably just copied\n", + "an old Config.pl file.\n"; +} + +# Check mount point +if (-d $CFG::mount_point and -w _) { + info 1, "Using $CFG::mount_point as mount point for $CFG::device\n"; +} else { + error "Mount point $CFG::mount_point must be a directory and\n", + "must be write-enabled.\n"; +} + +# Check for sane device choice before we start using it. +check_device(); + +# Make sure $CFG::device isn't already mounted and $CFG::mount_point is free +load_mount_info(); + +if (defined($::mounted{$CFG::device})) { + + if ($::mounted{$CFG::device} eq $CFG::mount_point) { + info 1, "Device $CFG::device is already mounted on $CFG::mount_point\n"; + info 1, "Unmounting it automatically.\n"; + sys("umount $CFG::mount_point"); + + } else { + error "$CFG::device is already mounted elsewhere (on $::mounted{$CFG::device})\n", + "Unmount it first.\n"; + } + +} elsif (defined($::mounted{$CFG::mount_point})) { + error "Some other device is already mounted on $CFG::mount_point\n"; +} + +# Have to test this every time so we can work around. +test_glob(); + +##### Determine release of $CFG::kernel for modules. +##### Set RELEASE environment variable for use in contents. +if (defined($CFG::kernel_version)) { + # Check to see if it agrees + my($version_guess) = kernel_version($CFG::kernel); + if ($version_guess ne $CFG::kernel_version) { + info 0, "You declared kernel ($CFG::kernel) to be version $CFG::kernel_version,\n", + "\teven though a probe says $version_guess.", + "\tI'll assume you're right.\n"; + } + $ENV{'RELEASE'} = $CFG::kernel_version; + +} elsif (defined($ENV{'RELEASE'} = kernel_version($CFG::kernel))) { + info 0, "Version probe of $CFG::kernel returns: $ENV{'RELEASE'}\n"; + +} else { + warning "Can't determine kernel version of $CFG::kernel\n"; + my($release) = `uname -r`; + if ($release) { + chomp($release); + info 0, "Will use version of current running kernel ($release)\n", + "Make sure this is OK\n"; + $ENV{'RELEASE'} = $release; + } else { + error "And can't determine running kernel's version either!\n"; + } +} + +warn_about_module_dependencies($ENV{'RELEASE'}); + +if ($CFG::disk_set !~ /^(single|double|base\+extra)$/) { + error "Config variable disk_set is set to \"$CFG::disk_set\"\n", + "which is not a valid value.\n"; +} + +############################################################################## +##### READ IN CONTENTS FILE ##### +############################################################################## +my($contents_file) = resolve_file($CFG::contents_file); +info 0, "\n\nPASS 1: Reading $CFG::contents_file"; +info 0, " ($contents_file)" if $contents_file ne $CFG::contents_file; +info 0, "\n"; + +my(%Included); +my(%replaced_by); +my(%links_to); +my(%is_module); + +open(CONTENTS, "<$contents_file") or error "$contents_file: $!"; + +my($cf_line) = 0; +my($line); + +LINE: while (defined($line = <CONTENTS>)) { + my(@files); + $cf_line++; + chomp $line; + $line =~ s/[\#%].*$//; # Kill comments + next if $line =~ /^\s*$/; # Ignore blank/empty line + + $line =~ s/^\s+//; # Delete leading/trailing whitespace + $line =~ s/\s+$//; + +# if ($line =~ /\$RELEASE/) { +# cf_warn($line, "Make sure \$RELEASE ($ENV{'RELEASE'}) is correct " . +# "for $CFG::kernel"); +# } + + if ($line =~ /->/) { ##### EXPLICIT LINK + if ($line =~ /[\*\?\[]/) { + cf_warn($line, "Can't use wildcards in link specification!"); + next LINE; + } + my($file, $link) = $line =~ /^(\S+)\s*->\s*(\S+)\s*$/; + if (!defined($link)) { + cf_warn($line, "Can't parse this link"); + next LINE; + } + ##### The '->' supersedes file structure on the disk, so don't + ##### call include_file until pass two after all explicit links + ##### have been seen. + my($abs_file) = find_file_in_path($file); + $Included{$abs_file} = 1; + #### Have to be careful here. Record the rel link for use + #### in setting up the root fs, but use the abs_link in @files + #### so next loop gets any actual files. + my($abs_link) = make_link_absolute($abs_file, $link); + my($rel_link) = make_link_relative($abs_file, $link); + $links_to{$abs_file} = $rel_link; + info 1, "$line links $abs_file to $rel_link\n"; + @files = ($abs_link); + + } elsif ($line =~ /<=/) { ##### REPLACEMENT SPEC + cf_die($line, "Can't use wildcard in replacement specification") if + $line =~ /[\*\?\[]/; + + my($file, $replacement) = $line =~ /^(\S+)\s*<=\s*(\S+)\s*$/; + + if (!defined($replacement)) { + cf_warn($line, "Can't parse this replacement spec"); + next LINE; + + } else { + must_be_abs($file); + (-d $file) and cf_warn($line, "left-hand side can't be directory"); + my($abs_replacement) = find_file_in_path($replacement); + if (!(defined($abs_replacement) and -e $abs_replacement)) { + cf_warn($line, "Can't find $replacement"); + + } elsif ($replacement =~ m|^/dev/(?!null)|) { + # Allow /dev/null but no other devices + cf_warn($line, "Can't replace a file with a device"); + + } else { + $replaced_by{$file} = $abs_replacement; + $Included{$file} = 1; + } + + next LINE; + } # End of replacement spec + + } elsif ($line =~ /(<-|=>)/) { + cf_warn($line, "Not a valid arrow."); + next LINE; + + } else { + + @files = (); + my($expr); + for $expr (split(' ', $line)) { + my(@globbed) = yard_glob($expr); + if ($#globbed == -1) { + cf_warn($line, "Warning: No files matched $expr"); + } elsif (!($#globbed == 0 and $globbed[0] eq $expr)) { + info 1, "Expanding $expr to @globbed\n"; + } + push(@files, @globbed); + } + } + + my($file); + FILE: foreach $file (@files) { + + if ($file =~ m|^/|) { ##### Absolute filename + + if (-l $file and readlink($file) =~ m|^/proc/|) { + info 1, "Recording proc link $file -> ", readlink($file), "\n"; + $Included{$file} = 1; + $links_to{$file} = readlink($file); + + } elsif (-e $file) { + + $Included{$file} = 1; + + } elsif ($file =~ m|^$CFG::oldroot/(.*)$|o and -e "/$1") { + ### Don't complain about links to files that will be mounted + ### under $oldroot, the hard disk root mount point. + next FILE; + + } else { + cf_warn($line, "Absolute filename $file doesn't exist"); + } + + } else { ##### Relative filename + my($abs_file) = find_file_in_path($file); + if ($abs_file) { + info 1, "Found $file at $abs_file\n"; + $Included{$abs_file} = 1; + } else { + cf_warn($line, "Didn't find $file anywhere in path"); + } + } + } # End of FILE loop +} # End of LINE loop + +info 0, "\nDone with $contents_file\n\n"; + +if ($CFG::disk_set eq "base+extra") { + include_file(find_file_in_path("tar")) +} + +close(CONTENTS) or error("close on $contents_file: $!"); + + +############################################################################## +info 0, "\n\nPASS 2: Picking up extra files from links...\n"; + +for (keys %Included) { + include_file($_); +} + +info 0, "Done.\n\n"; + +############################################################################## + +info 0, "PASS 3: Checking library dependencies...\n"; +info 1, "(Ignore any 'statically linked' messages.)\n"; + +# Normal file X: X in %Included. +# X -> Y: X in %links_to, Y in %Included +# X <= Y: X in %Included and %replaced_by + +my(%strippable); +my(%lib_needed_by); + +my($file); +foreach $file (keys %Included) { + + ##### Use replacement file if specified + $file = $replaced_by{$file} if defined($replaced_by{$file}); + + ##### Skip links (target will be checked) + next if defined($links_to{$file}); # Symbolic (declared) + next if -l $file; # Symbolic (on disk) + + my($file_line) = `file $file`; + ##### See whether it's strippable and make a note. + ##### This will prevent us from wasting time later running objcopy + ##### on binaries that are already stripped. + if ($file_line =~ m/not stripped/) { + $strippable{$file} = 1; + } + ##### See whether it's a module and mark the info for later + ##### so that we strip it correctly. + if ($file_line =~ m/relocatable/) { + info 1, "Marking $file as a module\n"; + $is_module{$file} = 1; + + } elsif ($file_line =~ m/shared object/) { + ##### Any library (shared object) seen here was explicitly included + ##### by the user. + + push(@{$lib_needed_by{$file}}, "INCLUDED BY USER"); + } + + if (-f $file and -B _ and -x _ and $file_line =~ /executable/) { + + ##### EXECUTABLE LOADABLE BINARY + ##### Run ldd to get library dependencies. + foreach $line (`ldd $file`) { + my($lib) = $line =~ / => (\S+)/; + next unless $lib; + my($abs_lib) = $lib; + + if ($lib =~ /not found/) { + warning "File $file needs library $lib, which does not exist!"; + } else { + + ##### Right-hand side of the ldd output may be a symbolic link. + ##### Resolve the lib absolutely. + ##### include_file follows links and adds each file; + ##### the while loop makes sure we get the last. + $abs_lib = $lib; + include_file($lib); + while (1) { + if (defined($links_to{$abs_lib})) { + $abs_lib = make_link_absolute($abs_lib, + $links_to{$abs_lib}); + } + if (defined($replaced_by{$abs_lib})) { + $abs_lib = $replaced_by{$abs_lib}; + } + last unless -l $abs_lib; + my($link) = readlink($abs_lib) or + error "readlink($abs_lib): $!"; + $abs_lib = make_link_absolute($abs_lib, $link); + + } + } + if (!defined($lib_needed_by{$abs_lib})) { + info 0, "\t$abs_lib\n"; + } + push(@{$lib_needed_by{$abs_lib}}, $file); + } + } +} + +############################################################################## +##### Check libraries and loader(s) ##### +############################################################################## +my(@Libs) = keys %lib_needed_by; + +my($seen_ELF_lib, $seen_AOUT_lib); +my(%full_name); + +if (@Libs) { + info 1, "\nYou need these libraries:\n"; + + my($lib); + foreach $lib (@Libs) { + my($size) = bytes_to_K(-s $lib); + my($line) = " " x 15; + my($file_output) = `file $lib`; + + if ($file_output =~ m/symbolic link/) { + error "Yiiiiii, library file $lib is a symbolic link!\n", + "This shouldn't happen!\n", + "Please report this error to the Yard author\n"; + } + + my($lib_type) = $file_output =~ /:\s*(ELF|Linux)/m; + + ##### All libraries are strippable + $strippable{$lib} = 1; + + info 1, "$lib (type $lib_type, $size K) needed by:\n"; + + my($binary); + for $binary (sort map(basename($_), @{$lib_needed_by{$lib}})) { + if (length($line) + length($binary) > 78) { + info 1, $line, "\n"; + $line = " " x 15; + } + $line .= $binary . " "; + } + info 1, $line, "\n" if $line; + + if (!($seen_ELF_lib and $seen_AOUT_lib)) { + + ##### Check library to make sure we have the right loader. + ##### (A better way is to do "ldconfig -p" and parse the output) + ##### Strings from /usr/lib/magic of file 3.19 + + if (!defined($lib_type)) { + error "Didn't understand `file` output for $lib:\n", + `file $lib`, "\n"; + + } elsif ($lib_type eq 'ELF') { + $seen_ELF_lib = 1; + + } elsif ($lib_type eq 'Linux') { # ie, a.out + $seen_AOUT_lib = 1; + } + } + + ##### See if some other version of this library file is + ##### being loaded, eg libc.so.3.1.2 and libc.so.5.2.18. + ##### Not an error, but worth warning the user about. + + my($lib_stem) = basename($lib) =~ /^(.*?\.so)/; + if (defined($full_name{$lib_stem})) { + warning "You need both $lib and $full_name{$lib_stem}\n", + "Check log file for details.\n"; + } else { + ##### eg, $full_name{"libc.so"} = "/lib/libc.so.5.2.18" + $full_name{$lib_stem} = $lib; + } + } +} + +info 1, "\n"; +if ($seen_ELF_lib) { + # There's no official way to get the loader file, AFAIK. + # This expression should get the latest version, and Yard will grab any + # hard-linked file. + my($ld_file) = (yard_glob("/lib/ld-linux.so.?"))[-1]; # Get last one + if (defined($ld_file)) { + info 1, "Adding loader $ld_file for ELF libraries\n"; + include_file($ld_file); + } else { + info 0, "Can't find ELF loader /lib/ld-linux.so.?"; + } +} +if ($seen_AOUT_lib) { + # Was: yard_glob("/lib/ld.so*") + # Same as above, but ld.so seems to have no version number appended. + my($ld_file); + foreach $ld_file (yard_glob("/lib/ld.so")) { + info 1, "Adding loader $ld_file for a.out libraries\n"; + include_file($ld_file); + } +} + +info 0, "Done\n\n"; + +info 0, "PASS 4: Recording hard links...\n"; + +##### Finally, scan all files for hard links. +my(%hardlinked); +foreach $file (keys %Included) { + + next if $links_to{$file} or $replaced_by{$file}; + ##### $file is guaranteed to be absolute and not symbolically linked. + + ##### Record hard links on plain files + if (-f $file) { + my($dev, $inode, $mode, $nlink) = stat(_); + if ($nlink > 1) { + $hardlinked{$file} = "$dev/$inode"; + } + } +} + +info 0, "Done.\n\n"; + +############################################################################## +info 0, "Checking space needed.\n"; +my($total_bytes) = 0; +my(%counted); + +foreach $file (keys %Included) { + + my($replacement, $devino); + if ($replacement = $replaced_by{$file}) { + ##### Use the replacement file instead of this one. In the + ##### future, improve this so that replacement is resolved WRT + ##### %links_to + info 1, "Counting bytes of replacement $replacement\n"; + $total_bytes += bytes_allocated($replacement); + + } elsif (-l $file or $links_to{$file}) { + ##### Implicit or explicit symbolic link. Only count link size. + ##### I don't think -l test is needed. + my($size) = (-l $file) ? length(readlink($file)) + : length($links_to{$file}); + info 1, "$file (link) size $size\n"; + $total_bytes += $size; + + } elsif ($devino = $hardlinked{$file}) { + ##### This file is hard-linked to another. We don't necessarily + ##### know that the others are going to be in the file set. Count + ##### the first and mark the dev/inode so we don't count it again. + if (!$counted{$devino}) { + info 1, "Counting ", -s _, " bytes of hard-linked file $file\n"; + $total_bytes += bytes_allocated($file); + $counted{$devino} = 1; + } else { + info 1, "Not counting bytes of hard-linked file $file\n"; + } + + } elsif (-d $file) { + $total_bytes += $::INODE_SIZE; + info 1, "Directory $file = ", $::INODE_SIZE, " bytes\n"; + + } elsif ($file =~ m|^/proc/|) { + ##### /proc files screw us up (eg, /proc/kcore), and there's no + ##### Perl file test that will detect them otherwise. + next; + + } elsif (-f $file) { + ##### Count space for plain files + info 1, "$file size ", -s _, "\n"; + $total_bytes += bytes_allocated($file); + } +} + +# Libraries are already included in the count + +info 0, "Total space needed is ", bytes_to_K($total_bytes), " Kbytes\n"; + +if (bytes_to_K($total_bytes) > $CFG::fs_size) { + info 0, "This is more than $CFG::fs_size Kbytes allowed.\n"; + if ($CFG::strip_objfiles) { + info 0, "But since object files will be stripped, more space\n", + "may become available. Continuing...\n"; + } else { + error "You need to trim some files out and try again.\n"; + } +} + +info 0, "\n"; + +############################################################################## +##### Create filesystem +############################################################################## +sync(); +sys("dd if=/dev/zero of=$CFG::device bs=1k count=$CFG::fs_size"); +sync(); + +info 0, "Creating ${CFG::fs_size}K ext2 file system on $CFG::device\n"; + +if (-f $CFG::device) { + ##### If device is a plain file, it means we're using some loopback + ##### device. Use -F switch in mke2fs so it won't complain. + sys("mke2fs -F -m 0 -b 1024 $CFG::device $CFG::fs_size"); +} else { + sys("mke2fs -m 0 -b 1024 $CFG::device $CFG::fs_size"); +} + +&mount_device; +##### lost+found on a ramdisk is pointless +sys("rm -rf $CFG::mount_point/lost+found"); + +sync(); + + +##### Setting up the file structure is tricky. Given a tangled set +##### of symbolic links and directories, we have to create the +##### directories, symlinks and files in the right order so that no +##### dependencies are missed. + +##### First, create directories for symlink targets that are supposed +##### to be directories. Symlink targets can't be superseded so +##### sorting them by path length should give us a linear ordering. +info 0, "Creating directories for symlink targets\n"; + +for $file (sort { path_length($a) <=> path_length($b) } + keys %links_to) { + my($link_target) = $links_to{$file}; + my($abs_file) = make_link_absolute($file, $link_target); + if (-d $abs_file) { + my($floppy_file) = $CFG::mount_point . $abs_file; + my($newdir); + foreach $newdir (mkpath($floppy_file)) { + info 1, "\tCreating $newdir as a link target for $file\n"; + } + } +} + + +##### Next, set up actual symlinks, plus any directories that weren't +##### created in the first pass. Sorting by path length ensures that +##### parent symlinks get set up before child traversals. +info 0, "Creating symlinks and remaining directories.\n"; +for $file (sort { path_length($a) <=> path_length($b) } + keys %Included) { + + my($target); + if (defined($target = $links_to{$file})) { + my($floppy_file) = $CFG::mount_point . $file; + mkpath(dirname($floppy_file)); + info 1, "\tLink\t$floppy_file -> $target\n"; + symlink($target, $floppy_file) or + error "symlink($target, $floppy_file): $!\n"; + delete $Included{$file}; # Get rid of it so next pass doesn't copy it + + } elsif (-d $file) { + my($floppy_file) = $CFG::mount_point . $file; + my($newdir); + foreach $newdir (mkpath($floppy_file)) { + info 1, "\tCreate\t$newdir\n"; + } + delete $Included{$file}; # Get rid of it so next pass doesn't copy it + } +} + + +##### Tricky stuff is over with, now copy the remaining files. + +info 0, "\nCopying files to $CFG::device\n"; + +my(%copied); + +my($file); +while (($file) = each %Included) { + my($floppy_file) = $CFG::mount_point . $file; + + my($replacement); + if (defined($replacement = $replaced_by{$file})) { + $file = $replacement; + } + + if ($file =~ m|^/proc/|) { + ##### Ignore /proc files + next; + + } elsif (-f $file) { + ##### A normal file. + mkpath(dirname($floppy_file)); + + ##### Maybe a hard link. + my($devino, $firstfile); + if (defined($devino = $hardlinked{$file})) { + ##### It's a hard link - see if the linked file is already + ##### on the root filesystem. + if (defined($firstfile = $copied{$devino})) { + ##### YES - just hard link it to existing file. + info 1, "Hard linking $floppy_file to $firstfile\n"; + sys("ln $firstfile $floppy_file"); + next; # Skip copy + + } else { + ##### NO - copy it. + $copied{$devino} = $floppy_file; + } + } + info 1, "$file -> $floppy_file\n"; + copy_strip_file($file, $floppy_file); + + } elsif (-d $file) { + ##### A directory. + info 1, "Creating directory $floppy_file\n"; + mkpath($floppy_file); + + } elsif ($file eq '/dev/null' and + $floppy_file ne "$CFG::mount_point/dev/null") { # I hate this + info 1, "Creating empty file $floppy_file\n"; + mkpath(dirname($floppy_file)); + sys("touch $floppy_file"); + + } else { + ##### Some special file. + info 1, "Copying special $file to $floppy_file\n"; + mkpath(dirname($floppy_file)); + # The 'R' flag here allows cp command to handle devices and FIFOs. + sys("cp -dpR $file $floppy_file"); + } +} + + +############################################################################## + +info 0, "\nFinished creating root filesystem.\n"; + +if (@Libs) { + + info 0, "Re-generating /etc/ld.so.cache on root fs.\n"; + info 1, "Ignore warnings about missing directories\n"; + + sys("ldconfig -v -r $CFG::mount_point"); +} + +info 0, "\nDone with $PROGRAM_NAME. $Warnings warnings.\n", + "$CFG::device is still mounted on $CFG::mount_point\n"; + +exit( $Warnings>0 ? -1 : 0); + + +############################################################################# +##### Utility subs for make_root_fs.pl +############################################################################# + +##### Add file to the file set. File has to be an absolute filename. +##### If file is a symlink, add it and chase the link(s) until a file is +##### reached. +sub include_file { + my($file) = @_; + + must_be_abs($file); + if (onto_proc_filesystem($file)) { + info 1, "File $file points into proc filesystem -- not pursued.\n"; + return; + } + + $Included{$file} = 1; + + ##### If we have links A -> B -> C -> D -> E + ##### on disk and A -> D is set explicitly, then we pick up + ##### files A and D in pass 1, and E on pass 2. + + while (!defined($links_to{$file}) and !defined($replaced_by{$file}) + and -l $file) { + + ##### SYMBOLIC LINK on disk, not overridden by explicit link or + ##### replacement. Relativize the link for use later, but also + ##### check and resolve the target so it gets onto the rescue disk. + my($link) = readlink($file) or error "readlink($file): $!"; + my($rel_link) = make_link_relative($file, $link); + $links_to{$file} = $rel_link; + + my($abs_target) = make_link_absolute($file, $link); + if (onto_proc_filesystem($abs_target)) { + info 1, "$file points to $abs_target, on proc filesystem\n"; + last; + } + + if (!$Included{$abs_target}) { + info 1, "File $file is a symbolic link to $link\n"; + info 1, "\t(which resolves to $abs_target),\n" + if $link ne $abs_target; + info 1, "\twhich was not included in $CFG::contents_file.\n"; + if (-e $abs_target) { + info 1, "\t ==> Adding it to file set.\n\n"; + $Included{$abs_target} = $file; + } else { + info 0, "\t ==> $abs_target does not exist. Fix this!\n"; + } + } + $file = $abs_target; # For next iteration of while loop + } +} + + + +##### More informative versions of warn and die, for the contents file +sub cf_die { + my($line, @msgs) = @_; + info 0, "$CFG::contents_file($cf_line): $line\n"; + foreach (@msgs) { info 0, "\t$_\n"; } + exit; +} + +sub cf_warn { + my($line, @msgs) = @_; + info 0, "$CFG::contents_file($cf_line): $line\n"; + $Warnings++; + foreach (@msgs) { info 0, "\t$_\n"; } +} + + +# Copy a file, possibly stripping it. Stripping is done if the file +# is strippable and stripping is desired by the user, and if the +# objcopy program exists. +sub copy_strip_file { + my($from, $to) = @_; + + if ($CFG::strip_objfiles and defined($objcopy) and $strippable{$from}) { + # Copy it stripped + + if (defined($lib_needed_by{$from})) { + # It's a library + info 1, "Copy/stripping library $from to $to\n"; + sys("$objcopy --strip-all $from $to"); + + } elsif (defined($is_module{$from})) { + info 1, "Copy/stripping module $from to $to\n"; + sys("$objcopy --strip-debug $from $to"); + + } else { + # It's a binary executable + info 1, "Copy/stripping binary executable $from to $to\n"; + sys("$objcopy --strip-all $from $to"); + } + # Copy file perms and owner + my($mode, $uid, $gid); + (undef, undef, $mode, undef, $uid, $gid) = stat $from; + chown($uid, $gid, $to) or error "chown: $!"; + chmod($mode, $to) or error "chmod: $!"; + + } else { + # Normal copy, no strip + sys("cp $from $to"); + } +} + + +##### End of make_root_fs diff --git a/sys-apps/yard/files/yard-2.2/sbin/mklibs.sh b/sys-apps/yard/files/yard-2.2/sbin/mklibs.sh new file mode 100644 index 000000000000..0fc5d90b0819 --- /dev/null +++ b/sys-apps/yard/files/yard-2.2/sbin/mklibs.sh @@ -0,0 +1,863 @@ +#!/bin/bash +# +# mklibs.sh: An automated way to create a minimal /lib/ directory. +# +# Copyright 1999 by Marcus Brinkmann <Marcus.Brinkmann@ruhr-uni-bochum.de> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Introduction: +# When creating boot floppies, there is never enough room on the disks. +# So it is important not to waste bytes on unnecessary code. +# Shared Libraries contain many functions that are probably not used in the +# binaries included in the boot disks, and copying the whole library is a +# waste of space. +# This utilitiy helps to reduce the necessary libraries to only include the +# symbols needed to run a given set of executables. +# +# Features: +# * Automatic detection of all necessary libraries, even for inter-library +# dependencies, for a given set of executables. +# * Automatic installation of all needed libraries and soname links. +# * Automatic reduction of all libraries to minimal size for which PIC +# libraries are provided. +# +# Requirements: +# * Beside the shared libraries, you need libfoo_pic.a files for all +# libraries you want to reduce. +# * You need binutils (notably objdump and objcopy) installed. + +# A GENERAL NOTE ABOUT LANGUAGE ABUSE +# +# If you believe this program had better not been written in shell script +# language, I invite you to reimplement it in the language of your +# preference. +# The reasons I chose shell are: +# * Shell scripts are very portable and available even on minimal systems as +# well as boot disks. +# * Shell scripts can be run without compilation. +# * Shell scripts provide a very easy interface to the various system +# commands I need to get the library dependencies and sort through them. +# Perl is lacking good data types, so implementing this would be equally +# cumbersome in perl (I need trees and queues, for example). +# C and C++ are lacking easy access to the system commands. +# +# Of course, shell scripting has many problems: +# * Use of temporary files for string arrays. +# * Slow. +# * Hard to debug. +# +# I think for hand written code, I hit a limit with the size and execution +# time of this program of what is still acceptable as a shell script. I also +# tried to improve the situation with many comments. + +# TODO: +# * Make sure versioned symbols get correct version number. +# This seems to work now, however, we always include +# all versions of a symbol. This is not a problem. To do +# it properly, we had to parse the version information in +# objdump, which is hard. +# * Use --dynamic-syms on so lib instead --syms on pic file. +# * Autodetect that libc needs ld (should be possible from +# output of objdump --privat-headers| grep NEEDD). +# * Code to create libs in cycles !!! + +# HISTORY: +# +# 1999-09-13 Marcus Brinkmann <brinkmd@debian.org> +# +# * Initial release (v0.1). +# + +# STATIC DATA SECTION +# + +usage="Usage: $0 [OPTION]... -d DEST FILE ..." +try="Try "\`"$0 --help' for more information" +version="$0 0.1, Copyright 1999 Marcus Brinkmann" + +PATH=/bin:/usr/bin + +default_src_path=/lib:/usr/lib +dest="" +exec="" +action="" +verbose="false" + +gcc=${GCC-gcc} +objdump=${OBJDUMP-objdump} +objcopy=${OBJCOPY-objcopy} + +# ================= +# GRAPH ABSTRACTION +# ================= +# +# Because we do some hairy graph operations, we provide some +# abstractions of them. Some functions here are very simple, but +# the source is much more readable this way. + +# check-node NODE ... +# checks if all NODEs are valid node names. +# Used internally for verificaton only. +# Return 0 if all NODEs are valid. +# Currently, a node is valid if it does not contain a space. + +check-node () { + local node + for node in "$@" ; do + if [ "x`echo $node | sed -e '/ /d'`" = x ] ; then + echo 1>&2 $0: check-node: invalid node \"$node\" + exit 1 + fi + done + return 0 +} + +# is-graph FILE ... +# provides a very simple type assertion +# Turns FILE into a graph if it isn't already and returns 0. + +is-graph () { + local file + for file in "$@" ; do + if [ ! -e "$file" ] ; then + touch "$qfile" + fi + done +} + +# add-node FILE NODE +# add a node NODE to graph FILE. +# This is useful if you need to make sure that a node appears +# in the graph without actually connecting it to an arrow. +# You don't need to add nodes that are part of an arrow. + +add-node () { + if [ $# != 2 ] ; then + echo 1>&2 $0: add-node: internal error: called with invalid number of arguments + exit 1 + fi + check-node "$2" + echo "$2 $2" >> "$1" + return 0 +} + +# add-arrow FILE NODE1 NODE2 +# add an arrow from NODE1 to NODE2 to graph FILE. + +add-arrow () { + if [ $# != 3 ] ; then + echo 1>&2 $0: add-arrow: internal error: called with invalid number of arguments + exit 1 + fi + check-node "$2" "$3" + echo "$2 $3" >> "$1" + return 0 +} + +# find-cycle FILE +# finds a cycle in a graph FILE. +# If a cycle is found, it is printed out at stdin, one node each line, +# and 0 is returned. Otherwise, nothing is printed on stdout and exit +# status is 1. + +find-cycle () { + if [ $# != 1 ] ; then + echo 1>&2 $0: find-cycle: internal error: called with invalid number of arguments + exit 1 + fi + tsort "$1" 2> "$fl_dir/find-cycle" > /dev/null + if [ "x`cat $fl_dir/find-cycle`" = x ] ; then + return 1 + else + if [ "x`head -1 $fl_dir/find-cycle`" != "xtsort: cycle in data" ] ; then + echo 1>&2 $0: find-cycle: internal error: tsort has invalid output format + exit 1 + fi + cat "$fl_dir/find-cycle" | sed -e '1d' -e '/tsort: cycle in data/,$d' -e 's/^tsort: //' + fi +} + +# shrink-nodes FILE NODE1 ... +# shrinks several nodes NODE1 ... to a single node in graph FILE. +# To hide cycles, we treat a cycle as a single node and replace +# each occurence of a node in the cycle with a new node +# [NODE1,...] . This change is destructive and can not be undone! +# (You would need to store the entry point to the cycle for each arrow +# pointing to/from it). +# This function does not check if the the nodes NODE1 ... exist. +# However, if none of these nodes exists already, the new node will +# not appear either. This makes this function sort of idem potent. +# It does not check if NODE1 ... are a cycle. We will assume this +# later in the library dependency analysis, but nothing in the code +# relies on it. +# Always shrink all cycles, or you may get unresolved symbols. +# +# Example: +# N1 ---> N2 N1 -------> /------------\ +# | "shrink-nodes N2 N4" | _ | [N2,N4] | +# v -------------------> v _____/| \------------/ +# N3 ---> N4 N3 / + +# A small helper function will aid us... +# equal-match STRING STRING1 ... +# return 0 if STRING is among STRING1 ..., 1 otherwise. +equal-match () { + local string + local stringk + string="$1" + shift + for stringk in "$@" ; do + if [ "x$string" = "x$stringk" ] ; then + return 0 + fi + done + return 1 +} + +shrink-nodes () { + local head + local lnode + local rnode + local graph="$1" + shift + is-graph "$graph" + check-node "$@" + local cnode="[`echo "$@" | sed 's/ /,/g'`]" + # Okay, it's a hack. We treat the graph as a queue. I am just too + # lazy to copy the relevant code here. Of course, we exploit several + # properties of the graph and queue file format here (for example, + # that graphs never can contain a QUEUE_SEPERATOR, and that a graph is + # really a simple file with "a b" entries). + cat /dev/null > "$fl_dir/shrink-cycle" + while head=`get-top-of-queue "$graph"` ; do + lnode=`echo $head|sed 's/ [^ ]*$//'` + if equal-match "$lnode" "$@" ; then + lnode="$cnode" + fi + rnode=`echo $head|sed 's/^[^ ]* //'` + if equal-match "$rnode" "$@" ; then + rnode="$cnode" + fi + echo "$lnode $rnode" >> "$fl_dir/shrink-cycle" + done + cat "$fl_dir/shrink-cycle" | sort -u > "$graph" +} + +# ================= +# QUEUE ABSTRACTION +# ================= +# +# I added an abstract interface for queues to make the code more readable. +# Queue operations usually consist of several atomic file operations, which +# can get quite messy. +# +# You can use queues to simply loop through all lines of a file, but you +# also can add stuff to the queue while processing it. +# +# Implementation: All queues consist of a QUEUE_FILE which has two parts: +# the remaining entries in the queue (QUEUE) and the already processed +# entries (BUCKET). +# The two parts are seperated by a line containing only QUEUE_SEPERATOR. + +QUEUE_SEPERATOR=SEPERATOR___ABOVE_IS_QUEUE__BELOW_IS_BUCKET___SEPERATOR + +# check-queue-entry QENTRY ... +# checks if all queue entries QENTRY are valid. +# Used internally for verificaton only. +# Return 0 if all QENTRYs are valid. +# Currently, a node is valid if it does not match the QUEUE_SEPERATOR. + +check-queue-entry () { + local qentry + for qentry in "$@" ; do + if [ "x`echo $qentry | sed "/^$QUEUE_SEPERATOR$/d"`" = x ] ; then + echo 1>&2 $0: check-queue-entry: invalid qentry name \"$qentry\" + exit 1 + fi + done + return 0 +} + +# is-queue QUEUE_FILE ... +# provides a very simple type assertion +# Turns QUEUE_FILE into a queue if it isn't already and returns 0. + +is-queue () { + local qfile + for qfile in "$@" ; do + if [ ! -e "$qfile" ] ; then + echo "$QUEUE_SEPERATOR" > "$qfile" + else + if ! grep -q "^$QUEUE_SEPERATOR$" "$qfile" ; then + echo "$QUEUE_SEPERATOR" >> "$qfile"; + fi + fi + done +} + +# get-top-of-queue QUEUE_FILE +# processes a queue one more time. +# If QUEUE of QUEUE_FILE is empty, exit status is 1 and no output is given. +# Otherwise, top of QUEUE is removed, returned on stdout and +# appended to the end of the BUCKET part of QUEUE_FILE. + +get-top-of-queue () { + if [ $# != 1 ] ; then + echo 1>&2 $0: get-top-of-queue: internal error: called with invalid number of arguments + exit 1 + fi + is-queue "$1" + local head=`head -1 "$1"` + if [ "x$head" = "x$QUEUE_SEPERATOR" ] ; then + return 1 + else + sed -e 1d "$1" > "$fl_dir/get-top-of-queue" + echo "$head" | tee --append "$fl_dir/get-top-of-queue" + cat "$fl_dir/get-top-of-queue" > "$1" + return 0 + fi +} + +# add-to-queue-if-not-there QUEUE_FILE QENTRY ... +# add queue entries QENTRY ... to the beginning of the +# QUEUE of QUEUE_FILE if it is neither in QUEUE nor in BUCKET +# of QUEUE_FILE. +# Return with exit status 0. +# Note: If you want to add QENTRY to the *end* of QUEUE, you would do +# something like the following: +# sed -e s/^$QUEUE_SEPERATOR$/$head"'\ +# '"$QUEUE_SEPERATOR/" +# which is necessary to pass the newline to sed. I think we can take the +# easy way out. + +add-to-queue-if-not-there () { + local qentry + local qfile="$1" + shift + check-queue-entry "$@" + is-queue "$qfile" + for qentry in "$@" ; do + if ! grep -q "^$qentry\$" "$qfile" ; then + echo "$qentry" > "$fl_dir/add-to-queue-if-not-there" + cat "$qfile" >> "$fl_dir/add-to-queue-if-not-there" + cat "$fl_dir/add-to-queue-if-not-there" > "$qfile" + fi + done + return 0 +} + +# ================== +# LIBRARY PROCESSING +# ================== +# +# The following helper functions mess around with the actual +# processing and installation of libraries. +# + +# get-library-depends OBJ1 ... +# get all libraries the objects OBJ1 ... depend on. +# OBJs can be binaries or shared libraries. +# The list is neither sort'ed nor uniq'ed. + +get-library-depends () { + if [ $# = 0 ] ; then + echo 1>&2 $0: get-library-depends: internal error: no arguments + exit 1 + fi + $objdump --private-headers "$@" 2> /dev/null \ + | sed -n 's/^ *NEEDED *\([^ ]*\)$/\1/p' +} + +# get-undefined-symbols OBJ1 ... +# get all unresolved symbols in OBJ1 ... +# The list is neither sort'ed nor uniq'ed. + +get-undefined-symbols () { + if [ $# = 0 ] ; then + echo 1>&2 $0: get-undefined-symbols: internal error: no arguments + exit 1 + fi + # ash has undefined reference to sys_siglist if .bss is not mentioned + # here. Reported by Joel Klecker. + # All symbols are epxosed, so we just catch all. Suggested by Roland + # McGrath. Another thing to try is to investigate --dynamic-reloc. + $objdump --dynamic-syms "$@" 2> /dev/null \ + | sed -n 's/^.* \([^ ]*\)$/\1/p' +# | sed -n 's/^.*[\*UND\*|.bss].* \([^ ]*\)$/\1/p' +} + +# get-provided-symbols LIB1 LIB2 ... +# get all symbols available from libraries LIB1 ... . +# Does only work for pic libraries. +# +# v Watch the tab stop here. +# 00000000 w F .text 00000000 syscall_device_write_request +# 00000000 g F .text 0000056c __strtoq_internal + +get-provided-symbols () { + if [ $# = 0 ] ; then + echo 1>&2 $0: get-provided-symbols: internal error: no arguments + exit 1 + fi + $objdump --syms "$@" 2>/dev/null | grep -v '\*UND\*' \ + | sed -n 's/^[0-9a-f]\+ \(g \| w\) .. .* [0-9a-f]\+ \(0x8[08]\)\? *\([^ ]*\)$/\3/p' +} + +# Crude hack (?) only used for diagnostic. + +get-provided-symbols-of-so-lib () { + if [ $# = 0 ] ; then + echo 1>&2 $0: get-provided-symbols: internal error: no arguments + exit 1 + fi + $objdump --dynamic-syms "$@" 2>/dev/null \ + | sed -e '/\*UND\*/d' | sed -n 's/^.* \([^ ]*\)$/\1/p' +} + +# get-common-symbols FILE1 FILE2 +# returns a list of all symbols in FILE1 that appear also in FILE2 +# Note: When get-common-symbols returns, FILE1 and FILE2 are "sort -u"'ed. +# Note: Version Information in FILE1 is ignored when comparing. + +get-common-symbols () { + if [ $# != 2 ] ; then + echo 1>&2 $0: get-common-symbols: internal error: called with invalid number of arguments + exit 1 + fi + # Not needed anymore, but we go for compatibility. + # (Somewhere we HAVE to clean FILE2 up). + sort -u "$1" > $fl_dir/get-common-symbols + cat $fl_dir/get-common-symbols > "$1" + sort -u "$2" > $fl_dir/get-common-symbols + cat $fl_dir/get-common-symbols > "$2" + + local symbol= + while symbol=`get-top-of-queue $fl_dir/get-common-symbols` ; do + grep ^$symbol\$\\\|^$symbol@ "$1" + done +} + +# create-link TARGET LINK_NAME +# creates a soft link if there isn't one already. + +create-link () { + if [ $# != 2 ] ; then + echo 1>&2 $0: create-link: internal error: called with invalid number of arguments + exit 1 + fi + if [ ! -e "$2" ] ; then + $action ln -s "$1" "$2" + fi +} + +# find-file PATH FILE +# search all directories in PATH for file FILE, return absolute path +# FILE can be a relative path and a filename. +# PATH is a list, seperator is ':'. + +find-file () { + if [ $# != 2 ] ; then + echo 1>&2 $0: find-file: internal error: exactly two arguments required + exit 1 + fi + local path=$1 + local dir=`echo $path | sed -e 's/:.*$//'` + until [ "x$path" = x ] ; do + if [ "x$dir" != x ] ; then + if [ -e "$dir/$2" ] ; then + echo "$dir/$2" + return 0 + fi + fi + path=`echo $path | sed -e 's/^[^:]*:*//'` + dir=`echo $path | sed -e 's/:.*$//'` + done + return 1 +} + +# find-files PATH FILE1 FILE2 ... +# search all directories in PATH for file FILE1, FILE2... +# FILE can be a relative path and a filename. +# PATH is a list, seperator is ':'. +# Return value is a white space seperated list of absolute filenames. + +find-files () { + if [ $# -lt 2 ] ; then + echo 1>&2 $0: find-files: internal error: too few arguments + exit 1 + fi + local path="$1" ; shift + while [ $# != 0 ] ; do + find-file $path $1 + shift + done +} + +# get-pic-file LIB +# returns the filename of the pic archive for LIB. +# Note: There doesn't seem to be any convention, *ick*. + +get-pic-file () { + if [ $# != 1 ] ; then + echo 1>&2 $0: get-pic-file: internal error: called with invalid number of arguments + exit 1 + fi + if [ "x$1" = "xlibc-2.2.2.so" ] ; then + # Order does matter! First init, then lib, then fini! + echo `find-files $src_path libc_pic/soinit.o libc_pic.a libc_pic/sofini.o libc_pic/interp.o` + return 0 + fi + # librt + for i in libm libcrypt libdl + do + if [ "x$1" = "x${i}-2.2.2.so" ] ; then + echo `find-file "$src_path" ${i}_pic.a` + return 0 + fi + done + if [ "x$1" = "xlibpthread-0.9.so" ] ; then + echo `find-file $src_path libpthread_pic.a` + return 0 + fi + if [ "x$1" = "xlibslang.so.1.3.9" ] ; then + echo `find-file $src_path libslang1.3.9_pic.a` + return 0 + fi +# local libname=`echo $1 | sed -e 's/^lib\(.*\)-.*.so.*/\1/'` +# echo `find-file "$src_path" lib${libname}_pic.a` + return 0 +} + +get-extra-flags () { + if [ $# != 1 ] ; then + echo 1>&2 $0: get-extra-flags: internal error: called with invalid number of arguments + exit 1 + fi + if [ "x$1" = "xlibc-2.2.2.so" ] ; then + echo "`find-file $src_path ld-2.2.2.so` -lgcc -Wl,--version-script=`find-file $src_path libc_pic.map`" + return 0 + fi + if [ "x$1" = "xlibpthread-0.9.so" ] ; then + echo "-Wl,--version-script=`find-file $src_path libpthread_pic.map`" + return 0 + fi + + for i in libm libcrypt libdl + do + if [ "x$1" = "x${i}-2.2.2.so" ] ; then + echo "-Wl,--version-script=`find-file $src_path ${i}_pic.map`" + return 0 + fi + done + if [ "x$1" = "xlibpthread-0.9.so" ] ; then + echo "-Wl,--version-script=`find-file $src_path libpthread_pic.map`" + return 0 + fi + + return 0 +} + +# install-small-lib LIB_SONAME +# makes a small version of library LIB_SONAME +# +# This happens the following way: +# 0. Make exception for the linker ld. +# 1. Try to figure out complete path of pic library. +# 2. If no found, copy the shared library, else: +# a. Get shared libraries this lib depends on, transform into a +# list of "-lfoo" options. +# b. Get a list of symbols both provided by the lib and in the undefined +# symbols list. +# c. Make the library, strip it. +# d. Add symbols that are still undefined to the undefined symbols list. +# e. Put library into place. + +install-small-lib () { + if [ $# != 1 ] ; then + echo 1>&2 $0: install-small-lib: internal error: called with invalid number of arguments + exit 1 + fi + local src_file=`find-file $src_path $1` + if `echo "$1" | grep -q ^ld` ; then + get-provided-symbols "$src_file" >> $fl_dir/provided-symbols + $action $objcopy --strip-unneeded -R .note -R .comment "$src_file" "$dest/$1" + return 0 + fi + local pic_objects=`get-pic-file "$1"` + local extra_flags=`get-extra-flags "$1"` + if [ "x$pic_objects" = x ] ; then + $verbose 2>&1 No pic archive for library "$1" found, falling back to simple copy. + get-provided-symbols-of-so-lib "$src_file" >> $fl_dir/provided-symbols + get-undefined-symbols "$src_file" >> $fl_dir/undefined-symbols + $action $objcopy --strip-unneeded -R .note -R .comment "$src_file" "$dest/$1" + else + $verbose 2>&1 Make small lib from "$pic_objects" in "$dest/$1". + + # XXX: If ld is NEEDED, we need to include it on the gcc command line + get-library-depends "$src_file" \ + | sed -n -e 's/^lib\(.*\)\.so.*$/\1/p' > $fl_dir/lib-dependencies + get-provided-symbols $pic_objects > $fl_dir/lib-provided-symbols + # Argument order does matter: + get-common-symbols $fl_dir/lib-provided-symbols \ + $fl_dir/undefined-symbols > $fl_dir/lib-symbols-to-include + + ${gcc} \ + -nostdlib -nostartfiles -shared \ + "-Wl,-soname=$1" \ + `cat $fl_dir/lib-symbols-to-include | sed 's/^/-u/'` \ + -o $fl_dir/lib-so \ + $pic_objects $extra_flags \ + "-L$dest" \ + -L`echo $src_path | sed -e 's/::*/:/g' -e 's/^://' -e 's/:$//' \ + -e 's/:/ -L/g'` \ + `cat $fl_dir/lib-dependencies | sed 's/^/-l/'` \ + && $objcopy --strip-unneeded -R .note -R .comment $fl_dir/lib-so $fl_dir/lib-so-stripped \ + || { + echo 1>&2 $0: install-small-lib: $gcc or $objcopy failed. + exit 1 + } + get-undefined-symbols $fl_dir/lib-so-stripped \ + >> $fl_dir/undefined-symbols + get-provided-symbols-of-so-lib $fl_dir/lib-so-stripped >> $fl_dir/provided-symbols + $action cp $fl_dir/lib-so-stripped "$dest/$1" + fi +} + +# install-libs-in-sphere [LIB1,...] +# extracts the libs in a shrinked node and cycles through them until all +# possible symbols are resolved. +# Always make sure this can be called recursively (from install-libs)! + +install-libs-in-sphere () { + if [ $# != 1 ] ; then + echo 1>&2 $0: install-libs-in-sphere: internal error: called with invalid number of arguments + exit 1 + fi + # Unfortunately, we need a small parser here to do the right thing when + # spheres are within spheres etc. RegEx simply can't count brackets. :( + local string=`echo "$1" | sed -e 's/^\[//' -e 's/\]$//'` + local char + local result= + local depth=0 + while [ "x$string" != x ] ; do + # Jump to next special char for faster operation. + # Don't be confused by the regex, it matches everything but ],[ + char=`echo $string | sed -e 's/^\([^],[]*\).*$/\1/'` + string=`echo $string | sed -e 's/^[^],[]*//'` + result="$result$char"; + # Read special char + char=`echo $string | sed -e 's/^\(.\).*$/\1/'` + string=`echo $string | sed -e 's/^.//'` + case "$char" in + [) depth=$(($depth+1));; + ]) depth=$(($depth-1));; + ,) if [ $depth = 0 ] ; then + char=' '; + fi;; + esac + result="$result$char"; + done + $verbose 2>&1 "RESOLVING LOOP...`echo $result | md5sum`" + echo XXX: CODE NOT FINISHED + install-libs $result + $verbose 2>&1 "END OF LOOP... `echo $result | md5sum`" +} + +# install-libs LIB1 ... +# goes through an ordered list of libraries and installs them. +# Make sure this can be called recursively, or hell breaks loose. +# Note that the code is (almost) tail-recursive. I wish I could +# write this in Scheme ;) + +install-libs () { + local cur_lib + local lib + for cur_lib in "$@" ; do + if echo "$cur_lib" | grep -q '^\[' ; then + install-libs-in-sphere "$cur_lib" + else + lib=`find-file $src_path $cur_lib` + if [ -L "$lib" ] ; then + lib=`basename \`readlink $lib\`` + create-link $lib $dest/$cur_lib + else + install-small-lib $cur_lib + fi + fi + done +} + +# +# MAIN PROGRAM +# +# 1. Option Processing +# 2. Data Initialization +# 3. Graph Construction and Reduction +# 4. Library Installation + +# Global Files: +# $fl_dir/undefined-symbols +# Holds all undefined symbols we consider for inclusion. +# Only grows. Does not to be sort'ed and uniq'ed, but will +# get occasionally. +# $fl_dir/provided-symbols +# Holds all defined symbols we included. +# Only grows. Should later be a superset of undefined-symbols. +# But some weak symbols may be missing! +# $fl_dir/library-depends +# Queue of all libraries to consider. + +# +# 1. Option Processing +# + +while :; do + case "$1" in + -L) src_path="$src_path:$2"; shift 2;; + -d|--dest-dir) dest=$2; shift 2;; + -n|--dry-run) action="echo"; shift;; + -v|--verbose) verbose="echo"; shift;; + -V|--version) echo "$version"; exit 1;; + -h|--help) + echo "$usage" + echo "Make a set of minimal libraries for FILE ... in directory DEST." + echo '' + echo "\ +Options: + -L DIRECTORY Add DIRECTORY to library search path. + -n, --dry-run Don't actually run any commands; just print them. + -v, --verbose Print additional progress information. + -V, --version Print the version number and exit. + -h, --help Print this help and exit. + + -d, --dest-dir DIRECTORY Create libraries in DIRECTORY. + +Required arguments for long options are also mandatory for the short options." + exit 0;; + -*) echo 1>&2 $0: $1: unknown flag; echo 1>&2 "$usage"; echo 1>&2 "$try"; exit 1;; + ?*) exec="$exec $1"; shift;; + *) break;; + esac +done + +src_path=${src_path-$default_src_path} + +if [ "x$exec" = x ] ; then + exit 0 +fi +if [ "x$dest" = x ] ; then + echo 1>&2 $0: no destination directory given; echo 1>&2 "$usage"; exit 1 +fi + + +# +# 2. Data Initialization +# + +$verbose -n 2>&1 "Initializing data objects... " + +# Temporary directory. Here is a race condititon to fix! + +fl_dir="/tmp/,mklibs.$$" +mkdir $fl_dir + +trap "rm -fr $fl_dir" EXIT + +# Intialize our symbol array and library queue with the information +# from the executables. + +get-undefined-symbols $exec > $fl_dir/undefined-symbols +add-to-queue-if-not-there $fl_dir/library-depends `get-library-depends $exec` + +$verbose 2>&1 "done." + +# +# 3.a Graph Construction +# +# Build the dependency graph, add new library dependencies to the queue on +# the way. +# If the soname is a link, add the target to the end of the queue and +# add a simple arrow to the graph. +# If the soname is a real lib, get its dependencies and add them to +# the queue. Furthermore, add arrows to the graph. If the lib is not +# dependant on any other lib, add the node to make sure it is mentioned +# at least once in the graph. + +$verbose -n 2>&1 "Constructing dependency graph... (" + +while cur_lib=`get-top-of-queue $fl_dir/library-depends` +do + lib=`find-file $src_path $cur_lib` + if [ -L "$lib" ] ; then + $verbose -n 2>&1 L + lib=`basename \`readlink $lib\`` + add-to-queue-if-not-there $fl_dir/library-depends "$lib" + add-arrow $fl_dir/dependency-graph "$cur_lib" "$lib" + else + get-library-depends "$lib" > $fl_dir/backup + if [ "x`head -1 $fl_dir/backup`" = x ] ; then + $verbose -n 2>&1 N + add-node $fl_dir/dependency-graph "$cur_lib" + else + $verbose -n 2>&1 A + for lib in `cat $fl_dir/backup` ; do + add-to-queue-if-not-there $fl_dir/library-depends "$lib" + add-arrow $fl_dir/dependency-graph "$cur_lib" "$lib" + done + fi + fi +done + +$verbose 2>&1 ") done." + +# +# 3.b Graph Reduction +# +# Find and shrink cycles in the graph. + +$verbose -n 2>&1 "Eliminating cycles... (" + +while cycle=`find-cycle "$fl_dir/dependency-graph"` ; do + $verbose -n 2>&1 C + shrink-nodes "$fl_dir/dependency-graph" $cycle +done + +$verbose 2>&1 ") done." + +# +# 4. Library Installation +# +# Let tsort(1) do the actual work on the cycle-free graph. + +tsort $fl_dir/dependency-graph > $fl_dir/backup + +# Now the ordered list of libraries (or cycles of them) +# can be processed by install-libs. This is indeed the last step. + +install-libs `cat $fl_dir/backup` + +#sort -u $fl_dir/provided-symbols > $fl_dir/diag1 +#sort -u $fl_dir/undefined-symbols > $fl_dir/diag2 +#cat $fl_dir/diag1 $fl_dir/diag2 | sort | uniq -u > $fl_dir/diag3 +## diag3 has now the symmetric difference. +#cat $fl_dir/diag3 $fl_dir/diag2 | sort | uniq -d > $fl_dir/diag1 +## diag1 has now all undefined symbols that are not provided. +##cat $fl_dir/diag1 | wc +## Note that some of these symbols are weak and not having them is probably +## not an error. + +exit 0 + diff --git a/sys-apps/yard/files/yard-2.2/sbin/reduce_libs_root_fs b/sys-apps/yard/files/yard-2.2/sbin/reduce_libs_root_fs new file mode 100644 index 000000000000..87b0be1f58aa --- /dev/null +++ b/sys-apps/yard/files/yard-2.2/sbin/reduce_libs_root_fs @@ -0,0 +1,8 @@ +#!/bin/sh + +find /mnt/floppy -path *bin/* -type f -print | \ + xargs mklibs.sh --verbose \ + --dest-dir /mnt/floppy/lib \ + /mnt/floppy/lib/libnss_files.so.2 \ + /mnt/floppy/lib/security/pam_permit.so + diff --git a/sys-apps/yard/files/yard-2.2/sbin/write_rescue_disk b/sys-apps/yard/files/yard-2.2/sbin/write_rescue_disk new file mode 100644 index 000000000000..9b1642df164e --- /dev/null +++ b/sys-apps/yard/files/yard-2.2/sbin/write_rescue_disk @@ -0,0 +1,566 @@ +#! /usr/bin/perl +# -*- Mode: Perl -*- +# This script created automatically from scripts/write_rescue_disk.in +# $Header: /var/cvsroot/gentoo-x86/sys-apps/yard/files/yard-2.2/sbin/write_rescue_disk,v 1.1 2002/08/25 20:25:33 aliz Exp $ +############################################################################## +## +## WRITE_RESCUE_DISK +## Copyright (C) 1996,1997,1998 Tom Fawcett (fawcett@croftj.net) +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +## +############################################################################## +use strict; +use File::Basename; +use File::Find; +use File::Path; +use FileHandle; +use Cwd; +use English; +use lib "/etc/yard", "/usr/lib/yard"; +use yardconfig; + +BEGIN { require "yard_utils.pl" } + +require "Config.pl"; + +STDOUT->autoflush(1); + +start_logging_output(); +print "write_rescue_disk 2.0\n"; + +if ($CFG::disk_set !~ /^(single|double|base\+extra)$/) { + error "Config variable disk_set is set to \"$CFG::disk_set\"\n", + "which is not a valid value.\n"; +} + +############################################################################## +###### Global variables used in this file +############################################################################## +my($kernel_fs_blocks); # Number of blocks taken up by kernel + # on floppy, whether raw or within fs. +my($root_start); # Where the root starts on the floppy +my($rootfsz_blocks); # Number of blocks required by compressed root +my($seek_clause); # 'Seek' clause for dd root + + + +############################################################################## +##### Check a few things before starting. ##### +############################################################################## +$REAL_USER_ID == 0 or die "This script must be run as root!\n"; + +# Check mount point +if (-d $CFG::mount_point and -w $CFG::mount_point) { + info 1, "Using $CFG::mount_point as the floppy mount point\n"; +} else { + error "Mount point $CFG::mount_point must be a directory and\n", + "must be write-enabled.\n"; +} + +# This test is slightly pointless; an uncompressed kernel these days is +# much too large to fit anyway. +if (!-r $CFG::kernel) { + error "Can't read kernel file $CFG::kernel\n"; +} elsif (`file $CFG::kernel` =~ /executable/) { + error "It looks like the kernel you specified ($CFG::kernel)\n", + "is uncompressed. It should be the compressed version.\n"; +} + +# If user has set $CFG::double_disk_set, convert value +# to appropriate $CFG::disk_set setting. +if (!defined($CFG::disk_set) and defined($CFG::double_disk_set)) { + if ($CFG::double_disk_set == 1) { + $CFG::disk_set = "double"; + } elsif ($CFG::double_disk_set == 0) { + $CFG::disk_set = "single"; + } else { + error "Value of \$CFG::double_disk_set is $CFG::double_disk_set\n", + "Should be 1 or 0\n"; + } +} + + +# We only come back up here if we discover that there is not enough +# space on a single-disk rescue set, and the user has agreed to try +# a two-disk rescue set. +RESTART: + +if (&rootfsz_up_to_date) { + info 0, "***** There is an existing $CFG::rootfsz file, and\n", + " it appears that no relevant files have changed since\n", + " $CFG::rootfsz was created.\n", + " Using this $CFG::rootfsz file as root fs\n"; +} else { + compress_root(); +} + +#### At this point, the root filesystem has been copied and compressed +#### and resides in $CFG::rootfsz. $CFG::mount_point is free. +info 0, "Flushing $CFG::device buffer cache.\n"; +flush_device_buffer_cache($CFG::device); + +load_mount_info(); + +##### Dereference $CFG::device in case it's a symbolic link +while (-l $CFG::device) { + ($CFG::device = readlink($CFG::device)) + or die "Can't resolve CFG::device\n"; +} + + +if (defined($::mounted{$CFG::floppy})) { + error "Floppy device $CFG::floppy is mounted. It shouldn't be in use.\n", + "Unmount $mounted::{$CFG::floppy}, remove the disk and insert a fresh one.\n", + "Do NOT mount it.\n"; + +} elsif (defined($::mounted{$CFG::mount_point})) { + if ($::mounted{$CFG::device} eq $CFG::mount_point) { + sys("umount $CFG::mount_point"); + } else { + error "Some other device is already mounted on $CFG::mount_point\n", + "Unmount it before running write_rescue_disk.\n"; + } +} + +info 0, "Rescue disk set: $CFG::disk_set\n"; + +##### Major control branch here. Transfer the kernel to the floppy +##### either using Lilo or not. + +if ($CFG::use_lilo) { + setup_kernel_using_lilo(); +} else { + setup_kernel_raw(); +} + +##### At this point, kernel is rdev'd with root start address and +##### $kernel_fs_blocks is set. +##### Check remaining space. + +$rootfsz_blocks = bytes_to_K((-s $CFG::rootfsz)); +info 1, "Compressed root filesystem is $rootfsz_blocks blocks.\n"; + +if ($CFG::disk_set eq "double") { ########## DOUBLE DISK SET + if ($rootfsz_blocks > $CFG::floppy_capacity - 1) { + error "compressed root fs ($rootfsz_blocks blocks)", + "> space on floppy (", $CFG::floppy_capacity - 1, ")\n"; + } else { + sync(); + info 0, "\n\aRemove the floppy disk from the drive.\n", + "Label it BOOT DISK.\n\n"; + insert_fresh_floppy_in_drive(); + info 1, "Proceeding with root disk...\n"; + transfer_sentinel(); + $root_start = 1; + } + +} else { ########## SINGLE DISK + if ($kernel_fs_blocks + $rootfsz_blocks > $CFG::floppy_capacity) { + info 0, "Kernel fs ($kernel_fs_blocks K) + compressed root fs ", + "($rootfsz_blocks K) > floppy capacity ", + "($CFG::floppy_capacity)\n"; + ##### See whether rootfsz_blocks would fit on another disk. + ##### if so, offer to switch to a two-disk rescue set. + + if ($rootfsz_blocks <= $CFG::floppy_capacity - 1 + and $CFG::disk_set eq "single") { + + info 0, "\aBut the compressed root fs will fit on a second disk.\n", + "Do you want to try a two-disk set now?\n"; + if (<STDIN> =~ /^y/i) { + info 0, "OK, retrying...\n"; + ##### NB. We must start over because the kernel must be + ##### rdev'd with the new root fs location. + + $CFG::disk_set = "double"; + goto RESTART; # yechh, spaghetti code + + } else { # User didn't say yes. + error "Aborting $0\n"; + } + } else { # Root fs too large -- no way to continue. + error "and your compressed root fs is too large ", + "($rootfsz_blocks K)\nto fit on a second disk.", + " Trim down your file set or go to a larger disk.\n"; + } + } else { + + $root_start = $kernel_fs_blocks; + + info 0, "Kernel fs needs blocks 0-", $kernel_fs_blocks-1, + ", so root filesystem will begin at block $root_start\n", + "Kernel + filesystem = ", $kernel_fs_blocks + $rootfsz_blocks, + " blocks = ", + int(($kernel_fs_blocks + $rootfsz_blocks) / $CFG::floppy_capacity + * 100), + "% of floppy capacity\n"; + } +} + +info 0, "Transferring compressed root filesystem ($CFG::rootfsz) to floppy \n"; +sync(); +sys("dd if=$CFG::rootfsz of=$CFG::floppy bs=1k seek=$root_start"); +sync(); + +sleep 2; +info 0, "\a\n\nTransfer completed successfully.\n\n"; + +if ($CFG::disk_set eq "single") { + info 0, "Remove the floppy disk from the drive\n", + "This is a complete rescue disk.\n"; + +} elsif ($CFG::disk_set eq "double") { + info 0, "Remove the second floppy disk from the drive\n", + "and label it ROOT DISK.\n", + "During the boot process you will be prompted for this\n", + "second disk after the kernel has been loaded from the first.\n"; + +} elsif ($CFG::disk_set eq "base+extra") { + info 0, "Remove the floppy disk from the drive and label it BOOT DISK.\n", + "This will be the first disk to load.\n", + "The other disk, which you've already prepared,\n", + "will be loaded second after the kernel has booted.\n"; +} + +exit(0); + + +############################################################################## +# A typical LILO FS: +# +# total 361 +# 1 drwxr-xr-x 2 root root 1024 Jan 10 07:23 boot/ +# 1 drwxr-xr-x 2 root root 1024 Jan 10 07:22 dev/ +# 1 -rw-r--r-- 1 root root 176 Jan 10 07:22 lilo.conf +# 358 -rw-r--r-- 1 root root 362707 Jan 10 07:23 vmlinuz +# boot: +# total 8 +# 4 -rw-r--r-- 1 root root 3708 Jan 10 07:22 boot.b +# 4 -rw------- 1 root root 3584 Jan 10 07:23 map +# dev: +# total 0 +# 0 brw-r----- 1 root root 2, 0 Jan 10 07:22 fd0 +# 0 crw-r--r-- 1 root root 1, 3 Jan 10 07:22 null +# +sub setup_kernel_using_lilo { + + info 0, "Creating kernel filesystem for Lilo\n"; + + ##### Contants for use with EXT2 + my($inode_allocation) = 8192; # bytes requested per inode + + my($blocks_for_kernel) = bytes_to_K(-s $CFG::kernel); + # We have to guess how big the other files are going to be. + # We also need additional space for lilo to work with. + my($blocks_for_other_files) = 20; + + $kernel_fs_blocks = $blocks_for_kernel + $blocks_for_other_files; + $kernel_fs_blocks += bytes_to_K($::INODE_SIZE * ($kernel_fs_blocks * 1024 / + $inode_allocation)); + + insert_fresh_floppy_in_drive(); + + sys("mke2fs -b 1024 -i $inode_allocation -m 0 $CFG::floppy " + . $kernel_fs_blocks); + sys("mount -t ext2 $CFG::floppy $CFG::mount_point"); + sys("rm -rf $CFG::mount_point/lost+found"); + + info 0, "Copying kernel $CFG::kernel to kernel fs on $CFG::floppy\n"; + ## This is slightly bad -- we shouldn't be mucking in the CFG + ## package, but this var is necessary for c_f_w_s. + local($CFG::kernel_basename) = basename($CFG::kernel); + sys("cp $CFG::kernel $CFG::mount_point/"); + + ##### Put the mount point on the floppy, and /dev/null for lilo + sys("cp --parents -R $CFG::floppy $CFG::mount_point"); + sys("cp --parents -R /dev/null $CFG::mount_point"); + + ##### Set up lilo + mkdir("$CFG::mount_point/boot", 0777) or error "mkdir: $!"; + sys("cp /boot/boot.b $CFG::mount_point/boot"); + + my($lilo_conf) = resolve_file("./Replacements/etc/lilo.conf"); + copy_file_with_substitution($lilo_conf, "$CFG::mount_point/lilo.conf"); + + sys("lilo -v -v -C lilo.conf -r $CFG::mount_point"); + + set_ramdisk_word("$CFG::mount_point/$CFG::kernel_basename", + $kernel_fs_blocks); + + sys("umount $CFG::mount_point"); + +} ## End of setup_kernel_using_lilo + + +sub setup_kernel_raw { + + ##### These are returned at the end: + info 0, "Writing kernel file $CFG::kernel to $CFG::floppy\n"; + sync(); + insert_fresh_floppy_in_drive(); + sys("dd if=$CFG::kernel of=$CFG::floppy bs=8192"); + sync(); + + $kernel_fs_blocks = bytes_to_K(-s $CFG::kernel); + + set_ramdisk_word($CFG::floppy, $kernel_fs_blocks); + + sys("rdev $CFG::floppy $CFG::floppy"); + sys("rdev -R $CFG::floppy 0"); + sync(); + +}# End of setup_kernel_raw + + + +# SET_RAMDISK_WORD($kernel, $kernel_blocks) +# Sets the ramdisk word in kernel based on configuration options +# and $kernel_blocks. +sub set_ramdisk_word { + my($image_loc, $kernel_blocks) = @_; + + my($prompt_flag); + + if ($CFG::disk_set eq "double") { + $root_start = 1; # Skip a block for sentinel sector + $prompt_flag = $::RAMDISK_PROMPT_FLAG; + + } else { + $root_start = $kernel_blocks; + $prompt_flag = 0; + } + + my($RAMDISK_WORD) = $::RAMDISK_LOAD_FLAG + | $prompt_flag + | ($::RAMDISK_IMAGE_START_MASK & $root_start); + + info 1, "rdev'ing kernel with root fs addr\n"; + info 1, "RAMDISK_WORD = ", sprintf("%X", $RAMDISK_WORD), "\n"; + sync(); + sys("rdev -r $image_loc $RAMDISK_WORD"); + sync(); +} + +# TRANSFER_SENTINEL - transfer the boot sector sentinel to the second disk. +sub transfer_sentinel { + my($sentinel_sector) = "$lib_dest/extras/bsect.b"; + + if (!-e $sentinel_sector) { + error "Sentinel boot sector $sentinel_sector does not exist!\n" + + } else { + sys("dd if=$sentinel_sector of=$CFG::floppy bs=1k") + } +} + +# Return 0 if the contents file or any of the Replacements files are +# newer than rootfsz, else 1. +sub rootfsz_up_to_date { + return(0) unless -e $CFG::rootfsz; + my($rootfsz_age) = -C $CFG::rootfsz; + my($any_newer) = 0; + return(0) unless $rootfsz_age < -C $CFG::contents_file; + + sub any_newer + { if ($rootfsz_age > -C $File::Find::name) + { $any_newer = 1 } + }; + find(\&any_newer, $CFG::mount_point); + + $any_newer ? 0 : 1; +} + + + + +sub insert_fresh_floppy_in_drive { + info 0, "Insert a new, write-enabled, ${CFG::floppy_capacity}K floppy into", + " the drive.\n"; + info 0, "Press RETURN when ready.\n"; + scalar(<STDIN>); # read and discard + while (system("dd if=/dev/zero of=$CFG::floppy count=1 bs=1 >/dev/null 2>&1")) { + warn "\a**** Drive $CFG::floppy does not contain a write-enabled diskette\n"; + warn "**** Fix this and press RETURN\n"; + scalar(<STDIN>); + } +} + + +sub compress_root { + ##### Make sure $CFG::device isn't already mounted + load_mount_info(); + if (defined($::mounted{$CFG::device})) { + if ($::mounted{$CFG::device} ne $CFG::mount_point) { + error "Device $CFG::device is already mounted on ", + $::mounted{$CFG::device}, "\n", + "Are you sure it contains the root filesystem?\n"; + } + } elsif (defined($::mounted{$CFG::mount_point})) { + error "Another device ($::mounted{$CFG::mount_point}) is already mounted", + " on $CFG::mount_point\n", + "Unmount it.\n"; + + } else { + + ## Mount the root filesystem one last time. This accomplishes + ## two things: We can get a ls-alR listing for future reference, + ## and it checks that the root filesystem hasn't been trashed at + ## some point along the way (if so, the mount will fail). + + info 0, "Mounting $CFG::device on $CFG::mount_point\n"; + &mount_device(); + } + + info 1, "Listing filesystem contents\n"; + my($contents_base) = basename($CFG::contents_file); + my($logfile_dir) = ($CFG::yard_temp or getcwd()); + my($ls_file) = "${logfile_dir}/${contents_base}.ls"; + sys("cd $CFG::mount_point; ls -alR > $ls_file"); + info 0, "Listing of rootdisk is on $ls_file\n"; + + ## Siphon off base files if necessary + + if ($CFG::disk_set eq "base+extra") { + #### FIX THIS + patch_rc_to_load_extra(); + prepare_extra_disks(); + } + + sys("umount $CFG::mount_point"); + info 0, "Compressing root filesystem on $CFG::device to $CFG::rootfsz\n"; + sync(); + # Note to myself: + # Can't reroute dd STDERR in this command because of the pipe + sys("dd if=$CFG::device count=$CFG::fs_size bs=1k | gzip -v9 " + . "> $CFG::rootfsz"); + +}##### End of compress_root + +##### END OF WRITE_RESCUE_DISK +__END__ +$Log: write_rescue_disk,v $ +Revision 1.1 2002/08/25 20:25:33 aliz +Add missing files + fixup + +Revision 1.1 2001/04/09 03:01:00 achim +*** empty log message *** + +Revision 1.2 1998/05/23 13:42:57 fawcett +yard-1.15 + + +##### Code graveyard. + +sub prepare_extra_disks { + ### Figure out what has to be left on the base disk + my(@base_dirs) = qw(dev etc proc sbin lib); + push(@other_files, &find_tar_and_dependents); + + print "Otherfiles: @other_files\n"; + + my(%in_base); + @in_base{@base_dirs} = @base_dirs; + my(@dirs) = split(' ', `ls -1 $CFG::mount_point`); + my(@extra_dirs) = grep(!exists($in_base{$_}), @dirs); + my($tmpfile) = "/tmp/yard$$"; + + info 0, "Creating \"extra\" disk with dirs: @extra_dirs\n"; + sys("cd $CFG::mount_point; tar " . + join(' ', map("--exclude=$_ ", @other_files)) . + " --remove " . + " -czf $tmpfile @extra_dirs"); + insert_fresh_floppy_in_drive(); + sys("dd if=$tmpfile of=$CFG::floppy"); + + sync(); + info 0, "Done with this disk. This will be the second disk to load.\n"; +} + + +sub patch_rc_to_load_extra { + + # Grab name of rc script, just for propriety + my($INITTAB) = "$CFG::mount_point/etc/inittab"; + my($rc, $rc_text); + + open(INITTAB, "<$INITTAB") or error "$INITTAB: $!\n"; + while (<INITTAB>) { + chomp; + next if /^\#/ or /^\s*$/; + my($code, $runlevels, $action, $command) = split(':'); + if ($action eq 'sysinit') { + info 0, "Found sysinit command file $command\n"; + $rc = "$CFG::mount_point$command"; + last; + } + } + close(INITTAB); + + $ldconfig_path = `cd $CFG::mount_point ; find -name ldconfig -print`; + chomp($ldconfig_path); + $ldconfig_path =~ s|^\.||; # Remove leading dot + + open(RC, "<$rc") or error "$rc: $!"; + print "Patching $rc\n"; + { local($INPUT_RECORD_SEPARATOR); + $rc_text = <RC>; + }; + close(RC); + + open(RC, ">$rc") or error "$rc: $!"; + print RC "$ldconfig_path\n"; + print RC "echo Insert second floppy into drive and press RETURN\n"; + print RC ": \\$<\n"; + print RC "tar xvzf $CFG::floppy\n"; + print RC $rc_text, "\n$ldconfig_path\n"; + close(RC) or die "Writing $rc: $!"; +} + + +##### Finds the tar executable +sub find_tar_and_dependents { + my(@all_files); + ##### Find tar. For a "base+extras" disk, this was added to the + ##### root by make_root_fs, so we must find it now. + my($tar) = `find $CFG::mount_point -name tar -print`; + chomp($tar); + if (!$tar) { + error "tar executable is not on root fs"; + } else { + push(@all_files, $tar); + } + + ##### Now find dependents. Tar usually only needs libc, but we may + ##### as well be careful. + foreach $line (`ldd $tar`) { + my($rel_lib, $abs_lib) = $line =~ /(\S+) => (\S+)/; + next unless $abs_lib; + my($final) = $abs_lib; + + if ($abs_lib =~ /not found/) { + error "File $tar needs library $rel_lib, which does not exist!"; + } else { + my(@libs) = `find $CFG::mount_point -name \'$rel_lib*\' -print`; + chomp(@libs); + foreach (@libs) { s/(\.so).*$/$1*/ }; + push(@all_files, @libs); + } + } + + info 0, "Moving files to base disk: @all_files\n"; + foreach (@all_files) { s|^$CFG::mount_point/(.*)$|$1| }; + @all_files +} |