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