Add lilo-uuid-diskid script with manpage
[rrq/maintain_lilo.git] / debian / scripts / lilo-uuid-diskid
1 #!/usr/bin/perl -w
2
3 #       lilo-uuid-diskid - convert boot / root options to diskid and 
4 #                          uuid in lilo.conf
5 #
6 #       Copyright 2010 Joachim Wiedorn <ad_debian at joonet.de>
7 #       
8 #       This program is free software; you can redistribute it and/or modify
9 #       it under the terms of the GNU General Public License as published by
10 #       the Free Software Foundation; either version 2 of the License, or
11 #       (at your option) any later version.
12 #       
13 #       This program is distributed in the hope that it will be useful,
14 #       but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 #       GNU General Public License for more details.
17 #       
18 #       You should have received a copy of the GNU General Public License
19 #       along with this program; if not, write to the Free Software
20 #       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21 #       MA 02110-1301, USA.
22
23 #---- some modules
24 use strict;
25 use warnings;
26 use Getopt::Std;
27 use Pod::Usage;
28 use File::Copy;
29
30 #---- global variables
31 my $prog = $0;
32 $prog =~ s#.*/##;
33 my $version = "0.1";
34
35 #---- parameter check
36 our $opt_h = 0;
37 our $opt_v = 0;
38 getopts('hv');
39 # define perldoc usage
40 pod2usage(1) if $opt_h;
41
42 #---- other variables
43 our $liloconf = "/etc/lilo.conf";
44 our $liloconfold = '';
45 our $fstabconf = "/etc/fstab";
46 our $idpath = "/dev/disk/by-id";
47 our $uuidpath = "/dev/disk/by-uuid";
48
49 our $bootready = 0;
50 our $boot_id = '';
51 our $root_id = '';
52 our $optboot = 0;     #  -1 = only ^#boot,  0 = nothing,  1 = ^boot  exist
53
54 #-------------------- main program --------------------
55
56 sub main {
57         
58         my $exit = 0;
59
60         if (@ARGV == 1) {
61                 $liloconf = "$ARGV[0]";
62         }
63
64         if (-f $liloconf) {
65                 $liloconfold = $liloconf . "_old";
66                 $exit = convert_lilo_conf();
67         }
68         else {
69                 print "cannot open $liloconf: file not found!\n";
70                 $exit = 1;
71         }
72         return $exit;
73 }
74
75 #-------------------- subroutines --------------------
76
77 sub convert_lilo_conf {
78         
79         my @sellines;
80         my $exit = 0;
81         my $line = '';
82         my $bootline = '';
83
84         # at first read lilo.conf and search for 'boot'
85         my $ok = 0;
86         open(MYFH, "<$liloconf") or die ("cannot open $liloconf: $!");
87         @sellines = grep(/^#?boot/, readline(MYFH));
88         close(MYFH);
89
90         # analyse the boot option in config file
91         foreach $line (@sellines) {
92                 if ($line =~ /^boot/) {
93                         # activated boot option found
94                         $bootline = $line;
95                         chomp $bootline;
96                         $optboot = 1;
97                 }
98                 if ($optboot == 0) {
99                         # commented boot option found
100                         if ($line =~ /^#boot/) { $optboot = -1; }
101                 }
102         }
103
104         if ($optboot != 0) {
105
106                 if($opt_v) { print "++++++++++ write options into $liloconf ++++++++++\n\n"; }
107
108                 if ( detect_boot_device($bootline) == 0) {
109                         # found diskid or uuid for boot device: $boot_id
110                         if (-l $boot_id) {
111                                 write_boot_option();
112                         }
113                         else {
114                                 if($opt_v) { print "cannot open $boot_id: link does not exist!\n"; }
115                                 $exit = 1;
116                         }
117                 }
118                 else {
119                         if($opt_v) { print "\n"; }
120                 }
121         }
122         else {
123                 print "cannot use $liloconf: uncomplete configuration!\n";
124                 $exit = 1;
125         }
126
127         return $exit;
128 }
129
130
131 sub detect_boot_device {
132         
133         my $boot_line = $_[0];
134         my $boot_disk = '';
135         my $searchpath;
136         my $_part;
137         my $exit = 0;
138
139         if ($optboot == 1) {
140                 # the usual case: found ^boot in lilo.conf
141                 $boot_disk = ($boot_line =~ /^boot *= *(\/dev\/.*)/) ? ($1) : ();
142
143                 # check if the found partition is a raid volume
144                 if($boot_disk =~ /\/dev\/md/) {
145                         $boot_disk = check_raid($boot_disk);
146                 }
147         }
148         elsif ($optboot == -1) {
149                 # found only ^#boot in lilo.conf, then /etc/fstab is needed
150                 if (-f $fstabconf) {
151                         if($opt_v) {
152                                 print "no boot option in $liloconf: selecting from $fstabconf\n";
153                         }
154                         $boot_disk = read_fstab($fstabconf);
155                 }
156                 else {
157                         print "no boot option in $liloconf and no file $fstabconf found!\n";
158                         $exit = 1;
159                 }
160         }
161
162         if (-b $boot_disk) {
163                 if($boot_disk =~ /$idpath/ or $boot_disk =~ /$uuidpath/) {
164                         print "boot option is already updated to $boot_disk\n";
165                         $bootready = 1; $exit = 1;
166                 }
167                 else {
168                         if($opt_v) { print "convert boot option $boot_disk into new ID\n"; }
169
170                         # is it a block device name ?
171                         $_part = $boot_disk;
172                         $_part =~ s/\d+$//;
173                         $searchpath = ($_part eq $boot_disk) ? $idpath : $uuidpath;
174
175                         $boot_id = $searchpath . "/" . find_id_link($boot_disk,$searchpath);
176                         if(not -l "$boot_id") { $exit = 1; }
177                 }
178         }
179         else {
180                 if($opt_v) { print "cannot read $boot_disk: link does not exist!\n"; }
181                 $exit = 1;
182         }
183         return $exit;
184 }
185
186
187 sub read_fstab {
188         
189         my $ffile = $_[0];
190         my $root_line;
191         my $root_part;
192         my $_item;
193
194         $root_line = `awk '{ if (\$2=="/") print \$1}' <$ffile`;
195
196         # search for the last valid entry in /etc/fstab about root partition
197         foreach $_item (split("\n", $root_line)) {
198                 if(not $_item =~ /#/) {
199                         $root_part = $_item;
200                 }
201         }
202         # check if the found partition is a raid volume
203         if($root_part =~ /\/dev\/md/) {
204                 $root_part = check_raid($root_part);
205         }
206         # now find the right block device name
207         $root_part =~ s/\d+$//;
208         $root_part =~ s/part$/disc/;
209
210         return $root_part;
211 }
212
213 sub check_raid {
214         
215         my $part = $_[0];
216
217         # check if the found partition is a raid volume
218         if($part =~ /\/dev\/md/)
219         {
220                 my $mdname;
221                 my $md;
222                 my @devices;
223         
224                 $mdname = $part;
225                 $mdname =~ s/\/dev\///;
226                 $mdname =~ s/\///;
227                 $md = `grep $mdname /proc/mdstat`;
228         
229                 @devices = split(" ", $md);
230                 @devices = sort(@devices[4..$#devices]);
231                 $part = "/dev/" . $devices[0];
232                 $part =~ s/\[.*$//;
233
234         }
235         return $part;
236 }
237
238 sub detect_root_device {
239         
240         my $root_line = $_[0];
241         my $root_disk = '';
242         my $exit = 0;
243
244         if (not $exit) {
245                 # extract the root device name
246                 $root_disk = ($root_line =~ /^\t?root *= *(\/dev\/.*)/) ? ($1) : ();
247                 chomp $root_disk;
248
249                 # check if the found partition is a raid volume
250                 if($root_disk =~ /\/dev\/md/) {
251                         $root_disk = check_raid($root_disk);
252                 }
253         }
254         
255         if (-b $root_disk) {
256
257                 if($root_disk =~ /$uuidpath/) {
258                         print "$root_disk is already updated - nothing to do\n";
259                 }
260                 else {
261                         if($opt_v) { print "convert root option $root_disk into new UUID\n"; }
262
263                         $root_id = find_id_link($root_disk,$uuidpath);
264                         if (not -l "$uuidpath/$root_id") { $exit = 1; }
265                 }
266         }
267         else {
268                 if($opt_v) { print "cannot read $root_disk: link does not exist!\n"; }
269                 $exit = 1;
270         }
271         return $exit;
272 }
273
274 sub find_id_link {
275         
276         my $olddev = $_[0];
277         my $path_id = $_[1];
278         my @sellinks;
279         my $_idlink;
280         my $_actlink;
281         my $newdevid = '';
282         my $ok = 0;
283
284         opendir(MYDH, "$path_id") or die("cannot open $path_id: $! \n");
285
286         @sellinks = grep(!/\-part\d\d?$/, grep(!/^\.\.?$/, readdir(MYDH)));
287         foreach $_idlink (@sellinks) {
288                 
289                 if(not $_idlink =~ /^usb/ and not $_idlink =~ /^scsi/
290                                 and length($_idlink) > 10 and $ok == 0) {
291
292                         $_actlink = readlink("$path_id/$_idlink");
293                         $_actlink =~ s/^\.\.\/\.\.\//\/dev\//;
294                         if($opt_v) { print "** try: $_actlink => $_idlink \n"; }
295                         
296                         if($_actlink eq $olddev) {
297                                 $newdevid = $_idlink;
298                                 if($opt_v) { print "** convert: $_actlink => $path_id/$_idlink \n"; }
299                                 # run only one time
300                                 $ok = 1;
301                         }
302                 }
303         }
304         closedir(MYDH);
305
306         if($opt_v and not $ok) { print "$olddev not converted: link not useful\n\n"; }
307         
308         return ($newdevid);
309 }
310
311 sub write_boot_option {
312         
313         my $oldline = '';
314         my $comline = '';
315         my $newline = '';
316         my @status;
317         my $_preold;
318         my $_prenew;
319
320         if (-f $liloconf) {
321                 # move old lilo.conf to lilo.conf_old
322                 @status = stat($liloconf);
323                 move ($liloconf, $liloconfold);
324                 utime ($status[9],$status[9],$liloconfold);
325
326                 # copy all lines from lilo.conf_old into
327                 # new lilo.conf and add 'boot=' line
328                 my $ok = 0;
329                 open(MYFH_NEW, "> $liloconf") or die("cannot open $liloconf: $!");
330                 open(MYFH_OLD, "< $liloconfold") or die ("cannot open $liloconfold: $!");
331
332                 while (<MYFH_OLD>) {
333                         # line read from MYFH_OLD
334                         $oldline = $_;
335
336                         if (/^boot/ and $ok == 0) {
337                                 $newline = "boot = $boot_id\n";
338                                 print MYFH_NEW "#" . $oldline;
339                                 print MYFH_NEW $newline;
340                                 if($opt_v) { print "+  #" . $oldline; }
341                                 print "+  " . $newline;
342                                 if($opt_v) { print "\n"; }
343                                 # convert only one time
344                                 $ok = 1;
345                         }
346                         elsif (/^#boot/ and $optboot == -1 and $ok == 0) {
347                                 # found a line with boot option commented out
348                                 $newline = "boot = $boot_id\n";
349                                 print MYFH_NEW $oldline;
350                                 print MYFH_NEW $newline;
351                                 if($opt_v) { print "+  " . $oldline; }
352                                 print "+  " . $newline;
353                                 if($opt_v) { print "\n"; }
354                                 # convert only one time
355                                 $ok = 1;
356                         }
357                         elsif (/^root/ or /^\troot/) {
358                                 # found a line with root option
359                                 if (detect_root_device($oldline) == 0) {
360                                         $comline = comment_root_line($oldline);
361                                         $newline = modern_root_line($oldline,$root_id);
362                                         print MYFH_NEW $comline;
363                                         print MYFH_NEW $newline;
364                                         if($opt_v) { print '+  ' . $comline; }
365                                         print '+  ' . $newline;
366                                         if($opt_v) { print "\n"; }
367                                 }
368                                 else {
369                                         print MYFH_NEW $oldline;
370                                 }
371                         }
372                         else {
373                                 print MYFH_NEW $oldline;
374                         }
375                 }
376                 close(MYFH_OLD);
377                 close(MYFH_NEW);
378         }
379         else {
380                 print "file $liloconf does not exist: nothing changed\n";
381         }
382 }
383
384 sub comment_root_line {
385
386         my $rootline = $_[0];
387
388         if( $rootline =~ /root/) {
389                 $rootline =~ s/root/#root/;
390         }
391         return $rootline;
392 }
393
394 sub modern_root_line {
395         
396         my $oldline = $_[0];
397         my $newline = $_[0];
398         my $rootid  = $_[1];
399         my $indent  = '';
400
401         if($oldline =~ /root/) {
402                 $indent = $oldline;
403                 chomp $indent;
404                 $indent =~ s/^(\t?)root.*/$1/;
405                 $newline = $indent . "root = \"UUID=$rootid\"\n";
406         }
407         return $newline;
408 }
409
410
411 main();
412
413
414 __END__
415
416
417 =head1 NAME
418
419 lilo-uuid-diskid - convert boot / root options to diskid and uuid in lilo.conf
420
421 =head1 SYNOPSIS
422
423 lilo-uuid-diskid [-h] [-v] [lilo.conf]
424
425 =head1 DESCRIPTION
426
427 This script looks for the boot block device or boot partition and create the
428 right diskid or uuid as boot option. Then it looks for all root partitions
429 and create the right uuids as root options.
430
431 These conversions are necessary for use with newer kernel (>= 2.6.26) if it
432 use the libata module for parallel and serial ATA interfaces of block devices
433 (i. e. hard disks with IDE or SATA interface, usbsticks).
434
435 =head1 EXAMPLES
436
437 Lines in the configuration file /etc/lilo.conf:
438
439   #boot = /dev/sda
440   boot = /dev/disk/by-id/ata-SAMSUNG_SV1604N_S01FJ10X999999
441
442   #root = /dev/sda1
443   root = "UUID=/dev/disk/by-uuid/18843936-00f9-4df0-a373-000d05a5dd44"
444
445 =head1 OPTIONS
446
447 =over 4
448
449 =item B<-h>
450
451 Print a brief help.
452
453 =item B<-v>
454
455 Print verbose messages.
456
457 =back
458
459 =head1 AUTHOR
460
461 B<lilo-uuid-diskid> was written by Joachim Wiedorn.
462
463 =cut