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

Last change on this file since 1402 was 1402, checked in by Bruno Cornec, 12 years ago

r4487@cabanilles: bruno | 2012-02-01 16:21:48 +0100

  • Fix the -t option without param for pb
  • add a function to analyze busybox countent and create appropriate structure for pbmkbm
File size: 13.2 KB
RevLine 
[1341]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;
[1402]18use POSIX qw(strftime);
19
[1341]20use ProjectBuilder::Version;
21use ProjectBuilder::Base;
22use ProjectBuilder::Env;
23use ProjectBuilder::Conf;
24use ProjectBuilder::Distribution;
[1348]25use ProjectBuilder::VE;
[1341]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
[1348]55given configuration or a referenced content.
[1341]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.
[1348]59The boot config file is generated from analysis content or provided externally.
[1341]60
61=head1 SYNOPSIS
62
[1348]63pbmkbm [-vhq][-t boot-type [-d device]][-b boot-method][-m os-ver-arch]
[1341]64[-s script][-a pkg1[,pkg2,...]] [target-dir]
65
[1348]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]
[1341]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
[1348]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
[1341]138=item B<-s|--script script>
139
[1348]140Name of the script you want to execute on the related boot media at the end o the build.
[1341]141
142=item B<-a|--add pkg1[,pkg2,...]>
143
[1348]144Additional packages to add from the distribution you want to install on the related boot media
145at the end of the build.
[1341]146
[1348]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
[1341]151=back
152
153=head1 ARGUMENTS
154
[1348]155target-dir is the directory under which the boot media will be build.
156
[1341]157=over 4
158
159=back
160
161=head1 EXAMPLE
162
[1348]163To setup a USB busybox based boot media on the /dev/sdb device for a Fedora 12 distribution with an i386 architecture issue:
[1341]164
[1348]165pbmkbm -t usb -d /dev/sdb -m fedora-12-i386 -b busybox
[1341]166
[1348]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
[1341]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'},
[1348]208 "man" => \$opts{'man'},
[1341]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'},
[1348]215 "device|d=s" => \$opts{'d'},
216 "type|t=s" => \$opts{'t'},
217 "boot|b=s" => \$opts{'b'},
[1341]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);
[1402]239pb_log(1,"$appname Version $projectbuilderver-$projectbuilderrev\n");
240my @date = pb_get_date();
241my $pbdate = strftime("%Y-%m-%d %H:%M:%S", @date);
[1341]242
[1402]243pb_log(1,"Start: $pbdate\n");
244
[1341]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'});
[1348]252pb_log(0,"Starting boot media build for $pbos->{'name'}-$pbos->{'version'}-$pbos->{'arch'}\n");
[1341]253
[1348]254pb_env_init_pbrc(); # to get content of HOME/.pbrc
255
[1341]256#
257# Check target dir
258# Create if not existent and use default if none given
259#
[1348]260my $targetdir = shift @ARGV;
[1341]261
262#
263# Check for command requirements
264#
[1402]265my ($req,$opt) = pb_conf_get_if("oscmd","oscmdopt");
[1341]266pb_check_requirements($req,$opt,$appname);
267
[1348]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'});
[1402]278 pb_log(1,"No target-dir specified, using $targetdir\n");
[1341]279}
280
[1348]281# Point to the right subdir and create it if needed
282pb_mkdir_p($targetdir) if (! -d $targetdir);
[1341]283
[1402]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 $lcmd (in $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}
[1348]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.
[1341]369#
[1348]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
[1402]376@date = pb_get_date();
377$pbdate = strftime("%Y-%m-%d %H:%M:%S", @date);
378
379pb_log(1,"End: $pbdate\n");
380
[1348]381sub pb_mkbm_create_content {
382
383pb_log(1,"Creating boot media content\n");
[1350]384
385# If not defined use VE mode by default
386$opts{'b'} = "ve" if (not defined $opts{'b'});
387
[1402]388# Hash of target content
389# atribute could be, copy, remove, link, dir
390my %targettree;
391
[1348]392if ($opts{'b'} eq "ve") {
[1402]393 # Use project-builder VE mecanism to create a good VE !
[1352]394 pb_ve_launch($ENV{'PBV'},1);
[1348]395} elsif ($opts{'b'} eq "native") {
396 # Use native tools to create a good VE !
[1402]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);
[1348]402} else {
403 die "Unknown method $opts{'b'} used to create the media content";
404}
[1352]405
[1402]406# Create the directory structure needed on the target dir
407$targettree->{"/bin"} = "dir";
408
[1352]409# Once the environment is made, add what is needed for this boot media to it.
[1402]410# Keyboard
411# Terminfo
412# List of commands
413# List of dependencies
414# Kernel
415# Initrd
416# init
417# BootLoader and its configuration
418# Additional data files coming from a potential caller (MondoRescue e.g. with fstab, LVM, mountlist, ...)
[1348]419}
420
[1402]421sub pb_mkbm_create_busybox_ve {
422
423my $tgtree = shift;
424
425# First, check which are the supported command in that version of busybox
426# and create the links for it in the target VE
427
428my $busycmd = pb_distro_get_param($pbos,pb_conf_get("ospathcmd-busybox"));
429open(BUSY,"$busycmd |") || die "Unable to execute $busycmd";
430my $cmdlist = 0;
431while (<BUSY>) {
432 pb_log(3,"busybox line : $_");
433 chomp($_);
434 # After these words, we have the list of functions, so trigger their analysis
435 if ($_ =~ /defined functions:/) {
436 $cmdlist = 1;
437 next;
438 }
439 # Analyse the list of commands provided by that busybox (, separated)
440 if ($cmdlist == 1) {
441 pb_log(3,"Analyzing that busybox line\n");
442 foreach my $c (split(/,/,$_)) {
443 $c =~ s/\s*//g;
444 # skip empty strings
445 next if ($c =~ /^$/);
446 my $c1 = pb_check_req($c,0);
447 if (defined $c1) {
448 $tgtree->{$c1} = "link:$busycmd";
449 } else {
450 # When not found on the system, create the link under /usr/bin by default
451 $tgtree->{"/usr/bin/$c"} = "link:$busycmd";
452 }
453 }
454 }
455}
456pb_log(2,"Target Tree is now: ".Dumper($tgtree)."\n");
457close(BUSY);
458}
459
[1348]460sub pb_mkbm_create_media {
461
462}
463
[1341]464# Get the package list to download, store them in a cache directory
465#
[1352]466#my ($mkbmcachedir) = pb_conf_get_if("mkbmcachedir");
467#my ($pkgs) = pb_distro_get_param($pbos,pb_conf_get("mkbmmindep"));
[1341]468
469#
470# /proc needed
471#
[1352]472#pb_system("mount -o bind /proc $targetdir/proc","Mounting /proc");
[1341]473
474# Installed additional packages we were asked to
[1352]475#if (defined $opts{'a'}) {
476#$opts{'a'} =~ s/,/ /g;
477#pb_system("chroot $targetdir /bin/bash -c \"$pbos->{'install'} $opts{'a'} \"","Adding packages to OS by running $pbos->{'install'} $opts{'a'}");
478#}
[1341]479
480#
481# Clean up
482#
[1352]483#pb_log(1,"Cleaning up\n");
484#pb_system("umount $targetdir/proc","Unmounting /proc");
[1341]485
486# Executes post-install step if asked for
[1352]487#if ($opts{'s'}) {
488#pb_system("$opts{'s'} $targetdir","Executing the post-install script: $opts{'s'} $targetdir");
489#}
Note: See TracBrowser for help on using the repository browser.