Imported Upstream version 23.2
[rrq/maintain_lilo.git] / 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         }
208         unless ($root_part =~ /^UUID/) {
209                 # now find the right block device name
210                 $root_part =~ s/\d+$//;
211         }
212
213         return $root_part;
214 }
215
216 sub check_raid {
217         
218         my $part = $_[0];
219         my $mdname;
220         my $md;
221         my @devices;
222         
223         # check if the found partition is a raid volume
224         if($part =~ /\/dev\/md/)
225         {
226                 $mdname = $part;
227                 $mdname =~ s/\/dev\///;
228                 $mdname =~ s/\///;
229                 $md = `grep $mdname /proc/mdstat`;
230         
231                 @devices = split(" ", $md);
232                 @devices = sort(@devices[4..$#devices]);
233                 $part = "/dev/" . $devices[0];
234                 $part =~ s/\[.*$//;
235
236         }
237         return $part;
238 }
239
240 sub detect_root_device {
241         
242         my $root_line = $_[0];
243         my $root_disk = '';
244         my $root_link = '';
245         my $exit = 0;
246
247         if (not $exit) {
248                 # extract the root device name
249                 $root_disk = ( $root_line =~ /^\t?root *= *(.*)/ ) ? ($1) : ();
250                 chomp $root_disk;
251
252                 # check if the found partition is a raid volume
253                 if($root_disk =~ /\/dev\/md/) {
254                         $root_disk = check_raid($root_disk);
255                 }
256         }
257
258         # check if root device exist / also for raid volume
259         if (-b $root_disk) {
260                 if($opt_v) { print "convert root option $root_disk into new UUID\n"; }
261                 $root_id = find_id_link($root_disk,$uuidpath);
262                 if (not -l "$uuidpath/$root_id") { $exit = 1; }
263         }
264         else {
265                 # nothing to do but perhaps give a message
266                 $exit = 1;
267                 $root_link = $root_disk;
268                 $root_link =~ s{\"}{}g;
269                 $root_link =~ s{^LABEL=}{/dev/disk/by-label/};
270                 $root_link =~ s{^UUID=}{/dev/disk/by-uuid/};
271                 if (not -l $root_link) {
272                         if($opt_v) { print "cannot check $root_link: link does not exist!\n"; }
273                 }
274         }
275         return $exit;
276 }
277
278 sub find_id_link {
279         
280         my $olddev = $_[0];
281         my $path_id = $_[1];
282         my @sellinks;
283         my $_idlink;
284         my $_actlink;
285         my $newdevid = '';
286         my $ok = 0;
287
288         opendir(MYDH, "$path_id") or die("cannot open $path_id: $! \n");
289         @sellinks = grep(!/\-part\d\d?$/, grep(!/^\.\.?$/, readdir(MYDH)));
290         @sellinks = sort(@sellinks);
291         closedir(MYDH);
292
293         foreach $_idlink (@sellinks) {
294                 if(not $_idlink =~ /^usb/ and length($_idlink) > 10 and $ok == 0) {
295                         $_actlink = readlink("$path_id/$_idlink");
296                         $_actlink =~ s/^\.\.\/\.\.\//\/dev\//;
297                         if($opt_v) { print "** try: $_actlink => $_idlink \n"; }
298                         
299                         if($_actlink eq $olddev) {
300                                 $newdevid = $_idlink;
301                                 if($opt_v) { print "** convert: $_actlink => $path_id/$_idlink \n"; }
302                                 # run only one time
303                                 $ok = 1;
304                         }
305                 }
306         }
307
308         if($opt_v and not $ok) { print "$olddev not converted: link not useful\n\n"; }
309         
310         return ($newdevid);
311 }
312
313 sub write_boot_option {
314         
315         my $oldline = '';
316         my $comline = '';
317         my $newline = '';
318         my @status;
319         my $_preold;
320         my $_prenew;
321
322         if (-f $liloconf) {
323                 # move old lilo.conf to lilo.conf_old
324                 @status = stat($liloconf);
325                 move ($liloconf, $liloconfold);
326                 utime ($status[9],$status[9],$liloconfold);
327                 chmod (0600,$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                 chmod (0600,$liloconf);
382         }
383         else {
384                 print "file $liloconf does not exist: nothing changed\n";
385         }
386 }
387
388 sub comment_root_line {
389
390         my $rootline = $_[0];
391
392         if( $rootline =~ /root/) {
393                 $rootline =~ s/root/#root/;
394         }
395         return $rootline;
396 }
397
398 sub modern_root_line {
399         
400         my $oldline = $_[0];
401         my $newline = $_[0];
402         my $rootid  = $_[1];
403         my $indent  = '';
404
405         if($oldline =~ /root/) {
406                 $indent = $oldline;
407                 chomp $indent;
408                 $indent =~ s/^(\t?)root.*/$1/;
409                 $newline = $indent . "root = \"UUID=$rootid\"\n";
410         }
411         return $newline;
412 }
413
414
415 main();
416
417
418 __END__
419
420
421 =head1 NAME
422
423 lilo-uuid-diskid - convert boot / root options to diskid and uuid in lilo.conf
424
425 =head1 SYNOPSIS
426
427 lilo-uuid-diskid [-h] [-v] [lilo.conf]
428
429 =head1 DESCRIPTION
430
431 This script looks for the boot block device or boot partition and create the
432 right diskid or uuid as boot option. Then it looks for all root partitions
433 and create the right uuids as root options.
434
435 These conversions are necessary for use with newer kernel (>= 2.6.26) if it
436 use the libata module for parallel and serial ATA interfaces of block devices
437 (i. e. hard disks with IDE or SATA interface, usbsticks).
438
439 =head1 EXAMPLES
440
441 Lines in the configuration file /etc/lilo.conf:
442
443   #boot = /dev/sda
444   boot = /dev/disk/by-id/ata-SAMSUNG_SV1604N_S01FJ10X999999
445
446   #root = /dev/sda1
447   root = "UUID=18843936-00f9-4df0-a373-000d05a5dd44"
448
449 =head1 OPTIONS
450
451 =over 4
452
453 =item B<-h>
454
455 Print a brief help.
456
457 =item B<-v>
458
459 Print verbose messages.
460
461 =back
462
463 =head1 AUTHOR
464
465 B<lilo-uuid-diskid> was written by Joachim Wiedorn.
466
467 =cut