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