source: devel/pbmkbm/bin/pbmkbm @ 1408

Last change on this file since 1408 was 1408, checked in by bruno, 7 years ago

r4511@cabanilles: bruno | 2012-02-06 09:29:48 +0100

  • New sync point which brings a start of kernel analysis for pbmkbm
File size: 15.3 KB
Line 
1#!/usr/bin/perl -w
2#
3# pbmkbm, a project-builder.org utility to make boot media
4#
5# $Id$
6#
7# Copyright B. Cornec 2011
8# Provided under the GPL v2
9
10# Syntax: see at end
11
12use strict 'vars';
13use Getopt::Long qw(:config auto_abbrev no_ignore_case);
14use Data::Dumper;
15use English;
16use File::Basename;
17use File::Copy;
18use POSIX qw(strftime);
19
20use ProjectBuilder::Version;
21use ProjectBuilder::Base;
22use ProjectBuilder::Env;
23use ProjectBuilder::Conf;
24use ProjectBuilder::Distribution;
25use ProjectBuilder::VE;
26
27# Global variables
28my %opts;                   # CLI Options
29
30=pod
31
32=head1 NAME
33
34pbmkbm -  a project-builder.org utility to make boot media
35
36=head1 DESCRIPTION
37
38pbmkbm creates a bootable media (CD/DVD, USB device, Network, tape, ...)
39with a minimal distribution in it, suited for building packages for example.
40It aims at supporting all distributions supported by project-builder.org
41(RHEL, RH, Fedora, OpeSUSE, SLES, Mandriva, ...)
42
43It is inspired by work done by Jean-Marc André around the HP SSSTK and
44aim at replacing the mindi project (http://www.mondorescue.org), but
45fully integrated with project-builder.org
46
47pbmkbm works in different phases. The first one is to check all
48
49pbmkbm needs to gather a certain number of components that could come
50from various sources and could be put on a different target media.
51We need a kernel, an initrd/initramfs for additional modules and init script,
52a root filesystem and a boot configuration file.
53Kernel, modules could come either from the local installed system
54(typically for disaster recovery context) or from a kernel package of a
55given configuration or a referenced content.
56Utilities could come from busybox, local utilities or set of packages.
57The root filesystem is made with them.
58The initrd/initramfs could be made internaly or by calling dracut.
59The boot config file is generated from analysis content or provided externally.
60
61=head1 SYNOPSIS
62
63pbmkbm [-vhq][-t boot-type [-d device]][-b boot-method][-m os-ver-arch]
64[-s script][-a pkg1[,pkg2,...]] [target-dir]
65
66pbmkbm [--verbose][--help][--man][--quiet][--type boot-type [--device device]]
67[--machine os-ver-arch][--boot boot-method]
68[--script script][--add pkg1,[pkg2,...]] [target-dir]
69
70=head1 OPTIONS
71
72=over 4
73
74=item B<-v|--verbose>
75
76Print a brief help message and exits.
77
78=item B<-h|--help>
79
80Print a brief help message and exits.
81
82=item B<--man>
83
84Prints the manual page and exits.
85
86=item B<-q|--quiet>
87
88Do not print any output.
89
90=item B<-t|--type boot-type>
91
92Type of the boot device to generate. A boot-type can be:
93
94=over 4
95
96=item B<iso>
97
98Generate an ISO9660 image format (suitable to be burned later on or loopback mounted. Uses isolinux.
99
100=item B<usb>
101
102Generate a USB image format (typically a key of external hard drive). Uses syslinux.
103
104=item B<pxe>
105
106Generate a PXE environement (suitable to be integrated in a PXElinux configuration). Uses pxelinux.
107
108=back
109
110=item B<-d|--device device-file>
111
112Name of the device or file on which you want to create the boot media.
113
114=item B<-b|--boot boot-method>
115
116This is the boot method to use to create the boot media. A boot-method can be:
117
118=over 4
119
120=item B<native>
121
122Use the tools of the native distribution to create the boot media. No other dependency.
123
124=item B<ve>
125
126Use the project-builder.org virtual environment notion to create the boot media. No other dependency outside of the project.
127
128=item B<busybox>
129
130Use the busybox tool to create the boot media. Cf: L<http://www.busybox.net>
131
132=item B<dracut>
133
134Use the dracut tool to create the boot media. Cf: L<http://www.dracut.net>
135
136=back
137
138=item B<-s|--script script>
139
140Name of the script you want to execute on the related boot media at the end o the build.
141
142=item B<-a|--add pkg1[,pkg2,...]>
143
144Additional packages to add from the distribution you want to install on the related boot media
145at the end of the build.
146
147=item B<-m|--machine os-ver-arch>
148
149This is the target tuple operating system-version-architecture for which you want to create the boot media.
150
151=back
152
153=head1 ARGUMENTS
154
155target-dir is the directory under which the boot media will be build.
156
157=over 4
158
159=back
160
161=head1 EXAMPLE
162
163To setup a USB busybox based boot media on the /dev/sdb device for a Fedora 12 distribution with an i386 architecture issue:
164
165pbmkbm -t usb -d /dev/sdb -m fedora-12-i386 -b busybox
166
167To setup an ISO image under /tmp for a RHEL 6 x86_64 distribution issue using the native environment:
168
169pbmkbm -t iso -d /tmp -m rhel-6-x86_64 -b ve
170
171=head1 WEB SITES
172
173The main Web site of the project is available at L<http://www.project-builder.org/>.
174Bug reports should be filled using the trac instance of the project at L<http://trac.project-builder.org/>.
175
176=head1 USER MAILING LIST
177
178Cf: L<http://www.mondorescue.org/sympa/info/pb-announce> for announces and
179L<http://www.mondorescue.org/sympa/info/pb-devel> for the development of the pb project.
180
181=head1 CONFIGURATION FILE
182
183Uses Project-Builder.org configuration file (/etc/pb/pb.conf or /usr/local/etc/pb/pb.conf)
184
185=head1 AUTHORS
186
187The Project-Builder.org team L<http://trac.project-builder.org/> lead by Bruno Cornec L<mailto:bruno@project-builder.org>.
188
189=head1 COPYRIGHT
190
191Project-Builder.org is distributed under the GPL v2.0 license
192described in the file C<COPYING> included with the distribution.
193
194=cut
195
196# ---------------------------------------------------------------------------
197
198my ($projectbuilderver,$projectbuilderrev) = pb_version_init();
199my $appname = "pbmkbm";
200$ENV{'PBPROJ'} = $appname;
201
202# Initialize the syntax string
203
204pb_syntax_init("$appname Version $projectbuilderver-$projectbuilderrev\n");
205pb_temp_init();
206
207GetOptions("help|?|h" => \$opts{'h'}, 
208    "man" => \$opts{'man'},
209    "verbose|v+" => \$opts{'v'},
210    "quiet|q" => \$opts{'q'},
211    "log-files|l=s" => \$opts{'l'},
212    "script|s=s" => \$opts{'s'},
213    "machine|m=s" => \$opts{'m'},
214    "add|a=s" => \$opts{'a'},
215    "device|d=s" => \$opts{'d'},
216    "type|t=s" => \$opts{'t'},
217    "boot|b=s" => \$opts{'b'},
218    "version|V=s" => \$opts{'V'},
219) || pb_syntax(-1,0);
220
221if (defined $opts{'h'}) {
222    pb_syntax(0,1);
223}
224if (defined $opts{'man'}) {
225    pb_syntax(0,2);
226}
227if (defined $opts{'v'}) {
228    $pbdebug = $opts{'v'};
229}
230if (defined $opts{'q'}) {
231    $pbdebug=-1;
232}
233if (defined $opts{'l'}) {
234    open(pbLOG,"> $opts{'l'}") || die "Unable to log to $opts{'l'}: $!";
235    $pbLOG = \*pbLOG;
236    $pbdebug = 0  if ($pbdebug == -1);
237}
238pb_log_init($pbdebug, $pbLOG);
239pb_log(1,"$appname Version $projectbuilderver-$projectbuilderrev\n");
240my @date = pb_get_date();
241my $pbdate = strftime("%Y-%m-%d %H:%M:%S", @date);
242
243pb_log(1,"Start: $pbdate\n");
244
245# Get VE name
246$ENV{'PBV'} = $opts{'m'};
247
248#
249# Initialize distribution info from pb conf file
250#
251my $pbos = pb_distro_get_context($ENV{'PBV'});
252pb_log(0,"Starting boot media build for $pbos->{'name'}-$pbos->{'version'}-$pbos->{'arch'}\n");
253
254pb_env_init_pbrc(); # to get content of HOME/.pbrc
255
256#
257# Check target dir
258# Create if not existent and use default if none given
259#
260my $targetdir = shift @ARGV;
261
262#
263# Check for command requirements
264#
265my ($req,$opt) = pb_conf_get_if("oscmd","oscmdopt");
266pb_check_requirements($req,$opt,$appname);
267
268# After that we will need root access
269die "$appname needs to be run as root" if ($EFFECTIVE_USER_ID != 0);
270
271#
272# Where is our build target directory
273#
274if (not defined $targetdir) {
275    $targetdir = "/var/cache/pbmkbm";
276    my ($vestdpath) = pb_conf_get("mkbmpath");
277    $targetdir = "$vestdpath->{'default'}/$pbos->{'name'}/$pbos->{'version'}/$pbos->{'arch'}" if (defined $vestdpath->{'default'});
278    pb_log(1,"No target-dir specified, using $targetdir\n");
279}
280
281# Point to the right subdir and create it if needed
282pb_mkdir_p($targetdir) if (! -d $targetdir);
283
284# Log information on our system
285# TODO: this should be put in a subfunction
286my ($logcmd) = pb_conf_get("logcmd");
287my ($logopt) = pb_conf_get_if("logopt");
288# check that the command is there first and then use it.
289if ($logcmd->{$appname} ne "internal") {
290    $logcmd = pb_check_req($logcmd->{$appname},1);
291    if (not defined $logcmd) {
292        pb_log(1,"INFO: command $logcmd->{$appname} doesn't exist. No log report is available\n");
293    } else {
294        my $c = $logcmd->{$appname};
295        $c .= " $logopt->{$appname}" if (defined $logopt->{$appname});
296        pb_system("$c","Creating a log report of your system");
297    }
298} else {
299    # We provide our own internal log reporter as a std one isn't found
300    my ($lcmds,$lfiles) = pb_conf_get_if("logcommands","logfiles");
301    pb_log(1,"------------------\n");
302    if (defined $lcmds->{$appname}) {
303        foreach my $c (split(/,/,$lcmds->{$appname})) {
304            my $lcmd = $c;
305            $lcmd =~ s/ .*$//;
306            $lcmd = pb_check_req($lcmd,1);
307            if (not defined $lcmd) {
308                pb_log(1,"INFO: command $c doesn't exist\n");
309                pb_log(1,"------------------\n");
310                next;
311            }
312            pb_log(1,"Execution of $c\n");
313            pb_log(1,"------------------\n");
314            pb_system($c,"",1);
315            pb_log(1,"------------------\n");
316        }
317    }
318    if (defined $lfiles->{$appname}) {
319        foreach my $f (split(/,/,$lfiles->{$appname})) {
320            if (! -e $f) {
321                pb_log(1,"INFO: $f doesn't exist\n");
322                pb_log(1,"------------------\n");
323                next;
324            }
325            if (! -r $f) {
326                pb_log(1,"INFO: $f isn't readable\n");
327                pb_log(1,"------------------\n");
328                next;
329            }
330            if ((-f $f) || (-l $f)) {
331                if (! open(FILE,$f)) {
332                    pb_log(1,"INFO: Unable to open $f\n");
333                    pb_log(1,"------------------\n");
334                    next;
335                }
336                pb_log(1,"Content of $f\n");
337                pb_log(1,"------------------\n");
338                while (<FILE>) {
339                    pb_log(1,$_);
340                }
341                close(FILE);
342                pb_log(1,"------------------\n");
343            } elsif (-d $f) {
344                my $dh;
345                if (! opendir($dh,$f)) {
346                    pb_log(1,"INFO: Unable to opendir $f\n");
347                    pb_log(1,"------------------\n");
348                    next;
349                }
350                pb_log(1,"Content of directory $f\n");
351                pb_log(1,"----------------------------\n");
352                while (readdir $dh) {
353                    my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = stat("$f/$_");
354                    my $str = sprintf("%10s %2s %5s %5s %10s %14s %s",$mode,$ino,$uid,$gid,$size,$mtime,$_);
355                    pb_log(1,"$str\n");
356                }
357                closedir($dh);
358                pb_log(1,"------------------\n");
359            } else {
360                pb_log(1,"INFO: $f is special\n");
361                pb_log(1,"------------------\n");
362            }
363        }
364    }
365}
366
367# Now the preparation is over, we need to do something useful :-)
368# But it all depends on how we're asked to do it.
369#
370# First we need to copy into the target dir all the relevant content
371pb_mkbm_create_content();
372
373# Then we need to package this content in the destination format
374pb_mkbm_create_media();
375
376@date = pb_get_date();
377$pbdate = strftime("%Y-%m-%d %H:%M:%S", @date);
378
379pb_log(1,"End: $pbdate\n");
380
381sub pb_mkbm_create_content {
382
383pb_log(1,"Creating boot media content\n");
384
385# If not defined use VE mode by default
386$opts{'b'} = "ve" if (not defined $opts{'b'});
387
388# Hash of target content
389# atribute could be, copy, remove, link, dir
390my %targettree;
391
392if ($opts{'b'} eq "ve") {
393    # Use project-builder VE mecanism to create a good VE !
394    pb_ve_launch($ENV{'PBV'},1);
395} elsif ($opts{'b'} eq "native") {
396    # Use native tools to create a good VE !
397} elsif ($opts{'b'} eq "drakut") {
398    # Use drakut to create a good VE !
399} elsif ($opts{'b'} eq "busybox") {
400    # Use busybox to create a good VE !
401    pb_mkbm_create_busybox_ve(\%targettree);
402} else {
403    die "Unknown method $opts{'b'} used to create the media content";
404}
405
406# Create the directory structure needed on the target dir
407my ($tdirs,$bdirs,$bfiles,$bcmds) = pb_distro_get_param($pbos,pb_conf_get("mkbmtargetdirs","mkbmbootdirs","mkbmbootfiles","mkbmbootcmds"));
408# Create empty dirs for these
409foreach my $d (split(/,/,$tdirs)) {
410    $targettree{$d} = "emptydir";
411}
412# And copy dirs for those
413foreach my $d (split(/,/,$bdirs)) {
414    if (-d $d) {
415        $targettree{$d} = "dir";
416    } elsif (-l $d) {
417        $targettree{$d} = "link";
418    } else {
419        pb_log(1,"INFO: Directory $d doesn't exist\n");
420    }
421}
422foreach my $f (split(/,/,$bfiles)) {
423    $targettree{$f} = "file";
424}
425pb_log(2,"INFO: Target Tree is now: ".Dumper(%targettree)."\n");
426# Once the environment is made, add what is needed for this boot media to it.
427# Keyboard
428# Terminfo
429# List of commands
430# List of dependencies
431# Kernel - We use 2 objects, the running kernel and the target kernel which could be different
432my %rkernel;
433my %tkernel;
434pb_mkbm_find_kernel(\%rkernel);
435# Initrd
436# init
437# BootLoader and its configuration
438# Additional data files coming from a potential caller (MondoRescue/Mindi e.g. with fstab, LVM, mountlist, ...)
439}
440
441sub pb_mkbm_create_busybox_ve {
442
443my $tgtree = shift;
444
445# First, check which are the supported command in that version of busybox
446# and create the links for it in the target VE
447
448my $busycmd = pb_distro_get_param($pbos,pb_conf_get("ospathcmd-busybox"));
449open(BUSY,"$busycmd |") || die "Unable to execute $busycmd";
450my $cmdlist = 0;
451while (<BUSY>) {
452    pb_log(3,"busybox line : $_");
453    chomp($_);
454    # After these words, we have the list of functions, so trigger their analysis
455    if ($_ =~ /defined functions:/) {
456        $cmdlist = 1;
457        next;
458    }
459    # Analyse the list of commands provided by that busybox (, separated)
460    if ($cmdlist == 1) {
461        pb_log(3,"Analyzing that busybox line\n");
462        foreach my $c (split(/,/,$_)) {
463            $c =~ s/\s*//g;
464            # skip empty strings
465            next if ($c =~ /^$/);
466            my $c1 = pb_check_req($c,0);
467            if (defined $c1) {
468                $tgtree->{$c1} = "link:$busycmd";
469            } else {
470                # When not found on the system, create the link under /usr/bin by default
471                $tgtree->{"/usr/bin/$c"} = "link:$busycmd";
472            }
473        }
474    }
475}
476pb_log(2,"Target Tree is now: ".Dumper($tgtree)."\n");
477close(BUSY);
478}
479
480sub pb_mkbm_find_kernel {
481
482my $kernel = shift;
483
484$kernel->is_xen = undef;
485# See if we're booted from a Xen kernel
486# From http://wiki.xensource.com/xenwiki/XenCommonProblems#head-26434581604cc8357d9762aaaf040e8d87b37752
487if ( -f "/proc/xen/capabilities") {
488    # It's a Xen kernel
489    pb_log(2,"INFO: We found a Xen Kernel running\n");
490}
491
492my $kfile = pb_distro_get_param($pbos,pb_conf_get_if("mkbmkernelfile"));
493if ((defined $kfile) && ($kfile ne "")) {
494    pb_log(1,"INFO: You specified your kernel as $kfile, so using it\n");
495    $kernel->file = $kfile;
496} else {
497    $kernel->dir = pb_distro_get_param($pbos,pb_conf_get("mkbmkerneldir"));
498    die "ERROR: The mkbmkerneldir content ($kernel->dir) doesn't refer to a directory\n"if (! -d $kernel->dir);
499    pb_log(1,"INFO: Analyzing directory $kernel->dir to find your kernel");
500    $kernel->namere = pb_distro_get_param($pbos,pb_conf_get("mkbmkernelnamere"));
501}
502
503# TODO: Look at a better way to find the name of the kernel we run
504# look at /proc/sys/kernel/bootloader_type /proc/sys/kernel/bootloader_version
505# to have a better guess
506my $dh;
507die "ERROR: Unable to open the mkbmkerneldir content ($kernel->dir)" if (! opendir($dh,$kernel->dir));
508while (readdir $dh) {
509    # Skip non-files
510    next if ((! -f $_) && (! -l $_));
511    # Skip files not correpsonding to the RE planned
512    next if ($_ !~ /$kernel->namere/);
513    #my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = stat("$f/$_");
514}
515closedir($dh);
516$kernel->release = pb_get_osrelase();
517}
518
519sub pb_mkbm_create_media {
520
521}
522
523# Get the package list to download, store them in a cache directory
524#
525#my ($mkbmcachedir) = pb_conf_get_if("mkbmcachedir");
526#my ($pkgs) = pb_distro_get_param($pbos,pb_conf_get("mkbmmindep"));
527
528#
529# /proc needed
530#
531#pb_system("mount -o bind /proc $targetdir/proc","Mounting /proc");
532
533# Installed additional packages we were asked to
534#if (defined $opts{'a'}) {
535#$opts{'a'} =~ s/,/ /g;
536#pb_system("chroot $targetdir /bin/bash -c \"$pbos->{'install'} $opts{'a'} \"","Adding packages to OS by running $pbos->{'install'} $opts{'a'}");
537#}
538
539#
540# Clean up
541#
542#pb_log(1,"Cleaning up\n");
543#pb_system("umount $targetdir/proc","Unmounting /proc");
544
545# Executes post-install step if asked for
546#if ($opts{'s'}) {
547#pb_system("$opts{'s'} $targetdir","Executing the post-install script: $opts{'s'} $targetdir");
548#}
Note: See TracBrowser for help on using the repository browser.