diff options
author | Olivier Blin <oblin@mandriva.com> | 2007-07-11 13:39:30 +0000 |
---|---|---|
committer | Olivier Blin <oblin@mandriva.com> | 2007-07-11 13:39:30 +0000 |
commit | de8bd8d785e067e25e6131f03355f2baaea69539 (patch) | |
tree | a4160d45f849c7d31972d4ec3b6dcce103f0ade2 | |
download | draklive-de8bd8d785e067e25e6131f03355f2baaea69539.tar draklive-de8bd8d785e067e25e6131f03355f2baaea69539.tar.gz draklive-de8bd8d785e067e25e6131f03355f2baaea69539.tar.bz2 draklive-de8bd8d785e067e25e6131f03355f2baaea69539.tar.xz draklive-de8bd8d785e067e25e6131f03355f2baaea69539.zip |
restore draklive for 2007.0
-rw-r--r-- | Makefile | 20 | ||||
-rwxr-xr-x | draklive | 1417 | ||||
-rw-r--r-- | draklive-copy-wizard.desktop | 7 | ||||
-rw-r--r-- | draklive.spec | 69 | ||||
l--------- | theme | 1 |
5 files changed, 1514 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5b60762 --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +NAME = draklive +VERSION := $(shell rpm -q --qf '%{VERSION}\n' --specfile $(NAME).spec | head -1) +RELEASE := $(shell rpm -q --qf '%{RELEASE}\n' --specfile $(NAME).spec | head -1) + +rpm: dis + rpm -ta ../$(NAME)-$(VERSION).tar.bz2 + +check: + perl -cw $(NAME) + +clean: + find -name '*~' -exec rm {} \; + +dis ../$(NAME)-$(VERSION).tar.bz2: clean check + rm -rf $(NAME)-$(VERSION) ../$(NAME)-$(VERSION).tar* + mkdir -p $(NAME)-$(VERSION) + find -L . -not -name "$(NAME)-$(VERSION)" -a -not -name .svn | cpio -pdL $(NAME)-$(VERSION)/ + tar cf ../$(NAME)-$(VERSION).tar $(NAME)-$(VERSION) + bzip2 -9f ../$(NAME)-$(VERSION).tar + rm -rf $(NAME)-$(VERSION) diff --git a/draklive b/draklive new file mode 100755 index 0000000..2547690 --- /dev/null +++ b/draklive @@ -0,0 +1,1417 @@ +#!/usr/bin/perl + +# draklive $Id: draklive 142254M 2007-07-11 13:19:37Z (local) $ + +# Copyright (C) 2005 Mandriva +# Olivier Blin <oblin@mandriva.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +use lib qw(/usr/lib/libDrakX); +use MDK::Common; +use common; +use list_modules; +use fs; +use modules; +use detect_devices; +use run_program; +use POSIX qw(strftime); +use Cwd 'abs_path'; +use Getopt::Long; +use Pod::Usage; + +my $dir_distrib_sqfs = { + mountpoint => '/distrib', + type => 'squashfs', + source => 'distrib.sqfs', + # perl -MMDK::Common -e 'print map_index { (32767 - $::i) . " $_" } grep { !m,^/(?:dev|proc|sys|live/distrib), } uniq(<>)' < bootlog.list > config/distrib.sort + sort => "config/distrib.sort", + build_from => '/', +}; +my $dir_memory = { + mountpoint => '/memory', + type => 'tmpfs', +}; + +# this is not unused (it can be used from config file): +my %predefined = ( + mounts => { + simple_union => { + root => '/union', + overlay => 'unionfs', + dirs => [ + { mountpoint => '/media' }, + $dir_memory, + ], + }, + volatile_squash_union => { + root => '/union', + overlay => 'unionfs', + dirs => [ + $dir_distrib_sqfs, + $dir_memory, + ], + }, + multi_squash_union => { + root => '/union', + overlay => 'unionfs', + dirs => [ + $dir_distrib_sqfs, + { + mountpoint => '/system', + type => 'squashfs', + source => 'system.sqfs' + }, + $dir_memory, + ], + }, + }, +); + +# this is not unused (it can be used from config file): +my %custom = ( + media => { + nfs => sub { + my ($module, $client, $source) = @_; + { + extra_modules => [ $module ], + fs => 'nfs', + storage => 'nfs', + pre => "ifconfig eth0 $client up", + source => $source, + }; + }, + }, + mounts => { + squash_union => sub { + my ($default_size, $o_min_size) = @_; + { + root => '/union', + overlay => 'unionfs', + dirs => [ + $dir_distrib_sqfs, + { + mountpoint => '/system', + type => 'loopfs', + pre_allocate => $default_size, + if_(defined $o_min_size, min_size => $o_min_size), + fs => 'ext2', + source => 'system.loop' + }, + { + mountpoint => '/system', + type => 'tmpfs', + fallback => 1, + }, + ], + }; + }, + }, +); + +my %storage = ( + cdrom => { + modules => 'bus/usb disk/cdrom|hardware_raid|ide|raw|sata|scsi|usb', + fs => 'iso9660', + source => 'LABEL=MDVCDROOT', + read_only => 1, + detect => \&detect_devices::burners, + create => \&create_cdrom_master, + format => \&format_cdrom_device, + record_needs_master => 1, + record => \&record_cdrom_master, + }, + usb => { + modules => 'bus/usb disk/raw|usb', + fs => 'vfat', + bootloader => 'grub', + source => 'LABEL=MDVUSBROOT', + detect => sub { grep { detect_devices::isKeyUsb($_) } detect_devices::get() }, + create => \&create_usb_master, + format => \&format_usb_device, + record => \&record_usb_master, + }, +); + +sub nls_modules { + my ($live) = @_; + if_(get_media_setting($live->{media}, 'fs') eq 'vfat', 'nls_cp437'), #- default FAT codepage + map { "nls_$_" } (map { "iso8859-$_" } 1..7, 9, 13..15), 'utf8'; +} + +sub progress_start { + my ($total, $time, $o_exp_divide) = @_; + { + total => $total, + current => 0, + start_time => $time, + exp_divide => $o_exp_divide, + maxl => length($total) - $o_exp_divide, + }; +} + +sub progress_show_incr { + my ($progress, $incr, $time) = @_; + $progress->{current} += $incr; + my $elapsed_time = $time - $progress->{start_time}; + my $eta = int($elapsed_time*$progress->{total}/$progress->{current}); + printf("\r%3d%% (%$progress->{maxl}s/%-$progress->{maxl}s), %8s/%8s (ETA)", + int(100*$progress->{current}/$progress->{total}), + (map { substr($_, 0, length($_)-$progress->{exp_divide}) } $progress->{current}, $progress->{total}), + (map { POSIX::strftime("%H:%M:%S", gmtime($_)) } $elapsed_time, $eta)); +} + +sub progress_end() { print "\n" } + +my $loop_number = 0; +my %loop = ( + squashfs => { + read_only => 1, + modules => [ qw(loop squashfs) ], + build => sub { + my ($live, $dir) = @_; + my $dest = get_workdir($live) . $live->{prefix}{build}{loopbacks} . '/' . $_->{source}; + my $root = get_system_root($live) . $_->{build_from}; + my $total = directory_usage($root); + print "have to process " . int($total/1000000) . " MB\n"; + my $progress = progress_start($total, time(), 6); + my $sort = Cwd::abs_path($dir->{sort}); + run_foreach(sub { + if (/^mksquashfs: file .*, uncompressed size (\d+) bytes\s*(?:DUPLICATE|LINK)?$/) { + progress_show_incr($progress, $1, time()); + } + }, + 'mksquashfs', $root, $dest, '-noappend', '-info', if_(-f $sort, '-sort', $sort)) + or die "unable to run mksquashfs"; + progress_end(); + }, + mount => sub { + my ($live, $dir) = @_; + $dir->{loop} = "/dev/loop" . $loop_number++; + ( + "/bin/losetup $dir->{loop} $live->{prefix}{live}{mnt}$live->{prefix}{media}{mnt}$live->{prefix}{media}{loopbacks}/$dir->{source}", + "mount -o ro -t squashfs $dir->{loop} $live->{prefix}{live}{mnt}$dir->{mountpoint}", + ); + }, + }, + loopfs => { + modules => [], + build => sub { + my ($live, $dir) = @_; + my $dest = get_workdir($live) . $live->{prefix}{build}{loopbacks} . '/' . $_->{source}; + device_allocate_fs($dest, $dir->{pre_allocate}); + device_mkfs($dest, $dir->{fs}) if !defined $dir->{min_size}; + }, + mount => sub { + my ($live, $dir) = @_; + $dir->{loop} = "/dev/loop" . $loop_number++; + my $fsck = "chroot </dev/tty1 $live->{prefix}{live}{mnt}$dir_distrib_sqfs->{mountpoint} /sbin/fsck $dir->{loop}"; + ( + "losetup $dir->{loop} $live->{prefix}{live}{mnt}$live->{prefix}{media}{mnt}$live->{prefix}{media}{loopbacks}/$dir->{source}", + qq(sh -c "$fsck -a || $fsck -y"), + "mount -t $dir->{fs} $dir->{loop} $live->{prefix}{live}{mnt}$dir->{mountpoint}", + ); + }, + }, + tmpfs => { + mount => sub { + my ($live, $dir) = @_; + my $mnt = $live->{prefix}{live}{mnt} . $dir->{mountpoint}; + my $cmd = "mount -t tmpfs none $mnt"; + $dir->{fallback} ? qq(sh -c 'if ! grep -q " $mnt " /proc/mounts; then $cmd; fi') : $cmd; + }, + }, +); + +my %overlay = ( + unionfs => { + modules => [ qw(unionfs) ], + mount => sub { + my ($live) = @_; + #- build dirs list: "dir1=ro:dir2:ro:dir3=rw" + my $dirs = join(':', + map { "$live->{prefix}{live}{mnt}$_->{mountpoint}=" . + ($_->{type} && !$loop{$_->{type}}{read_only} ? 'rw' : 'ro'); + } reverse grep { !$_->{fallback} } @{$live->{mount}{dirs} || []}); + "mount -o dirs=$dirs -t unionfs unionfs $live->{prefix}{live}{mnt}$live->{mount}{root}"; + }, + }, +); + +my %moddeps; +sub load_moddeps { + my ($root, $kernel_path) = @_; + my $get_modname = sub { first($_[0] =~ m!^$kernel_path/kernel/(?:.*/|)(.*?)\.k?o!) }; + %moddeps = (map { + my ($f, $deps) = split ':'; + my $modname = $get_modname->($f); + $modname => { full => $f, deps => [ map { $get_modname->($_) } split ' ', $deps ] }; + } cat_($root . $kernel_path . '/modules.dep')); +} +sub moddeps_closure { + my ($module) = @_; + my @deps = @{$moddeps{$module}{deps}}; + (map { moddeps_closure($_) } @deps), @deps; +} + +sub directory_usage { first(split /\s/, `du -sb $_[0]`) } + +sub run_ { + my $options = ref $_[0] eq 'HASH' ? shift @_ : {}; + $options->{timeout} ||= 'never'; + print STDERR "running " . (exists $options->{root} && "(in chroot) ") . join(' ', @_) . "\n"; + run_program::raw($options, @_); +} + +sub run_foreach { + my ($foreach, @command) = @_; + print STDERR "running " . join(' ', @command) . "\n"; + open(my $OUTPUT, '-|', @command); + local $_; #- avoid outside $_ to be overwritten + $foreach->() while <$OUTPUT>; + close $OUTPUT; +} + +sub get_region_suffix { + my ($live) = @_; + defined $live->{system}{region} ? '/' . $live->{system}{region} : ref $live->{regions} ? '/noregion' : ''; +} + +sub get_workdir { + my ($live) = @_; + $live->{workdir} . get_region_suffix($live); +} + +sub get_system_root { + my ($live) = @_; + $live->{system}{root} . get_region_suffix($live); +} + +sub get_initrd_path { + my ($live, $media) = @_; + '/' . $media->{storage} . '/initrd.gz'; +} + +sub get_syslinux_path { + my ($live, $media, $opts) = @_; + '/' . $media->{storage} . '/syslinux' . ($opts->{boot} && '-boot-' . $opts->{boot}) . '.cfg'; +} + +sub find_kernel { + my ($live) = @_; + my $kernel = $live->{system}{kernel}; + unless ($kernel) { + my $vmlinuz = readlink(get_system_root($live) . '/boot/vmlinuz'); + $vmlinuz ||= find { -e $_ } glob_(get_system_root($live) . '/boot/vmlinuz-*'); + $kernel = first($vmlinuz =~ /\bvmlinuz-(.*)$/) or die "no kernel can be found"; + } + $kernel; +} + +sub create_initrd { + my ($live) = @_; + foreach ($live->{media}, @{$live->{extra_media}}) { + create_initrd_for_media($live, $_); + } + cp_f(get_workdir($live) . $live->{prefix}{build}{boot} . get_initrd_path($live, $live->{media}), + $live->{copy_initrd}) if $live->{copy_initrd}; +} + +sub create_initrd_for_media { + my ($live, $media) = @_; + + my $initrd_tree = get_workdir($live) . $live->{prefix}{build}{initrd} . '/' . $media->{storage}; + rm_rf($initrd_tree) if -e $initrd_tree; + + my ($lib_prefix, $need_libs, @libs); + foreach (qw(/lib64 /lib)) { + my @ld = glob(get_system_root($live) . $_ . '/ld-*.so.*'); + my @libc = glob(get_system_root($live) . $_ . '/libc.so.*'); + if (@ld && @libc) { + @libs = (@ld, @libc); + $lib_prefix = $_; + last; + } + } + $lib_prefix or die 'unable to find system libraries in /lib or /lib64'; + + mkdir_p($initrd_tree . $_) foreach + qw(/bin /dev /proc /sys), $lib_prefix, + map { $live->{prefix}{live}{mnt} . $_ } + $live->{prefix}{media}{mnt}, + $live->{mount}{root}, + map { $_->{mountpoint} } @{$live->{mount}{dirs} || []}; + + #- use nash with label support + cp_f(get_system_root($live) . '/sbin/nash', $initrd_tree . '/bin/'); + + #- busybox is required to: + #- detect usb-storage process (we need sh/while/ps/grep) + #- mount loopbacks read-only with losetup (useful over NFS) + my $busybox = get_system_root($live) . '/usr/bin/busybox'; + cp_f($busybox, $initrd_tree . '/bin') + or die 'unable to copy busybox from system chroot'; + my @l = map { /functions:/ .. /^$/ ? do { s/\s//g; split /,/ } : () } `$busybox`; + shift @l; + symlink('busybox', $initrd_tree . "/bin/$_") foreach @l; + + my $rrpt_dev = get_media_setting($media, 'rereadpt'); + if ($rrpt_dev) { + $need_libs = 1; + cp_f(get_system_root($live) . '/sbin/blockdev', $initrd_tree . '/bin') + or die 'unable to copy blockdev from system chroot'; + } + + if (get_media_setting($media, 'fs') eq 'nfs') { + $need_libs = 1; + cp_f(get_system_root($live) . '/sbin/ifconfig', $initrd_tree . '/bin/'); + #- needed to mount NFS (with nolock) + cp_f(get_system_root($live) . '/bin/mount', $initrd_tree . '/bin/'); + if ($live->{debug}) { + cp_f(get_system_root($live) . '/bin/ping', $initrd_tree . '/bin/'); + cp_f(glob(get_system_root($live) . $lib_prefix . '/libresolv*.so.*'), $initrd_tree . $lib_prefix); + } + } + if ($live->{debug}) { + $need_libs = 1; + cp_f(get_system_root($live) . '/usr/bin/strace', $initrd_tree . '/bin/'); + } + + cp_f(@libs, $initrd_tree . $lib_prefix) if $need_libs; + + require devices; + devices::make($initrd_tree . "/dev/$_") foreach + if_($rrpt_dev, $rrpt_dev), + qw(console initrd null ram systty), + (map { "tty$_" } 0..5), + (map { "loop$_" } 0..7); + + my $kernel = find_kernel($live); + print "using kernel $kernel\n"; + load_moddeps(get_system_root($live), "/lib/modules/" . $kernel); + + my ($modules, $skipped) = partition { exists $moddeps{$_} } + uniq(map { modules::cond_mapping_24_26($_) } category2modules(get_media_setting($media, 'modules'))); + my ($extra_modules, $extra_missing) = partition { exists $moddeps{$_} } + nls_modules($live), + get_media_fs_module($media), + @{get_media_setting($media, 'extra_modules') || []}, + (map { @{$loop{$_}{modules} || []} } uniq(map { $_->{type} } grep { $_->{type} } @{$live->{mount}{dirs} || []})), + ($live->{mount}{overlay} ? @{$overlay{$live->{mount}{overlay}}{modules} || []} : ()); + + my @additional_modules = map { m!([^/]+)\.ko! } @{$live->{system}{additional_modules}}; + cp_f($_, $initrd_tree . "/lib/") foreach @{$live->{system}{additional_modules}}; + + my @missing = sort(difference2($extra_missing, \@additional_modules)); + @missing and die "missing mandatory modules:\n" . join("\n", @missing); + push @$modules, @$extra_modules; + + my @module_deps = uniq(map { moddeps_closure($_) } @$modules); + mkdir_p($initrd_tree . "/lib"); + run_('gzip', '>', $initrd_tree . "/lib/$_.ko", '-dc', get_system_root($live) . $moddeps{$_}{full}) + foreach @module_deps, @$modules; + + @$skipped and warn "skipped modules:" . join("\n", '', sort(@$skipped)); + + #- move ide-generic to end of loaded modules, so that it registers unhandled drives only + #- this avoid it to take-over drives which would have been managed by SATA modules + my ($head, $tail) = partition { $_ ne 'ide-generic' } @$modules; + @$modules = (@$head, @$tail); + + create_initrd_linuxrc($live, $media, @module_deps, @$modules, @additional_modules); + compress_initrd_tree($live, $media); + add_splash($live, $media); +} + +sub create_initrd_linuxrc { + my ($live, $media, @modules) = @_; + my $target = $live->{prefix}{live}{mnt} . ($live->{mount}{root} || $live->{prefix}{media}{mnt}); + my $pre = get_media_setting($media, 'pre'); + my $fs = get_media_setting($media, 'fs'); + my $rrpt_dev = get_media_setting($media, 'rereadpt'); + my $debug_shell = "sh -c 'if grep -q debug /proc/cmdline; then exec sh; fi'"; + my @mount_options = ( + if_(get_media_setting($media, 'read_only'), "ro"), + grep { $_ } get_media_setting($media, 'mount_options'), + ); + output_with_perm(get_workdir($live) . $live->{prefix}{build}{initrd} . '/' . $media->{storage} . '/linuxrc', 0755, + join("\n", + "#!/bin/nash", + (map { "insmod /lib/$_.ko" } @modules), + #- required for labels and ps + "mount -t proc none /proc", + #- required for cdrom labels + "mount -t sysfs none /sys", + if_(member('usb-storage', @modules), + #- wait some seconds for the usb-stor-scan process to be run + "sleep 2", + q(sh -c 'while ps | grep -q \\\[usb-stor-scan\\\]; do sleep 1; done')), + if_($rrpt_dev, + "echo *** Waiting for new partitions on device ${rrpt_dev} ***", + "sh -c 'while ! ls /sys/block/${rrpt_dev}/${rrpt_dev}* >/dev/null 2>&1; do sleep 3; blockdev --rereadpt /dev/${rrpt_dev} >/dev/null 2>&1; done'"), + $debug_shell, + if_($pre, deref_array($pre)), + ($fs eq 'nfs' ? '/bin/mount -n -o ro,nolock' : 'mount') . + if_(@mount_options, " -o " . join(",", @mount_options)) . + " -t $fs " . get_media_setting($media, 'source') . " $live->{prefix}{live}{mnt}$live->{prefix}{media}{mnt}", + (map { $loop{$_->{type}}{mount}->($live, $_) } grep { $_->{type} } @{$live->{mount}{dirs} || []}), + ($live->{mount}{overlay} ? $overlay{$live->{mount}{overlay}}{mount}->($live) : ()), + "echo 0x0100 > /proc/sys/kernel/real-root-dev", + "umount /sys", + "umount /proc", + "pivot_root $target $target/initrd", + "sh -c 'rmdir /initrd$target; cd /initrd$live->{prefix}{live}{mnt}; for i in `ls -1`; do mkdir -p $live->{prefix}{live}{mnt}/\$i; mount -n --move \$i $live->{prefix}{live}{mnt}/\$i; done'", + if_($live->{system}{initrd_post}, deref_array($live->{system}{initrd_post})), + "")); +} + +sub compress_initrd_tree { + my ($live, $media) = @_; + + my $initrd_tree = get_workdir($live) . $live->{prefix}{build}{initrd} . '/' . $media->{storage}; + my $size = run_program::get_stdout("du -ks $initrd_tree | awk '{print \$1}'") + 250; + my $inodes = run_program::get_stdout("find $initrd_tree | wc -l") + 1250; + $size = int($size + $inodes / 10) + 1; #- 10 inodes needs 1K + my $initrd = get_workdir($live) . $live->{prefix}{build}{boot} . get_initrd_path($live, $media); + $initrd =~ s/.gz$//; + + mkdir_p(dirname($initrd)); + run_('dd', 'if=/dev/zero', "of=$initrd", 'bs=1k', "count=$size"); + run_('mke2fs', '-q', '-m', 0, '-F', '-N', $inodes, '-s', 1, $initrd); + mkdir_p($live->{mnt}); + run_('mount', '-o', 'loop', '-t', 'ext2', $initrd, $live->{mnt}); + cp_af(glob("$initrd_tree/*"), $live->{mnt}); + rm_rf($live->{mnt} . "/lost+found"); + run_('umount', $live->{mnt}); + run_('gzip', '-f', '-9', $initrd); +} + +sub add_splash { + my ($live, $media) = @_; + if ($live->{system}{vga_mode} && $live->{system}{splash} ne 'no') { + require bootloader; + my $initrd = get_workdir($live) . $live->{prefix}{build}{boot} . get_initrd_path($live, $media); + my $tmp_initrd = '/tmp/initrd.gz'; + cp_f($initrd, get_system_root($live) . $tmp_initrd); + { + local $::prefix = get_system_root($live); + bootloader::add_boot_splash($tmp_initrd, $live->{system}{vga_mode}); + } + cp_f(get_system_root($live) . $tmp_initrd, $initrd); + unlink(get_system_root($live) . $tmp_initrd); + } +} + +sub get_default_append { + my ($live) = @_; + join(' ', + 'fastboot', #- needed to avoid fsck + if_($live->{system}{vga_mode}, + 'splash=silent', + 'vga=' . $live->{system}{vga_mode}), + ); +} + +my @syslinux_boot_files = qw(/vmlinuz /bootlogo /help.msg); + +sub build_syslinux_cfg { + my ($live, $media, $opts) = @_; + my $append = get_default_append($live); + #- syslinux wants files at root (used with vfat fs) + my $to_root = get_boot_setting($media, 'fs', $opts) eq 'vfat'; + my ($initrd, $kernel, $bootlogo, $help) = map { $to_root ? basename($_) : $_ } + map { $live->{prefix}{media}{boot} . $_ } get_initrd_path($live, $media), @syslinux_boot_files; + my $has_bootlogo = -e get_workdir($live) . $live->{prefix}{media}{boot} . '/bootlogo'; + join("\n", + "default $live->{media}{title}", + "prompt 1", + "timeout 40", + $has_bootlogo ? ( + "gfxboot $bootlogo", + "F1 $help", + ) : + "display $help", + (map { + my ($name, $cmdline) = @$_; + $name =~ s/\s/_/g; + "label " . ($name || $live->{media}{title}), + " kernel $kernel", + " append initrd=$initrd $append $cmdline"; + } group_by2('' => '', @{$live->{system}{boot_entries}})), + "", + ); +} + +sub build_grub_cfg { + my ($live, $media, $device) = @_; + #- FIXME: use the bootloader module from drakx + my ($part_nb) = $device =~ /(\d+)$/; + my $grub_part = "(hd0" . (defined $part_nb ? "," . ($part_nb-1) : "") . ")"; + join("\n", + "timeout 4", + "color 2", + "splashimage $grub_part" . $live->{prefix}{media}{boot} . "/grub/splash.xpm.gz", + "default 0", + "viewport 3 2 77 22", + "shade 1", + (map { + my ($name, $cmdline) = @$_; + "title " . $live->{media}{title} . if_($name, " ($name)"), + "kernel $grub_part" . $live->{prefix}{media}{boot} . "/vmlinuz " . get_default_append($live) . if_($cmdline, " $cmdline"), + "initrd " . $live->{prefix}{media}{boot} . get_initrd_path($live, $media); + } group_by2('' => '', @{$live->{system}{boot_entries}})), + "", + ); +} + +sub install_system { + my ($live) = @_; + local %ENV = (%ENV, %{$live->{system}{install_env}}); + $ENV{DRAKLIVE_LANGS} = join(':', uniq((ref $live->{regions} ? @{$live->{regions}{$live->{system}{region}}} : ()), @{$live->{system}{langs_always}})); + $ENV{DRAKLIVE_REGION} = $live->{system}{region}; + run_('drakx-in-chroot', + $live->{system}{repository}, + get_system_root($live), + if_($live->{system}{auto_install}, '--auto_install', Cwd::abs_path($live->{system}{auto_install})), + if_($live->{system}{patch_install}, '--defcfg', Cwd::abs_path($live->{system}{patch_install})), + if_($live->{system}{rpmsrate}, '--rpmsrate', Cwd::abs_path($live->{system}{rpmsrate})), + ($live->{system}{stage2_updates} ? (map { ('--stage2-update', Cwd::abs_path($_->[0]), $_->[1]) } @{$live->{system}{stage2_updates}}) : ()), + ) or die "unable to install system chroot"; + post_install_system($live); +} + +sub configure_draklive_resize { + my ($live) = @_; + + my $resizable_loopback = find { $_->{min_size} } @{$live->{mount}{dirs} || []}; + if ($resizable_loopback) { + output(get_system_root($live) . '/etc/sysconfig/draklive-resize', <<EOF); +DRAKLIVE_RESIZE=yes +LOOPBACK=$live->{prefix}{live}{mnt}$live->{prefix}{media}{mnt}$live->{prefix}{media}{loopbacks}/$resizable_loopback->{source} +TYPE=$resizable_loopback->{fs} +MIN_SIZE=$resizable_loopback->{min_size} +MOUNT=$live->{prefix}{live}{mnt}$resizable_loopback->{mountpoint}_resized +OLD_MOUNT=$live->{prefix}{live}{mnt}$resizable_loopback->{mountpoint} +UNION=/ +EOF + } +} + +sub copy_files_to { + my ($files, $root) = @_; + foreach (@$files) { + my ($source, $dest, $o_perm) = @$_; + $dest = $root . '/' . $dest; + mkdir_p(dirname($dest)); + cp_f(glob($source), $dest); + chmod $o_perm, $dest if $o_perm; + } +} + +sub post_install_system { + my ($live) = @_; + + my $previous_umask = umask; + #- workaround buggy installation of directories that are not owned by any packages + umask 022; + + #- remove previous draklive leftovers if needed + run_({ root => get_system_root($live) }, 'urpmi.removemedia', '-a'); + + foreach (@{$live->{system}{additional_media}}) { + run_({ root => get_system_root($live) }, 'urpmi.addmedia', if_($_->{distrib}, '--distrib'), $_->{name}, $_->{path}) + or die "unable to add media from $_->{path}"; + @{$_->{packages}} or next; + run_({ root => get_system_root($live) }, 'urpmi', '--auto', '--no-verify-rpm', if_(!$_->{distrib}, '--searchmedia', $_->{name}), @{$_->{packages}}) + or die "unable to install packages from $_->{path}"; + } + + #- additional rpms may have dependencies in additional media + if (@{$live->{system}{rpms}}) { + my $rpm_tmp_dir = '/tmp/draklive_rpms'; + mkdir_p(get_system_root($live) . $rpm_tmp_dir); + cp_f(@{$live->{system}{rpms}}, get_system_root($live) . $rpm_tmp_dir); + run_({ root => get_system_root($live) }, 'urpmi', '--auto', '--no-verify-rpm', + map { $rpm_tmp_dir . '/' . basename($_) } @{$live->{system}{rpms}}) + or die "unable to install additional system rpms"; + rm_rf(get_system_root($live) . $rpm_tmp_dir); + } + + #- remove urpmi media added by drakx-in-chroot and additional media, they're unusable + run_({ root => get_system_root($live) }, 'urpmi.removemedia', '-a'); + + my $erase = join(' ', @{$live->{system}{erase_rpms}}); + run_({ root => get_system_root($live) }, 'sh', '-c', "rpm -qa $erase | xargs rpm -e ") if $erase; + + run_({ root => get_system_root($live) }, 'chkconfig', '--del', $_) foreach @{$live->{system}{disable_services}}; + + #- make sure harddrake is run: + #- if previous HW config file is empty, we assumes DrakX has just completed the installation + #- (do it in chroot, or else Storable from the build box may write an incompatible config file) + run_({ root => get_system_root($live) }, + 'perl', '-MStorable', '-e', qq(Storable::store({ UNKNOWN => {} }, '/etc/sysconfig/harddrake2/previous_hw'))); + + #- remove some build-machine specific configuration + substInFile { undef $_ if /^[^#]/ } get_system_root($live) . $_ + foreach qw(/etc/fstab /etc/mtab /etc/modprobe.conf /etc/modprobe.preload /etc/iftab /etc/shorewall/interfaces /etc/mdadm.conf); + unlink($_) foreach map { glob_(get_system_root($live) . $_) } ( + "/etc/modprobe.preload.d/*", + "/etc/udev/rules.d/61-*_config.rules", + "/etc/dbus-1/machine-id", + ); + output_with_perm(get_system_root($live) . '/etc/fstab', 0644, "none / unionfs rw 0 0\n"); + + #- run harddrake because a crappy snd-usb-audio workaround may do something at shutdown + #- (do it after the modprobe files are cleaned) + run_({ root => get_system_root($live) }, '/usr/share/harddrake/service_harddrake', 'stop'); + + #- interactive mode can lead to race in initscripts + #- (don't use addVarsInSh from MDK::Common, it breaks shell escapes) + substInFile { s/^PROMPT=.*/PROMPT=no/ } get_system_root($live) . '/etc/sysconfig/init'; + + configure_draklive_resize($live); + + if ($live->{system}{preselect_kdm_user}) { + #- preselect specified user in kdm + my $kdm_cfg = get_system_root($live) . '/etc/kde/kdm/kdmrc'; + update_gnomekderc($kdm_cfg, 'X-:0-Greeter' => (PreselectUser => 'Default', DefaultUser => $live->{system}{preselect_kdm_user})) if -f $kdm_cfg; + } + + #- apply patches and install files after the configuration is cleaned + #- to allow special configuration files (especially modprobe.preload) + foreach (@{$live->{system}{patches}}) { + my $patch = Cwd::abs_path($_) or die "unable to find file " . $_; + my @command = ('patch', '-p0', '-d', get_system_root($live), '-i', $patch); + run_(@command, '--dry-run', '-f', '-R') || run_(@command) or die "unable to apply patch " . $patch; + } + + copy_files_to($live->{system}{files}, get_system_root($live)); + + run_("chroot", get_system_root($live), "bash", "-c", $live->{system}{postInstall}) if $live->{system}{postInstall}; + + umask $previous_umask; +} + +sub umount_external_filesystem { + my ($live) = @_; + my $mnt = get_system_root($live) . "/mnt/"; + eval { fs::mount::umount("$mnt/$_") } foreach all($mnt); +} + +sub create_loopback_files { + my ($live) = @_; + # umount filesystem in the live before creating the loopback + umount_external_filesystem($live); + mkdir_p(get_workdir($live) . $live->{prefix}{build}{loopbacks}); + foreach (grep { exists $loop{$_->{type}}{build} } @{$live->{mount}{dirs} || []}) { + $loop{$_->{type}}{build}->($live, $_); + } +} + +#- mainly for storage-specific subroutines +sub get_storage_setting { + my ($media, $setting) = @_; + $storage{$media->{storage}}{$setting}; +} + +#- for actions that support an optionnal boot storage type +sub get_boot_setting { + my ($media, $setting, $opts) = @_; + $opts->{boot} ? $storage{$opts->{boot}}{$setting} : get_storage_setting($media, $setting); +} + +#- for user-customisable media setting, that can override storage setting +sub get_media_setting { + my ($media, $setting) = @_; + $media->{$setting} || get_storage_setting($media, $setting); +} + +sub get_media_fs_module { + my ($media) = @_; + my $fs = get_media_setting($media, 'fs'); + $fs eq 'iso9660' ? 'isofs' : $fs eq 'ext2' ? () : $fs; +} + +sub get_media_label { + my ($media) = @_; + first(get_media_setting($media, 'source') =~ /^LABEL=(.*)$/); +} + +sub get_media_device { + my ($media) = @_; + return $media->{device} if $media->{device}; + my $label = get_media_label($media) or return get_media_setting($media, 'source'); + my $device = chomp_(`readlink -f /dev/disk/by-label/$label`) + or die "unable to find device for /dev/disk/by-label/$label"; + $device; +} + +sub prepare_bootloader { + my ($live) = @_; + create_initrd($live); + cp_f(get_system_root($live) . '/boot/vmlinuz-' . find_kernel($live), get_workdir($live) . $live->{prefix}{build}{boot} . '/vmlinuz'); + require bootsplash; + my $theme = do { + local $::prefix = get_system_root($live); + bootsplash::themes_read_sysconfig('800x600'); + }; + my $bootlogo = get_system_root($live) . "/usr/share/gfxboot/themes/$theme->{name}/install/bootlogo"; + if (-f $bootlogo) { + print "using $bootlogo as gfxboot splash image\n"; + cp_f($bootlogo, get_workdir($live) . $live->{prefix}{build}{boot} . '/bootlogo'); + } else { + warn "unable to find gfxboot splash ($bootlogo)"; + } + output(get_workdir($live) . $live->{prefix}{build}{boot} . '/help.msg', + pack("C*", 0x0E, 0x80, 0x03, 0x00, 0xC) . qq( +Welcome to Mandriva live! + +The command line can be used to specify kernel options. + +$live->{media}{title} <kernel options> + +)); + + foreach my $media ($live->{media}, @{$live->{extra_media}}) { + foreach my $boot ('', @{$media->{extra_boot}}) { + my $opts = { boot => $boot }; + output(get_workdir($live) . $live->{prefix}{build}{boot} . get_syslinux_path($live, $media, $opts), + build_syslinux_cfg($live, $media, $opts)); + } + } +} + +sub device_allocate_fs { + my ($device, $size) = @_; + #- try to get size in bytes from human-readable size + $size = + $size =~ /^(\d+)k/i ? $1 * 1000 : + $size =~ /^(\d+)M/i ? $1 * 1000000 : + $size =~ /^(\d+)G/i ? $1 * 1000000000 : + $size; + run_('dd', "of=$device", 'count=0', 'bs=1', "seek=$size"); +} + +#- format $device as type $type +sub device_mkfs { + my ($device, $type) = @_; + if ($type eq 'vfat') { + run_('mkfs.vfat', $device); + } elsif (member($type, 'ext2', 'ext3')) { + run_("mkfs.$type", "-m", 0, if_(!-b $device, '-F'), $device); + } else { + die "unable to mkfs for unsupported media type $type"; + } +} + +sub set_device_label { + my ($device, $type, $label) = @_; + if ($type eq 'vfat') { + run_('mlabel', '-i', $device, '::' . $label); + } elsif (member($type, 'ext2', 'ext3')) { + run_('e2label', $device, $label); + } else { + die "unable to set label for unsupported media type $type"; + } +} + +sub get_cdrom_master_path { + my ($live, $opts) = @_; + get_workdir($live) . $live->{prefix}{build}{images} . '/' . ($opts->{boot} ? 'boot-' . $opts->{boot} : 'live') . '.iso'; +} + +sub create_cdrom_master { + my ($live, $media, $opts) = @_; + my $label = get_media_label($media) or die "the source device must be described by a label"; + my $dest; + unless ($opts->{onthefly}) { + $dest = get_cdrom_master_path($live, $opts); + mkdir_p(dirname($dest)); + } + run_('mkisofs', '-pad', '-l', '-R', '-J', '-v', '-v', + '-V', $label, #'-A', $application, '-p', $preparer, '-P', $publisher, + '-b', 'isolinux/isolinux.bin', + '-c', 'isolinux/boot.cat', + '-hide-rr-moved', '-no-emul-boot', + '-boot-load-size', 4, '-boot-info-table', + '-graft-points', + if_($dest, '-o', $dest), + 'isolinux/isolinux.bin=/usr/lib/syslinux/isolinux.bin', + 'isolinux/isolinux.cfg=' . get_workdir($live) . $live->{prefix}{build}{boot} . get_syslinux_path($live, $media, $opts), + $live->{prefix}{media}{boot} . '=' . get_workdir($live) . $live->{prefix}{build}{boot}, + if_(!$opts->{boot_only}, $live->{prefix}{media}{loopbacks} . '=' . get_workdir($live) . $live->{prefix}{build}{loopbacks}), + ); + run_('mkcd', '--addmd5', $dest) if $dest; +} + +sub get_usb_master_path { + my ($live) = @_; + get_workdir($live) . $live->{prefix}{build}{images} . '/live.img'; +} + +sub create_usb_master { + my ($live, $media, $opts) = @_; + my $dest = get_usb_master_path($live); + mkdir_p(dirname($dest)); + #- dumb guess, a bit too large, and valid for FAT only + my $size = directory_usage(get_workdir($live) . $live->{prefix}{build}{loopbacks}) + + directory_usage(get_workdir($live) . $live->{prefix}{build}{boot}); + device_allocate_fs($dest, int($size * 1.024)); + device_mkfs($dest, get_media_setting($media, 'fs')) + or die "unable to format $dest"; + local $opts->{device} = $dest; + record_usb_master($live, $media, $opts); +} + +#- $opts: +#- media: alternate media +#- onthefly : if true, the create function must output to stdout +sub create_master { + my ($live, $opts) = @_; + my $media = $opts->{media} || $live->{media}; + + if (my $create = get_boot_setting($media, 'create', $opts)) { + $create->($live, $media, $opts); + } else { + warn "not implemented yet"; + } +} + +sub maybe_umount_device { + my ($device) = @_; + run_('umount', $device) if cat_('/proc/mounts') =~ m!^$device\s+!m; +} + +sub format_cdrom_device { + my ($_live, $media) = @_; + run_('cdrecord', '-v', 'dev=' . $media->{device}, "blank=fast"); +} + +sub format_usb_device { + my ($_live, $media) = @_; + maybe_umount_device($media->{device}); + device_mkfs($media->{device}, get_media_setting($media, 'fs')) + or die "unable to format device $media->{device}"; +} + +#- $opts: +#- media: alternate media +sub format_device { + my ($live, $opts) = @_; + my $media = $opts->{media} || $live->{media}; + + $media->{device} or die "no device defined in media configuration"; + if (my $format = get_boot_setting($media, 'format', $opts)) { + $format->($live, $media); + } else { + warn "not implemented yet"; + } +} + +sub record_cdrom_master { + my ($live, $media, $opts) = @_; + $media->{device} or die "no device defined in media configuration"; + my $src = $opts->{onthefly} ? '-' : get_cdrom_master_path($live, $opts); + run_('cdrecord', '-v', 'dev=' . $media->{device}, $src); +} + +sub record_usb_master { + my ($live, $media, $opts) = @_; + my $o_device = $opts->{device} || $media->{device}; + if (my $label = $o_device && get_media_label($media)) { + set_device_label($o_device, get_media_setting($media, 'fs'), $label); + } + my $device = $opts->{device} || get_media_device($media) + or die "unable to find recording device (missing label? try with --device <device>)"; + my $bootloader = get_media_setting($media, 'bootloader'); + member($bootloader, 'grub', 'syslinux') or die "no bootloader defined in media configuration"; + mkdir_p($live->{mnt}); + run_('mount', if_(-f $device, '-o', 'loop'), $device, $live->{mnt}) + or die "unable to mount $device"; + rm_rf($live->{mnt} . $live->{prefix}{media}{boot}) if -e $live->{mnt} . $live->{prefix}{media}{boot}; + cp_af(get_workdir($live) . $live->{prefix}{build}{boot}, $live->{mnt} . $live->{prefix}{media}{boot}); + + my $grub_device_map = $live->{mnt} . $live->{prefix}{media}{boot} . "/grub/device.map"; + if ($bootloader eq 'syslinux') { + cp_f(get_workdir($live) . $_, $live->{mnt}) foreach map { + $live->{prefix}{boot} . $_; + } get_syslinux_path($live, $media, $opts), get_initrd_path($live, $media), @syslinux_boot_files; + } elsif ($bootloader eq 'grub') { + #- FIXME: add get_grub_path (when building boot configuration files) + # and get_bootloader_path (when copying) + mkdir_p($live->{mnt} . $live->{prefix}{media}{boot} . '/grub'); + cp_f(get_system_root($live) . $live->{prefix}{build}{boot} . '/grub/splash.xpm.gz', $live->{mnt} . $live->{prefix}{media}{boot} . '/grub'); + output_p($live->{mnt} . $live->{prefix}{media}{boot} . '/grub/menu.lst', build_grub_cfg($live, $media, $device)); + unlink $grub_device_map; + } + + if (-b $device) { + if ($bootloader eq 'syslinux') { + #- use syslinux -s, "safe, slow and stupid" version of SYSLINUX, unless specified otherwise + run_('syslinux', if_(!$media->{fast_syslinux}, '-s'), $device) + or die "unable to run syslinux on $device"; + } elsif ($bootloader eq 'grub') { + my $local_grub_install = get_workdir($live) . $live->{prefix}{build}{scripts} . '/grub-install'; + mkdir_p(dirname($local_grub_install)); + cp_f(get_system_root($live) . '/sbin/grub-install', $local_grub_install); + my $root = get_system_root($live); + substInFile { + s!^\s*exec_prefix=.*!exec_prefix=$root!; + s!^\s*grub_prefix=/boot/!grub_prefix=$live->{prefix}{media}{boot}/!; + s!^\s*bootdir=(.*)/boot$!bootdir=$1$live->{prefix}{media}{boot}!; + } $local_grub_install; + + my $master_device = $device; + $master_device =~ s/(\d+)$//; + foreach ($master_device, $device) { + run_($local_grub_install, '--root-directory=' . $live->{mnt}, '--no-floppy', $_) + or die "unable to run grub on $device"; + } + unlink $grub_device_map; + } + } else { + warn "not running $bootloader on non block device $device"; + } + + do { + my $loopbacks_source = get_workdir($live) . $live->{prefix}{build}{loopbacks} . '/'; + my $total = directory_usage($loopbacks_source); + my $all_files = 0; + my $current_file = 0; + local $/ = "\r"; + run_foreach($live->{update_progress} ? sub { + if (/^\s*(\d+)\s+\d+%\s+/) { + $current_file = $1; + $live->{update_progress}->(undef, $all_files + $current_file, $total); + } + if (/(?:^|\n)\S+/) { + $all_files += $current_file; + $current_file = 0; + } + } : sub {}, + 'rsync', '-vdP', '--inplace', $loopbacks_source, $live->{mnt} . $live->{prefix}{media}{loopbacks}) + or die "unable to copy loopback files"; + + copy_files_to($live->{media}{files}, $live->{mnt}); + } unless $opts->{boot_only}; + + run_('umount', $live->{mnt}); + maybe_umount_device($device); + + if ($live->{system}{hide_media_dirs}) { + run_('mattrib', '+a', '+r', '+s', '+h', '-/', '-i', $device, '::' . $live->{prefix}{media}{$_}) + foreach qw(boot loopbacks); + } +} + +#- $opts: +#- media: alternate media +#- onthefly : if true, the record function must read from stdin +sub record_master { + my ($live, $opts) = @_; + my $media = $opts->{media} || $live->{media}; + + if (my $record = get_boot_setting($media, 'record', $opts)) { + $record->($live, $media, $opts); + } else { + warn "not implemented yet"; + } +} + +sub pipe_subs { + my ($writer, $reader) = @_; + my ($r, $w) = POSIX::pipe; + if (my $pid = fork()) { + POSIX::close($w) or die "couldn't close: $!\n"; + my $stdin = POSIX::dup(0) or die "couldn't dup: $!\n"; + POSIX::dup2($r, 0) or die "couldn't dup2: $!\n"; + POSIX::close($r); + $reader->(); + POSIX::close(0) or warn "writer exited $?"; + POSIX::dup2($stdin, 0) or die "couldn't dup2: $!\n"; + waitpid($pid, 0); + } else { + POSIX::close($r) or die "couldn't close: $!\n"; + #- don't screw up reader + POSIX::dup2(POSIX::open('/dev/null', &POSIX::O_WRONLY), 2) or die "couldn't dup2: $!\n"; + POSIX::dup2($w, 1) or die "couldn't dup2: $!\n"; + POSIX::close($w); + $| = 1; #- autoflush write + exit !$writer->(); + } +} + +sub record_onthefly { + my ($live, $opts) = @_; + my $media = $opts->{media} || $live->{media}; + + my $record = get_storage_setting($media, 'record'); + unless ($record) { + warn "not implemented yet"; + return; + } + if (my $create = get_storage_setting($media, 'record_needs_master') && get_storage_setting($media, 'create')) { + #- pipe creation step to recording step + pipe_subs(sub { $create->($live, $media, { onthefly => 1 }) }, + sub { $record->($live, $media, { onthefly => 1 }) }); + } else { + #- no creation step, record directly + $record->($live, $media); + } +} + +sub copy_wizard { + my ($live) = @_; + + complete_config($live); + $live->{system}{vga_mode} = 788 if !defined $live->{system}{vga_mode}; + my $live_media = $live->{prefix}{live}{mnt} . $live->{prefix}{media}{mnt}; + + require interactive; + require wizards; + my $in = 'interactive'->vnew('su'); + my (@available_storage, @available_devices); + my ($storage, $device, $format); + my %source_types = ( + live => N("Use current live system"), + file => N("Select a file"), + ); + my ($source_type, $source_path); + my $source_is_mounted = sub { -d ($live->{workdir} . $live->{prefix}{media}{boot}) }; + my $umount_source = sub { + if ($source_type ne 'live' && $source_is_mounted->()) { + run_('umount', $live->{workdir}); + rmdir($live->{workdir}); + } + }; + my $w; + $w = wizards->new({ + name => N("Live system copy wizard"), + pages => { + welcome => { + name => N("Welcome to the live system copy wizard"), + no_back => 1, + next => 'source', + }, + source => { + name => N("Which live system do you want to copy?"), + data => [ if_(-d ($live_media . $live->{prefix}{media}{loopbacks}), + { type => 'list', val => \$source_type, + list => sort(keys(%source_types)), + format => sub { $source_types{$_[0]} } }), + { type => 'file', val => \$source_path, + disabled => sub { $source_type eq 'live' } } ], + pre => $umount_source, + complete => sub { + if ($source_type eq 'live') { + $live->{workdir} = $live_media; + } else { + require File::Temp; + $live->{workdir} = File::Temp::tempdir(); + if (!run_('mount', '-o', 'loop', $source_path, $live->{workdir})) { + $in->ask_warn(N("Error"), N("Unable to use selected file")); + return 1; + } + } + 0; + }, + post => sub { + my $boot = $live->{workdir} . $live->{prefix}{media}{boot}; + @available_storage = sort(grep { -d "$boot/$_" && exists $storage{$_}{detect} } all($boot)); + if (@available_storage == 1) { + $storage = $available_storage[0]; + return 'device'; + } + return 'storage'; + } + }, + storage => { + name => N("Please select the medium type"), + data => [ { type => 'list', allow_empty_list => 1, + val => \$storage, list => \@available_storage } ], + next => 'device', + }, + device => { + name => N("Please select the device that will contain the new live system"), + pre => sub { + my %devices = map { $_->{device} => $_ } $storage{$storage}{detect}->(); + $_->{formatted_name} = $_->{usb_description} || $_->{info} || $_->{device} foreach values %devices; + @available_devices = (); + require fs::proc_partitions; + foreach (fs::proc_partitions::read([ values %devices ])) { + if ($_->{rootDevice} && exists $devices{$_->{rootDevice}}) { + my $description = $devices{$_->{rootDevice}}{usb_description} || $devices{$_->{rootDevice}}{info}; + $_->{formatted_name} = $description ? "$description ($_->{device})" : $_->{device}; + push @available_devices, $_; + } + } + delete $devices{$_->{rootDevice}} foreach @available_devices; + unshift @available_devices, map { $devices{$_} } sort keys %devices; + undef $device; + }, + data => [ { type => 'list', allow_empty_list => 1, + val => \$device, , list => \@available_devices, + format => sub { $_[0]{formatted_name} } }, + { text => N("Format selected device"), val => \$format, type => 'bool' } ], + complete => sub { + return 0 if defined $device; + $in->ask_warn(N("Error"), N("You must select a device!")); + 1; + }, + post => sub { + (my $_wait, $live->{update_progress}) = $in->wait_message_with_progress_bar; + do { + local $::isInstall = 1; # quick hack to embed the wait message + $live->{update_progress}->(N("Copying in progress")); + }; + eval { + my $opts = { media => { storage => $storage, device => '/dev/' . $device->{device} } }; + format_device($live, $opts) if $format; + record_onthefly($live, $opts); + }; + delete $live->{update_progress}; + if (my $error = $@) { + $in->ask_warn(N("Error"), $error); + $w->{pages}{device}{end} = 1; + } + return "end"; + }, + }, + end => { + name => N("Congratulations") . "\n\n" . N("Your live system is now copied."), + no_back => 1, + end => 1, + }, + } + }); + $w->process($in); + $umount_source->(); + $in->exit; +} + +sub check_config { + my ($live) = @_; + unless (keys(%$live)) { + warn 'no live definition'; + Pod::Usage::pod2usage(); + } + #- check for minimum requirements + ref $live->{media} && $live->{media}{storage} or die "no media storage definition"; + ref $live->{system} or die "no system definition"; +} + +sub complete_config { + my ($live) = @_; + + my $default_prefix = { + build => { + boot => '/boot', + images => '/images', + initrd => '/initrd', + loopbacks => '/loopbacks', + scripts => '/scripts', + }, + media => { + boot => $live->{system}{hide_media_dirs} ? '/.boot' : '/boot', # FIXME (boot cd) + loopbacks => $live->{system}{hide_media_dirs} ? '/.loopbacks' : '/loopbacks', + mnt => '/media', + }, + live => { + mnt => '/live', + }, + }; + + #- set unsupplied config dirs + add2hash($live->{prefix}{$_} ||= {}, $default_prefix->{$_}) foreach keys %$default_prefix; + + $live->{workdir} ||= '/tmp/draklive'; + mkdir_p(get_workdir($live)); + + $live->{mnt} ||= get_workdir($live) . "/mnt"; + + $live->{media}{title} ||= "live"; +} + +sub clean { + my ($live) = @_; + # umount filesystem in the live before cleaning + umount_external_filesystem($live); + rm_rf($_) foreach grep { -e $_ } get_workdir($live), get_system_root($live); +} + +my @actions = ( + { name => 'clean', do => \&clean }, + { name => 'install', do => \&install_system }, + { name => 'post-install', do => \&post_install_system }, + { name => 'initrd', do => \&create_initrd }, + { name => 'boot', do => \&prepare_bootloader }, + { name => 'loop', do => \&create_loopback_files }, + { name => 'master', do => \&create_master }, + { name => 'format', do => \&format_device }, + { name => 'record', do => \&record_master }, + { name => 'record-onthefly', do => \&record_onthefly }, +); +my @all = qw(install boot loop master); + +require_root_capability(); + +my (%live, %opts); +GetOptions( + "help" => sub { Pod::Usage::pod2usage('-verbose' => 1) }, + "copy-wizard" => \$live{copy_wizard}, + "boot-only" => \$opts{boot_only}, + "boot-image:s" => sub { $opts{boot} = $_[1]; $opts{boot_only} = 1 }, + "all" => sub { $_->{to_run} = 1 foreach grep { member($_->{name}, @all) } @actions }, + (map { $_->{name} => \$_->{to_run} } @actions), + "device:s" => sub { $live{media}{device} = $_[1] }, + "region:s" => sub { $live{system}{region} = $_[1] }, + "all-regions" => sub { $live{all_regions} = 1 }, + "config:s" => sub { + my $path = $_[1]; + #- don't use do(), since it can't see lexicals in the enclosing scope + my $cfg = eval(cat_($path)) or die "unable to load $path: $@"; + put_in_hash(\%live, $cfg); + print "loaded $path as config file\n"; + }, +) or Pod::Usage::pod2usage(); + +require standalone; +if ($live{copy_wizard}) { + copy_wizard(\%live); +} else { + every { !$_->{to_run} } @actions and die 'nothing to do'; + check_config(\%live); + complete_config(\%live); + foreach my $region ($live{all_regions} ? sort(keys %{$live{regions}}) : $live{system}{region}) { + $region and print qq(=== proceeding with region "$region"\n); + $live{system}{region} = $region; + foreach (grep { $_->{to_run} } @actions) { + print qq(* entering step "$_->{name}"\n); + $_->{do}->(\%live, \%opts); + print qq(* step "$_->{name}" done\n); + } + } +} + +__END__ + +=head1 NAME + +draklive - A live distribution mastering tool + +=head1 SYNOPSIS + +draklive [options] + + Options: + --help long help message + + --install install selected distribution in chroot + --boot prepare initrd and bootloader files + --loop build compressed loopback files + --master build master image + + --all run all steps, from installation to mastering + + --clean clean installation chroot and work directory + + --device <dev> use this device for live recording (not needed + if the device already has the required label) + --format format selected device + --record record live on selected media + --record-onthefly record live by creating master from loopback files + on the fly + + --initrd build initrd only + --post-install run post install only (rpms and patches installation) + + --config <file> use this configuration file as live description + + --region <region> use the matching set of langs + (from the regions configuration hash) + --all-regions proceed with all configured regions + + --copy-wizard run the copy wizard + + --boot-only copy only boot files + (affects master/record steps) + + --boot-image <method> + create a boot image for the selected method + (affects master/record steps, implies --boot-only) + +Examples: + + draklive --config config/live.cfg --clean + + draklive --config config/live.cfg --all + + draklive --config config/live.cfg --record --device /dev/sdb1 + +=head1 OPTIONS + +=over 8 + +=item B<--config> + +Makes draklive use the next argument as a configuration file. +This file should contain an hash describing the live distribution, +meaning the system (chroot and boot), media (usb, cdrom, nfs), +and mount type (simple R/W union, union with squash files). + +Here's a configuration sample: + + { + system => { + root => '/chroot/live-move', + repository => '/mnt/ken/2006.0/i586', + kernel => '2.6.12-12mdk-i586-up-1GB', + auto_install => 'config/auto_inst.cfg.pl', + patch_install => 'config/patch-2006-live.pl', + rpmsrate => 'config/rpmsrate', + rpms => [ + 'rpms/unionfs-kernel-2.6.12-12mdk-i586-up-1GB-1.1.1.1.20051124.1mdk-1mdk.i586.rpm' + ], + patches => [ + 'patches/halt.loopfs.patch', + ], + vga_mode => 788, + }, + media => { + storage => 'cdrom', + }, + extra_media => [ + { + storage => 'usb', + }, + ], + mount => $predefined{mounts}{squash_union} + }; + +=back + +=head1 DESCRIPTION + +B<draklive> builds a live distribution according to a +configuration file, creates a master image, +and optionnally installs it on a device. + +See L<http://qa.mandriva.com/twiki/bin/view/Main/DrakLive> + +=head1 AUTHOR + +Olivier Blin <oblin@mandriva.com> + +=cut diff --git a/draklive-copy-wizard.desktop b/draklive-copy-wizard.desktop new file mode 100644 index 0000000..2c73b47 --- /dev/null +++ b/draklive-copy-wizard.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Encoding=UTF-8 +Exec=/usr/sbin/draklive --copy-wizard +Icon=MandrivaOne-copy-icon +Terminal=false +Type=Application +Name=Live copy diff --git a/draklive.spec b/draklive.spec new file mode 100644 index 0000000..6a39486 --- /dev/null +++ b/draklive.spec @@ -0,0 +1,69 @@ +# MAKE THE CHANGES IN SVN: NO PATCH OR SOURCE ALLOWED +%define name draklive +%define version 0.2 +%define release %mkrel 1 +%define iconname MandrivaOne-copy-icon.png +%define copy_wizard %{name}-copy-wizard + +Summary: Live systems generation and copying tool +Name: %{name} +Version: %{version} +Release: %{release} +Source0: %{name}-%{version}.tar.bz2 +License: GPL +Group: System/Configuration/Other +Url: https://svn.mandriva.com/svn/soft/drakx/trunk/live/draklive/ +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot +BuildArch: noarch +Requires: syslinux cdrecord rsync mtools + +%description +This tool allows to generate Mandriva live systems, and to copy +running live systems to CD or USB media (draklive --copy-wizard). + +%prep +%setup -q + +%build + +%install +rm -rf $RPM_BUILD_ROOT + +for d in %_datadir/nautilus/default-desktop %_datadir/apps/kdesktop/DesktopLinks; do + install -D -m 0644 %{copy_wizard}.desktop %buildroot/$d/%{copy_wizard}.desktop +done + +install -D -m 0755 %name %buildroot/%_sbindir/%{name} + +mkdir -p %buildroot{%_miconsdir,%_iconsdir,%_liconsdir,%_menudir} +install theme/IC-installone-48.png %buildroot%_liconsdir/%iconname +install theme/IC-installone-32.png %buildroot%_iconsdir/%iconname +install theme/IC-installone-16.png %buildroot%_miconsdir/%iconname + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%defattr(-,root,root) +%_sbindir/%name +%_datadir/apps/kdesktop/DesktopLinks/%{copy_wizard}.desktop +%_datadir/nautilus/default-desktop/%{copy_wizard}.desktop +%_iconsdir/%iconname +%_liconsdir/%iconname +%_miconsdir/%iconname + +%changelog +* Sat Sep 16 2006 Olivier Blin <oblin@mandriva.com> 0.2-1mdv2007.0 +- 0.2 + +* Mon Aug 21 2006 Olivier Blin <oblin@mandriva.com> 0.1-2mdv2007.0 +- allow to add additionnal boot entries +- run shell in initrd when the "debug" option is on cmdline +- use patch batch mode (-t) not to apply already applied patches + and die if a patch can't be applied +- create modules tree root (for x86_64) +- remove modprobe.preload.d files +- require syslinux, cdrecord, rsync and mtools + +* Mon Jun 19 2006 Olivier Blin <oblin@mandriva.com> 0.1-1mdv2007.0 +- initial release @@ -0,0 +1 @@ +../draklive-install/theme
\ No newline at end of file |