mirror of
				https://github.com/fspc/gbootroot.git
				synced 2025-11-03 16:05:34 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			297 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			Perl
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			297 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			Perl
		
	
	
		
			Executable File
		
	
	
	
	
#!/usr/bin/perl -w
 | 
						|
 | 
						|
#############################################################################
 | 
						|
##
 | 
						|
##  YARD_CHROOT_TEST
 | 
						|
##  Code from CHECK_ROOT_FS by Tom Fawcett
 | 
						|
##  Copyright (C) 1996,1997,1998  Tom Fawcett (fawcett@croftj.net)
 | 
						|
##  Copyright (C) 2000, 2001 Modifications by Jonathan Rosenbaum
 | 
						|
##                                           <freesource@users.sourceforge.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.
 | 
						|
##
 | 
						|
#####
 | 
						|
##
 | 
						|
##  The purpose of this program is to run chrooted processes separately from
 | 
						|
##  gBootRoot so that gBootRoot can continue to operate on "/", not on the 
 | 
						|
### chroot "/".
 | 
						|
##
 | 
						|
##############################################################################
 | 
						|
 | 
						|
use strict;
 | 
						|
use BootRoot::Yard;
 | 
						|
use File::Path;
 | 
						|
use File::Find;
 | 
						|
 | 
						|
my $login_binary;
 | 
						|
my $mount_point = $ARGV[0];
 | 
						|
 | 
						|
my %checked;
 | 
						|
my $checked_for_getty_files;
 | 
						|
 | 
						|
which_test();
 | 
						|
 | 
						|
sub which_test {
 | 
						|
 | 
						|
    my $test_fstab   = $ARGV[1];
 | 
						|
    my $test_inittab = $ARGV[2];
 | 
						|
    my $test_scripts = $ARGV[3];
 | 
						|
 | 
						|
    if ( $test_fstab   == 1 ) {
 | 
						|
	print "\nTEST: fstab";
 | 
						|
	fork_chroot_and(\&check_fstab);   
 | 
						|
    }
 | 
						|
    if ( $test_inittab == 1 ) {
 | 
						|
	print "\nTEST: inittab";
 | 
						|
	fork_chroot_and(\&check_inittab); 
 | 
						|
    }
 | 
						|
    if ( $test_scripts == 1 ) {
 | 
						|
	print "\nTEST: scripts";
 | 
						|
	fork_chroot_and(\&check_scripts);
 | 
						|
    }
 | 
						|
} # end sub which_test
 | 
						|
 | 
						|
#  This takes a procedure call, forks off a subprocess, chroots to
 | 
						|
#  $mount_point and runs the procedure.
 | 
						|
sub fork_chroot_and {
 | 
						|
   my($call) = @_;
 | 
						|
 | 
						|
   my($Godot) = fork;
 | 
						|
 | 
						|
   unless (defined $Godot) {
 | 
						|
 
 | 
						|
      die "Can't fork: $!"; 
 | 
						|
   }
 | 
						|
 | 
						|
   if (!$Godot) {
 | 
						|
      # Child process
 | 
						|
      chdir($mount_point);
 | 
						|
      chroot($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_test ("$FSTAB: $!");
 | 
						|
  if (-z $FSTAB) {
 | 
						|
      error_test ("fstab is an empty file");
 | 
						|
  }
 | 
						|
  print "\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) {
 | 
						|
	  print "$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 $::floppy and $type ne 'ext2' and $type ne 'auto') {
 | 
						|
	  warning("\t($.): You've declared your floppy drive $::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;
 | 
						|
  print "Done with $FSTAB\n";
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
sub check_inittab {
 | 
						|
  my($INITTAB) =  "/etc/inittab";
 | 
						|
  print "\nChecking $INITTAB\n";
 | 
						|
 | 
						|
  if (!open(INITTAB, "<$INITTAB")) {
 | 
						|
     warning("$INITTAB: $!\n");
 | 
						|
     return
 | 
						|
  }
 | 
						|
  if (-z $INITTAB) {
 | 
						|
      error_test ("fstab is an empty file");
 | 
						|
  }
 | 
						|
 | 
						|
  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) {
 | 
						|
	  print "$INITTAB($.): $line\n";
 | 
						|
	print "\tMaking $exec executable\n";
 | 
						|
	chmod(0777, $exec) or error_test("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_test("close(INITTAB): $!");
 | 
						|
 | 
						|
  if (!$saw_line_for_default_rl) {
 | 
						|
    warning("\tDefault runlevel is $default_rl, but no entry for it.\n");
 | 
						|
  }
 | 
						|
  print "Done with $INITTAB\n";
 | 
						|
}
 | 
						|
 | 
						|
sub check_scripts {
 | 
						|
  print "\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_test("$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, "/");
 | 
						|
}
 | 
						|
 | 
						|
#####  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};
 | 
						|
  print "\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;
 | 
						|
  print "Done scanning $cmdfile\n";
 | 
						|
 | 
						|
} # end sub scan_command_file
 | 
						|
 | 
						|
sub check_getty_type_call {
 | 
						|
  my($prog, @args) = @_;
 | 
						|
 | 
						|
  if ($prog eq 'getty') {
 | 
						|
    my($tty, $speed, $type) = @args;
 | 
						|
 | 
						|
    if (!-e "$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 "$mount_point/etc/gettydefs";
 | 
						|
      warning ("\tLine $.: $prog expects /etc/issue, which is missing.\n")
 | 
						|
	unless -e "$mount_point/etc/issue";
 | 
						|
      $checked_for_getty_files = 1;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
} # end sub check_getty_type_call
 | 
						|
 | 
						|
sub warning {
 | 
						|
    print "\n", @_;                                                   
 | 
						|
} 
 | 
						|
 | 
						|
sub error_test {
 | 
						|
  print STDERR "\nError: ", @_, "\n";                             
 | 
						|
  exit(-1);                                              
 | 
						|
} 
 |