source: ProjectBuilder/devel/pbmkbm/bin/pbmkbm@ 1408

Last change on this file since 1408 was 1408, checked in by Bruno Cornec, 12 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.