Add new script liloconfig
[rrq/maintain_lilo.git] / debian / scripts / liloconfig
1 #!/usr/bin/perl -w
2
3 #       liloconfig -  creating a new lilo.conf file
4 #       
5 #       Copyright 2011 Joachim Wiedorn <ad_debian@joonet.de>
6 #       
7 #       This program is free software; you can redistribute it and/or modify
8 #       it under the terms of the GNU General Public License as published by
9 #       the Free Software Foundation; either version 2 of the License, or
10 #       (at your option) any later version.
11 #       
12 #       This program is distributed in the hope that it will be useful,
13 #       but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #       GNU General Public License for more details.
16 #       
17 #       You should have received a copy of the GNU General Public License
18 #       along with this program; if not, write to the Free Software
19 #       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 #       MA 02110-1301, USA.
21
22 #---- some modules
23 use strict;
24 use warnings;
25 use Getopt::Std;
26 use Pod::Usage;
27 use File::Copy;
28
29
30 #---- global variables
31 my $prog = $0;
32 $prog =~ s#.*/##;
33 my $version = "0.1";
34
35 #---- parameter check
36 # h: help, v: verbose, f: force
37 our $opt_h = 0;
38 our $opt_v = 0;
39 our $opt_f = 0;
40 getopts('hvf');
41 # define perldoc usage
42 pod2usage(1) if $opt_h;
43
44 #---- other variables
45 our $liloconf  = "/etc/lilo.conf";
46 our $conftmp_1 = "/tmp/lilotmp1";
47 our $conftmp_2 = "/tmp/lilotmp2";
48 our $liloconfold = $liloconf . ".old";
49 our $liloconfnew = $liloconf . ".new";
50 our $fstabconf = "/etc/fstab";
51
52 our $idpath = "/dev/disk/by-id";
53 our $uuidpath = "/dev/disk/by-uuid";
54 our $lblpath = "/dev/disk/by-label";
55 our $template = "/usr/share/doc/lilo/examples/lilo.example.conf.gz";
56
57 our $rootpart;    # found root part
58 our $root_dev;    # /dev/hdX9, /dev/sdX9, /dev/md/*
59 our $root_id;     # UUID, LABEL, ID
60 our $boot_dev;    # /dev/hdX, /dev/sdX, /dev/md
61 our $boot_id;     # DISK-ID
62
63 #-------------------- main program --------------------
64
65 sub main {
66
67         my $exit = 0;
68
69         if (@ARGV == 1) {
70                 $liloconf = "$ARGV[0]";
71                 $liloconfold = $liloconf . ".old";
72                 $liloconfnew = $liloconf . ".new";
73         }
74         if (-f $liloconf and not $opt_f) {
75                 print "$prog: $liloconf already exist! Please use '-f' for overwriting.\n";
76                 $exit = 1;
77         }
78         else {
79                 $exit = create_lilo_conf()
80         }               
81         return $exit;
82 }
83
84 #-------------------- subroutines --------------------
85
86 sub create_lilo_conf {
87         
88         my $found = 0;
89         my $exit = 1;
90
91         # search for root device in fstab and convert it
92         $found = detect_root_device();
93
94         # convert root device to boot device
95         if ($found) { $found = convert_boot_device(); }
96
97         # finally write new lilo.conf file
98         if ($found) { $exit = write_lilo_conf(); }
99
100         return $exit;
101 }
102
103 sub detect_root_device {
104         
105         # read fstab and find root device; 
106         my $found = read_fstab();
107
108         # identify root device: root_dev and root_id
109         if ($found) { $found = convert_root_device(); }
110         
111         return $found;
112 }
113
114 sub read_fstab {
115         
116         my $root_part;
117         my $mountpoint;
118         my $broken_fstab = 1;
119         my $base_fstab = 0;
120         my $found = 1;
121
122         # check fstab for root device
123         if (-f $fstabconf) {
124                 # Parsing fstab for the root partition
125                 open(FSTAB, "<$fstabconf") or die "$prog: couldn't open $fstabconf: $!\n";
126
127                 while (<FSTAB>) {
128                         # Search magic string which indicates a base filesystem
129                         $base_fstab = 1 if /^# UNCONFIGURED FSTAB FOR BASE SYSTEM/;
130                         next if /^#/;     # ignore comment lines
131
132                         s/^[ \t]+//;      # remove space or tab at begin of the line
133                         ($root_part,$mountpoint) = split(/[ \t]+/);
134                         next unless defined $mountpoint;    # ignore empty lines too
135
136                         # stop if we found the root device...
137                         if ($mountpoint eq '/') {
138                                 $broken_fstab = 0;
139                                 last;
140                         }
141                 }
142                 close(FSTAB) or die "$prog: couldn't close $fstabconf: $!\n";
143         }
144
145         if ($base_fstab) {
146                 print "E: It seems you want configure the base filesystem \n" .
147                       "and I'm therefore simply going to exit successfully \n" .
148                       "without trying to actually configure LILO properly. \n";
149                 $found = 0;
150         }
151         if ($broken_fstab) {
152                 print "E: It seems the file /etc/fstab is not properly \n" .
153                       "configured: no root partition '/' found! \n";
154                 $found = 0;
155         }
156         # save the found root device
157         $rootpart = $root_part;
158
159         return $found;
160 }
161
162 sub convert_root_device {
163         
164         my $found = 1;
165         my $root_disk = '';
166         my $root_link;
167         # global variables: $root_dev, $root_id
168
169         if ($rootpart =~ /\/dev\//) {
170                 $root_disk = $rootpart;
171
172                 if (-b $root_disk) {
173                         $root_dev = $root_disk;
174                         if($opt_v) { print "Convert root option $root_disk into UUID\n"; }
175                         $root_id = find_id_link($root_disk,$uuidpath);
176
177                         if (not -l "$uuidpath/$root_id") {
178                                 if($opt_v) { print "W: could not find UUID for $root_disk!\n"; }
179                                 ## than we want use root_dev in lilo.conf
180                                 #$found = 0;
181                         }
182                         else {
183                                 # finally add uuid label
184                                 $root_id = "UUID=" . $root_id;
185                         }
186                 }
187                 else {
188                         if($opt_v) { print "E: cannot check $root_disk: device does not exist!\n"; }
189                         $found = 0;
190                 }
191         }
192         elsif ($rootpart =~ /^UUID/ or $rootpart =~ /^LABEL/) {
193                 $root_link = $rootpart;
194                 $root_link =~ s{\"}{}g;
195                 $root_link =~ s{^LABEL=}{/dev/disk/by-label/};
196                 $root_link =~ s{^UUID=}{/dev/disk/by-uuid/};
197
198                 if (-l $root_link) {
199                         $root_id = $rootpart;
200                         $root_disk = readlink($root_link);
201                         $root_disk =~ s{\.\./\.\./}{/dev/};
202
203                         if (-b $root_disk) { $root_dev = $root_disk; }
204                         else {
205                                 if($opt_v) { print "E: cannot check $root_link: link does not exist!\n"; }
206                                 $found = 0;
207                         }
208                 }
209                 else {
210                         print "E: cannot check $root_link: link does not exist!\n";
211                         $found = 0;
212                 }
213         }
214         else {
215                 print "E: cannot use uncommon $rootpart found as root device!\n";
216                 $found = 0;
217         }
218
219         return $found;
220 }
221
222 sub find_id_link {
223         
224         my $olddev = $_[0];
225         my $path_id = $_[1];
226         my @sellinks;
227         my $_idlink;
228         my $_actlink;
229         my $newdevid = '';
230
231         opendir(MYDH, "$path_id") or die("cannot open $path_id: $! \n");
232         @sellinks = grep(!/\-part\d\d?$/, grep(!/^\.\.?$/, readdir(MYDH)));
233         @sellinks = sort(@sellinks);
234         closedir(MYDH);
235
236         foreach $_idlink (@sellinks) {
237                 chomp $_idlink;
238                 if(not $_idlink =~ /^usb/ and length($_idlink) > 10) {
239                         $_actlink = readlink("$path_id/$_idlink");
240                         $_actlink =~ s{\.\./\.\./}{/dev/};
241                         if($opt_v) { print "** try: $_actlink => $_idlink \n"; }
242                         
243                         # stop if we find the right link...
244                         if($_actlink eq $olddev) {
245                                 $newdevid = $_idlink;
246                                 if($opt_v) { print "** convert: $_actlink => $path_id/$_idlink \n\n"; }
247                                 last;
248                         }
249                 }
250         }
251
252         if(not $newdevid) {
253                 if($opt_v) { print "W: $olddev not converted: link not useful\n\n"; }
254         }
255         
256         return ($newdevid);
257 }
258
259 sub convert_boot_device {
260
261         my $found = 1;
262         my $boot_disk = '';
263         my $boot_link;
264         # global variables: $boot_dev, $boot_id
265
266         if (-b $root_dev) {
267                 if ($root_dev =~ /\/dev\/md/) {
268                         # search if the found partition is a raid volume
269                         $boot_disk = check_raid($root_dev);
270                 }
271                 else {
272                         # find the right block device name
273                         $boot_disk = $root_dev;
274                         $boot_disk =~ s/\d+$//;
275                 }
276
277                 if (-b $boot_disk) {
278                         # set global variable boot_dev
279                         $boot_dev = $boot_disk;
280                 }
281                 else { 
282                         print "E: boot device $boot_disk does not exist! \n";
283                         $found = 0;
284                 }
285         }
286         else {
287                 print "E: could not find root device $root_dev! \n";
288                 $found = 0;
289         }
290
291         if ($found) {
292                 if($opt_v) { print "Convert boot option $boot_disk into DISK ID\n"; }
293                 $boot_id = $idpath . "/" . find_id_link($boot_disk,$idpath);
294
295                 if(not -l "$boot_id") {
296                         if($opt_v) { print "W: could not find DISK ID for $boot_disk!\n"; }
297                         ## not so important, than using boot_dev in lilo.conf
298                         #$found = 0;
299                 }
300         }
301
302         return $found;
303 }
304
305 sub check_raid {
306         
307         my $part = $_[0];
308         my $mdname;
309         my $md;
310         my @devices;
311
312         # check if the found partition is a raid volume
313         if($part =~ /\/dev\/md/)
314         {
315                 $mdname = $part;
316                 $mdname =~ s/\/dev\///;
317                 $mdname =~ s/\///;
318                 $md = `grep $mdname /proc/mdstat`;
319         
320                 @devices = split(" ", $md);
321                 @devices = sort(@devices[4..$#devices]);
322                 $part = "/dev/" . $devices[0];
323                 $part =~ s/\[.*$//;
324
325         }
326         return $part;
327 }
328
329 sub write_lilo_conf {
330         
331         my @status;
332         my $exit = copy_template();
333
334         if (not $exit) {
335                 # create lilo.conf.new
336                 write_boot_option();
337                 write_image_config();
338         }
339
340         if (-f $liloconf and not -f $liloconfold) {
341                 # move old lilo.conf to lilo.conf.old
342                 @status = stat($liloconf);
343                 move ($liloconf, $liloconfold) or die "Cannot rename file: $!\n";
344                 utime ($status[9],$status[9],$liloconfold);
345                 chmod (0600,$liloconfold);
346                 print "Old file moved to: $liloconfold \n";
347         }
348         if (-f $liloconfnew) {
349                 move ($liloconfnew, $liloconf) or die "Cannot move file: $!\n";
350                 chmod (0600,$liloconf);
351                 print "New file created as: $liloconf \n";
352                 print "Now you must execute '/sbin/lilo' to " . 
353                       "activate this new configuation!\n\n";
354         }
355         else {
356                 print "E: Cannot find temporary file $conftmp_1!\n";
357                 $exit = 1;
358         }
359         
360         return $exit;
361 }
362
363 sub copy_template {
364
365         my $endreached = 0;
366         my $exit = 0;
367         
368         # copy template config
369         if (-f $template) {
370                 system("gzip -d -c $template >$conftmp_1") if ($template =~ /\.gz$/);
371                 system("cat $template >$conftmp_1") if ($template =~ /\.conf$/);
372
373                 open(CONFTMP1, "<$conftmp_1") or die "$prog: couldn't open $conftmp_1: $!\n";
374                 open(CONFTMP2, ">$conftmp_2") or die "$prog: couldn't open $conftmp_2: $!\n";
375
376                 while (<CONFTMP1>) {
377                         if (/first\ example/) {
378                                 $endreached = 1;
379                         }
380                         unless ($endreached) {
381                                 print CONFTMP2 $_;
382                         }
383                 }
384                 close(CONFTMP1) or die "$prog: couldn't close $conftmp_1: $!\n";;
385                 close(CONFTMP2) or die "$prog: couldn't close $conftmp_2: $!\n";;
386         }
387         else {
388                 open(CONFTMP2, ">$conftmp_2") or die "$prog: couldn't open $conftmp_2: $!\n";
389                 print CONFTMP2 "# /etc/lilo.conf
390
391 ### LILO global section ###
392
393 #large-memory
394 lba32
395 boot = /dev/sda
396 map = /boot/map
397 install = menu
398 menu-scheme = Wb:Yr:Wb:Wb
399 prompt
400 timeout = 100
401 vga = normal
402 #default = Linux
403
404 ### LILO per-image section ###
405
406 "; 
407                 close(CONFTMP2) or die "$prog: couldn't close $conftmp_2: $!\n";;
408         }
409         
410         return $exit;
411 }
412
413 sub write_boot_option {
414         
415         my $oldline = '';
416         my $newline = '';
417         my $ok = 0;
418
419         open(MYFH_NEW, "> $liloconfnew") or die "Cannot open file: $!";
420         open(MYFH_TMP, "< $conftmp_2") or die "Cannot read file: $!";
421
422         while (<MYFH_TMP>) {
423                 # line read from MYFH_TMP
424                 $oldline = $_;
425
426                 if (/^boot/ and $ok == 0) {
427                         if ($boot_id) {
428                                 $oldline = "#boot = $boot_dev\n";
429                                 $newline = "boot = $boot_id\n";
430                                 print MYFH_NEW $oldline;
431                                 if($opt_v) { print $oldline; }
432                         }
433                         else {
434                                 $oldline = "boot = $boot_dev\n";
435                         }
436                         print MYFH_NEW $newline;
437                         if($opt_v) { print $newline; }
438                         if($opt_v) { print "\n"; }
439
440                         # convert only one time
441                         $ok = 1;
442                 }
443                 else {
444                         print MYFH_NEW $oldline;
445                 }
446         }
447         close(MYFH_TMP);
448         close(MYFH_NEW);
449 }
450
451 sub write_image_config {
452
453         my $image;
454         my $initrd;
455         my $nr;
456         my $nr2;
457
458         # search for kernel image files
459         my @vmlinuz = readpipe("/bin/ls -t -1 /boot/vmlinuz-2* 2>/dev/null");
460
461         # append to new lilo.conf
462         open(MYFH_NEW, ">> $liloconfnew") or die "Cannot open file: $!";
463
464         # create some line for each kernel image
465         $nr = 0;
466         foreach $image (@vmlinuz) {
467                 # search for kernel initrd file
468                 chomp $image;
469                 $initrd = $image;
470                 $initrd =~ s/vmlinuz/initrd\.img/;
471                 $nr2 = $nr + 1;
472
473                         print MYFH_NEW     'image = ' . $image . "\n";
474                         if($opt_v) { print 'image = ' . $image . "\n"; }
475                 if ($nr == 0) {
476                         print MYFH_NEW     "\t"  . 'label = "Linux"' . "\n";
477                         if($opt_v) { print "\t"  . 'label = "Linux"' . "\n"; }
478                 }
479                 elsif ($nr == 1) {
480                         print MYFH_NEW     "\t"  . 'label = "Linux Old"' . "\n";
481                         if($opt_v) { print "\t"  . 'label = "Linux Old"' . "\n"; }
482                 }
483                 if ($root_id) {
484                         print MYFH_NEW     "\t"  . '#root = ' . $root_dev . "\n";
485                         if($opt_v) { print "\t"  . '#root = ' . $root_dev . "\n"; }
486                         print MYFH_NEW     "\t"  . 'root = "' . $root_id . '"' . "\n";
487                         if($opt_v) { print "\t"  . 'root = "' . $root_id . '"' . "\n"; }
488                 }
489                 else {
490                         print MYFH_NEW     "\t"  . 'root = ' . $root_dev . "\n";
491                         if($opt_v) { print "\t"  . 'root = ' . $root_dev . "\n"; }
492                 }
493                         print MYFH_NEW     "\t"  . 'read-only' . "\n";
494                         if($opt_v) { print "\t"  . 'read-only' . "\n"; }
495                         print MYFH_NEW     "#\t" . 'restricted' . "\n";
496                         if($opt_v) { print "#\t" . 'restricted' . "\n"; }
497                         print MYFH_NEW     "#\t" . 'alias = ' . "$nr2" . "\n";
498                         if($opt_v) { print "#\t" . 'alias = ' . "$nr2" . "\n"; }
499                         print MYFH_NEW     "#\t" . 'optional' . "\n";
500                         if($opt_v) { print "#\t" . 'optional' . "\n"; }
501                 if (-f $initrd) {
502                         print MYFH_NEW     "\t"  . 'initrd = ' . $initrd . "\n";
503                         if($opt_v) { print "\t"  . 'initrd = ' . $initrd . "\n"; }
504                 }
505                         print MYFH_NEW     "\n";
506                         if($opt_v) { print "\n"; }
507
508                 $nr++;
509                 last if ($nr > 1);
510         }
511
512         close(MYFH_NEW);
513 }               
514                 
515         
516 main();
517
518 __END__
519
520
521 =head1 NAME
522
523 liloconfig - create new lilo.conf file (with diskid and uuid)
524
525 =head1 SYNOPSIS
526
527 liloconfig [-h] [-v] [-f] [lilo.conf]
528
529 =head1 DESCRIPTION
530
531 liloconfig is an simple program for creating a new lilo.conf file.
532 After creating the new configuration file you must execute '/sbin/lilo'.
533
534 liloconfig use the lilo.example.conf file as template. In the final
535 lilo.conf file you find many useful comments for custom changes.
536
537 =head1 EXAMPLES
538
539 Lines in the configuration file /etc/lilo.conf:
540
541   ### LILO global section ###
542
543   #large-memory
544   lba32
545   boot = /dev/sda
546   map = /boot/map
547   install = menu
548   menu-scheme = Wb:Yr:Wb:Wb
549   prompt
550   timeout = 100
551   vga = normal
552   #default = Linux
553
554   ### LILO per-image section ###
555
556   #boot = /dev/sda
557   boot = /dev/disk/by-id/ata-SAMSUNG_SV1604N_S01FJ10X999999
558
559   image = /boot/vmlinuz-2.6.32-5book-686
560       label = "Linux"
561       #root = /dev/sda1
562       root = "UUID=18843936-00f9-4df0-a373-000d05a5dd44"
563       read-only
564   #   restricted
565   #   alias = 1
566   #   optional
567       initrd = /boot/initrd.img-2.6.32-5book-686
568
569   image = /boot/vmlinuz-2.6.32-5-686
570       label = "Linux Old"
571       #root = /dev/sda1
572       root = "UUID=18843936-00f9-4df0-a373-000d05a5dd44"
573       read-only
574   #   restricted
575   #   alias = 2
576   #   optional
577       initrd = /boot/initrd.img-2.6.32-5-686
578
579 =head1 OPTIONS
580
581 =over 4
582
583 =item B<-h>
584
585 Print a brief help.
586
587 =item B<-v>
588
589 Print verbose messages.
590
591 =item B<-f>
592
593 Force overriding existing lilo.conf.
594
595 =back
596
597 =head1 AUTHOR
598
599 B<liloconfig> was written by Joachim Wiedorn.
600
601 =cut