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