#!/usr/bin/perl -w # liloconfig - creating a new lilo.conf file # # Copyright 2011 Joachim Wiedorn # # 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., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301, USA. #---- some modules use strict; use warnings; use Getopt::Std; use Pod::Usage; use File::Copy; #---- global variables my $prog = $0; $prog =~ s#.*/##; my $version = "0.1"; #---- parameter check # h: help, v: verbose, f: force our $opt_h = 0; our $opt_v = 0; our $opt_f = 0; getopts('hvf'); # define perldoc usage pod2usage(1) if $opt_h; #---- other variables our $liloconf = "/etc/lilo.conf"; our $conftmp_1 = "/tmp/lilotmp1"; our $conftmp_2 = "/tmp/lilotmp2"; our $liloconfold = $liloconf . ".old"; our $liloconfnew = $liloconf . ".new"; our $fstabconf = "/etc/fstab"; our $idpath = "/dev/disk/by-id"; our $uuidpath = "/dev/disk/by-uuid"; our $lblpath = "/dev/disk/by-label"; our $template = "/usr/share/doc/lilo/examples/lilo.example.conf.gz"; our $rootpart; # found root part our $root_dev; # /dev/hdX9, /dev/sdX9, /dev/md/* our $root_id; # UUID, LABEL, ID our $boot_dev; # /dev/hdX, /dev/sdX, /dev/md our $boot_id; # DISK-ID #-------------------- main program -------------------- sub main { my $exit = 0; if (@ARGV == 1) { $liloconf = "$ARGV[0]"; $liloconfold = $liloconf . ".old"; $liloconfnew = $liloconf . ".new"; } if (-f $liloconf and not $opt_f) { print "$prog: $liloconf already exist! Please use '-f' for overwriting.\n"; $exit = 1; } else { $exit = create_lilo_conf() } return $exit; } #-------------------- subroutines -------------------- sub create_lilo_conf { my $found = 0; my $exit = 1; # search for root device in fstab and convert it $found = detect_root_device(); # convert root device to boot device if ($found) { $found = convert_boot_device(); } # finally write new lilo.conf file if ($found) { $exit = write_lilo_conf(); } return $exit; } sub detect_root_device { # read fstab and find root device; my $found = read_fstab(); # identify root device: root_dev and root_id if ($found) { $found = convert_root_device(); } return $found; } sub read_fstab { my $root_part; my $mountpoint; my $broken_fstab = 1; my $base_fstab = 0; my $found = 1; # check fstab for root device if (-f $fstabconf) { # Parsing fstab for the root partition open(FSTAB, "<$fstabconf") or die "$prog: couldn't open $fstabconf: $!\n"; while () { # Search magic string which indicates a base filesystem $base_fstab = 1 if /^# UNCONFIGURED FSTAB FOR BASE SYSTEM/; next if /^#/; # ignore comment lines s/^[ \t]+//; # remove space or tab at begin of the line ($root_part,$mountpoint) = split(/[ \t]+/); next unless defined $mountpoint; # ignore empty lines too # stop if we found the root device... if ($mountpoint eq '/') { $broken_fstab = 0; last; } } close(FSTAB) or die "$prog: couldn't close $fstabconf: $!\n"; } if ($base_fstab) { print "E: It seems you want configure the base filesystem \n" . "and I'm therefore simply going to exit successfully \n" . "without trying to actually configure LILO properly. \n"; $found = 0; } if ($broken_fstab) { print "E: It seems the file /etc/fstab is not properly \n" . "configured: no root partition '/' found! \n"; $found = 0; } # save the found root device $rootpart = $root_part; return $found; } sub convert_root_device { my $found = 1; my $root_disk = ''; my $root_link; # global variables: $root_dev, $root_id if ($rootpart =~ /\/dev\//) { $root_disk = $rootpart; if (-b $root_disk) { $root_dev = $root_disk; if($opt_v) { print "Convert root option $root_disk into UUID\n"; } $root_id = find_id_link($root_disk,$uuidpath); if (not -l "$uuidpath/$root_id") { if($opt_v) { print "W: could not find UUID for $root_disk!\n"; } ## than we want use root_dev in lilo.conf #$found = 0; } else { # finally add uuid label $root_id = "UUID=" . $root_id; } } else { if($opt_v) { print "E: cannot check $root_disk: device does not exist!\n"; } $found = 0; } } elsif ($rootpart =~ /^UUID/ or $rootpart =~ /^LABEL/) { $root_link = $rootpart; $root_link =~ s{\"}{}g; $root_link =~ s{^LABEL=}{/dev/disk/by-label/}; $root_link =~ s{^UUID=}{/dev/disk/by-uuid/}; if (-l $root_link) { $root_id = $rootpart; $root_disk = readlink($root_link); $root_disk =~ s{\.\./\.\./}{/dev/}; if (-b $root_disk) { $root_dev = $root_disk; } else { if($opt_v) { print "E: cannot check $root_link: link does not exist!\n"; } $found = 0; } } else { print "E: cannot check $root_link: link does not exist!\n"; $found = 0; } } else { print "E: cannot use uncommon $rootpart found as root device!\n"; $found = 0; } return $found; } sub find_id_link { my $olddev = $_[0]; my $path_id = $_[1]; my @sellinks; my $_idlink; my $_actlink; my $newdevid = ''; opendir(MYDH, "$path_id") or die("cannot open $path_id: $! \n"); @sellinks = grep(!/\-part\d\d?$/, grep(!/^\.\.?$/, readdir(MYDH))); @sellinks = sort(@sellinks); closedir(MYDH); foreach $_idlink (@sellinks) { chomp $_idlink; if(not $_idlink =~ /^usb/ and length($_idlink) > 10) { $_actlink = readlink("$path_id/$_idlink"); $_actlink =~ s{\.\./\.\./}{/dev/}; if($opt_v) { print "** try: $_actlink => $_idlink \n"; } # stop if we find the right link... if($_actlink eq $olddev) { $newdevid = $_idlink; if($opt_v) { print "** convert: $_actlink => $path_id/$_idlink \n\n"; } last; } } } if(not $newdevid) { if($opt_v) { print "W: $olddev not converted: link not useful\n\n"; } } return ($newdevid); } sub convert_boot_device { my $found = 1; my $boot_disk = ''; my $boot_link; # global variables: $boot_dev, $boot_id if (-b $root_dev) { if ($root_dev =~ /\/dev\/md/) { # search if the found partition is a raid volume $boot_disk = check_raid($root_dev); } else { # find the right block device name $boot_disk = $root_dev; $boot_disk =~ s/\d+$//; } if (-b $boot_disk) { # set global variable boot_dev $boot_dev = $boot_disk; } else { print "E: boot device $boot_disk does not exist! \n"; $found = 0; } } else { print "E: could not find root device $root_dev! \n"; $found = 0; } if ($found) { if($opt_v) { print "Convert boot option $boot_disk into DISK ID\n"; } $boot_id = $idpath . "/" . find_id_link($boot_disk,$idpath); if(not -l "$boot_id") { if($opt_v) { print "W: could not find DISK ID for $boot_disk!\n"; } ## not so important, than using boot_dev in lilo.conf #$found = 0; } } return $found; } sub check_raid { my $part = $_[0]; my $mdname; my $md; my @devices; # check if the found partition is a raid volume if($part =~ /\/dev\/md/) { $mdname = $part; $mdname =~ s/\/dev\///; $mdname =~ s/\///; $md = `grep $mdname /proc/mdstat`; @devices = split(" ", $md); @devices = sort(@devices[4..$#devices]); $part = "/dev/" . $devices[0]; $part =~ s/\[.*$//; } return $part; } sub write_lilo_conf { my @status; my $exit = copy_template(); if (not $exit) { # create lilo.conf.new write_boot_option(); write_image_config(); } if (-f $liloconf and not -f $liloconfold) { # move old lilo.conf to lilo.conf.old @status = stat($liloconf); move ($liloconf, $liloconfold) or die "Cannot rename file: $!\n"; utime ($status[9],$status[9],$liloconfold); chmod (0600,$liloconfold); print "Old file moved to: $liloconfold \n"; } if (-f $liloconfnew) { move ($liloconfnew, $liloconf) or die "Cannot move file: $!\n"; chmod (0600,$liloconf); print "New file created as: $liloconf \n"; print "Now you must execute '/sbin/lilo' to " . "activate this new configuation!\n\n"; } else { print "E: Cannot find temporary file $conftmp_1!\n"; $exit = 1; } return $exit; } sub copy_template { my $endreached = 0; my $exit = 0; # copy template config if (-f $template) { system("gzip -d -c $template >$conftmp_1") if ($template =~ /\.gz$/); system("cat $template >$conftmp_1") if ($template =~ /\.conf$/); open(CONFTMP1, "<$conftmp_1") or die "$prog: couldn't open $conftmp_1: $!\n"; open(CONFTMP2, ">$conftmp_2") or die "$prog: couldn't open $conftmp_2: $!\n"; while () { if (/first\ example/) { $endreached = 1; } unless ($endreached) { print CONFTMP2 $_; } } close(CONFTMP1) or die "$prog: couldn't close $conftmp_1: $!\n";; close(CONFTMP2) or die "$prog: couldn't close $conftmp_2: $!\n";; } else { open(CONFTMP2, ">$conftmp_2") or die "$prog: couldn't open $conftmp_2: $!\n"; print CONFTMP2 "# /etc/lilo.conf ### LILO global section ### #large-memory lba32 boot = /dev/sda map = /boot/map install = menu menu-scheme = Wb:Yr:Wb:Wb prompt timeout = 100 vga = normal #default = Linux ### LILO per-image section ### "; close(CONFTMP2) or die "$prog: couldn't close $conftmp_2: $!\n";; } return $exit; } sub write_boot_option { my $oldline = ''; my $newline = ''; my $ok = 0; open(MYFH_NEW, "> $liloconfnew") or die "Cannot open file: $!"; open(MYFH_TMP, "< $conftmp_2") or die "Cannot read file: $!"; while () { # line read from MYFH_TMP $oldline = $_; if (/^boot/ and $ok == 0) { if ($boot_id) { $oldline = "#boot = $boot_dev\n"; $newline = "boot = $boot_id\n"; print MYFH_NEW $oldline; if($opt_v) { print $oldline; } } else { $oldline = "boot = $boot_dev\n"; } print MYFH_NEW $newline; if($opt_v) { print $newline; } if($opt_v) { print "\n"; } # convert only one time $ok = 1; } else { print MYFH_NEW $oldline; } } close(MYFH_TMP); close(MYFH_NEW); } sub write_image_config { my $image; my $initrd; my $nr; my $nr2; # search for kernel image files my @vmlinuz = readpipe("/bin/ls -t -1 /boot/vmlinuz-2* 2>/dev/null"); # append to new lilo.conf open(MYFH_NEW, ">> $liloconfnew") or die "Cannot open file: $!"; # create some line for each kernel image $nr = 0; foreach $image (@vmlinuz) { # search for kernel initrd file chomp $image; $initrd = $image; $initrd =~ s/vmlinuz/initrd\.img/; $nr2 = $nr + 1; print MYFH_NEW 'image = ' . $image . "\n"; if($opt_v) { print 'image = ' . $image . "\n"; } if ($nr == 0) { print MYFH_NEW "\t" . 'label = "Linux"' . "\n"; if($opt_v) { print "\t" . 'label = "Linux"' . "\n"; } } elsif ($nr == 1) { print MYFH_NEW "\t" . 'label = "Linux Old"' . "\n"; if($opt_v) { print "\t" . 'label = "Linux Old"' . "\n"; } } if ($root_id) { print MYFH_NEW "\t" . '#root = ' . $root_dev . "\n"; if($opt_v) { print "\t" . '#root = ' . $root_dev . "\n"; } print MYFH_NEW "\t" . 'root = "' . $root_id . '"' . "\n"; if($opt_v) { print "\t" . 'root = "' . $root_id . '"' . "\n"; } } else { print MYFH_NEW "\t" . 'root = ' . $root_dev . "\n"; if($opt_v) { print "\t" . 'root = ' . $root_dev . "\n"; } } print MYFH_NEW "\t" . 'read-only' . "\n"; if($opt_v) { print "\t" . 'read-only' . "\n"; } print MYFH_NEW "#\t" . 'restricted' . "\n"; if($opt_v) { print "#\t" . 'restricted' . "\n"; } print MYFH_NEW "#\t" . 'alias = ' . "$nr2" . "\n"; if($opt_v) { print "#\t" . 'alias = ' . "$nr2" . "\n"; } print MYFH_NEW "#\t" . 'optional' . "\n"; if($opt_v) { print "#\t" . 'optional' . "\n"; } if (-f $initrd) { print MYFH_NEW "\t" . 'initrd = ' . $initrd . "\n"; if($opt_v) { print "\t" . 'initrd = ' . $initrd . "\n"; } } print MYFH_NEW "\n"; if($opt_v) { print "\n"; } $nr++; last if ($nr > 1); } close(MYFH_NEW); } main(); __END__ =head1 NAME liloconfig - create new lilo.conf file (with diskid and uuid) =head1 SYNOPSIS liloconfig [-h] [-v] [-f] [lilo.conf] =head1 DESCRIPTION liloconfig is an simple program for creating a new lilo.conf file. After creating the new configuration file you must execute '/sbin/lilo'. liloconfig use the lilo.example.conf file as template. In the final lilo.conf file you find many useful comments for custom changes. =head1 EXAMPLES Lines in the configuration file /etc/lilo.conf: ### LILO global section ### #large-memory lba32 boot = /dev/sda map = /boot/map install = menu menu-scheme = Wb:Yr:Wb:Wb prompt timeout = 100 vga = normal #default = Linux ### LILO per-image section ### #boot = /dev/sda boot = /dev/disk/by-id/ata-SAMSUNG_SV1604N_S01FJ10X999999 image = /boot/vmlinuz-2.6.32-5book-686 label = "Linux" #root = /dev/sda1 root = "UUID=18843936-00f9-4df0-a373-000d05a5dd44" read-only # restricted # alias = 1 # optional initrd = /boot/initrd.img-2.6.32-5book-686 image = /boot/vmlinuz-2.6.32-5-686 label = "Linux Old" #root = /dev/sda1 root = "UUID=18843936-00f9-4df0-a373-000d05a5dd44" read-only # restricted # alias = 2 # optional initrd = /boot/initrd.img-2.6.32-5-686 =head1 OPTIONS =over 4 =item B<-h> Print a brief help. =item B<-v> Print verbose messages. =item B<-f> Force overriding existing lilo.conf. =back =head1 AUTHOR B was written by Joachim Wiedorn. =cut