source: trunk/mindi/mindi.pl @ 3656

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

Adds old mindi.pl in trunk

  • Property svn:executable set to *
File size: 114.1 KB
Line 
1#! /usr/bin/env perl
2
3#-----------------------------------------------------------------------------
4# mindi - mini-Linux distro based on the user's filesystem & distribution
5#
6# Mindi can create a multi-floppy boot/root kit. The first floppy is the boot
7# disk: it contains a kernel, a ramdisk etc. The second disk is data disk #1;
8# the third disk is data disk #2; and so it goes.
9#
10# See http://www.microwerks.net/~hugo/ for details.
11#-----------------------------------------------------------------------------
12
13use integer;
14use strict;
15use warnings;
16# Here are all the subroutines in Mindi!
17use subs qw[
18     mkdir_p                    twirling_baton              loggit
19     logit                      tar_up_files                untar_files
20     fatal_error                find_homes                  print_progress
21     find_boot_b_file           find_isolinux_path          determine_if_called_by_mondo
22     copy_stuff_into_dir        copy_files_into_dir         get_size_of_stuff
23     find_exec                  check_system                usage
24     add_to_cfg                 make_mondo_config           get_ptype_of
25     make_mountlist             divide_bigdir_into_minidirs make_data_disks
26     zip_minidirs_into_tarballs write_one_liner             create_data_disk_images_from_tarballs
27     create_tiny_filesystem     make_zfile                  get_kmods
28     get_kmod_paths             make_ramdisk                replace_line
29     edit_ramdisk               prepare_boot_disks          make_bootable_cd
30     copy_images_to_floppies    make_one_floppy             find_kernel
31     find_specific_kmod_in_path make_modules_failsafe
32];
33# use diagnostics;
34
35package Mindi;
36use Cwd             qw< getcwd chdir realpath >;
37use POSIX           qw< SEEK_SET uname >;
38use File::Basename  qw< basename dirname >;
39use File::Copy      qw< copy move >;
40use File::Find      qw< find finddepth >;
41use Fcntl           qw< O_RDONLY O_WRONLY O_CREAT O_TRUNC >;
42use Getopt::Long    qw< :config bundling >;
43use Data::Dumper    qw< Dumper >;
44use Mindi::DiskSize qw< get_size_of_disk get_partition_type >;
45use IO::Handle; # 1000s of lines just for autoflush :-/
46
47our $Version = "0.99";
48
49our (@AfterInitialPhase, @PostBoot);
50
51
52### Mindi configuration
53# You can override any of these settings by placing them in /etc/mindi.conf.
54# Or edit them here.
55
56# NOTE: these next two should be mini-shell scripts, not Perl: one thing per item
57@AfterInitialPhase    = ("echo 'Loading data disks...'");
58@PostBoot             = ("echo 'Mindi-Linux has finished booting.'");
59# end NOTE
60
61# Make a 1722K floppy?
62our $Make1722kFloppy  = 1;
63
64# Change to 1 to get a stack backtrace to the screen as well as the
65# logfile on a fatal error. It's always logged to the logfile.
66our $TraceOnFatal     = 0;
67
68# Paths to mount, umount, isolinux.bin
69# All will be located if blank -- only set these if they can't be found otherwise.
70our $MountCmd         = "";
71our $UnmountCmd       = "";
72our $IsolinuxPath     = "";
73
74# How much "breathing room" on the ramdisk
75our $ExtraSpace       = 16384;
76
77# Whether to create EXTREMELY large logfile output
78our $LogVerbose       = 0;
79
80# Maximum size of stuff that can go on a 1440k floppy
81our $MaxDiskSize      = 1325;
82
83# Temp files are placed in <TempDir>/mindi/<PID>
84our $TempDir          = `mktemp -d /tmp/mindi.XXXXXXXXXX || echo /tmp`;
85chomp $TempDir;
86
87# Where to put our PID
88our $PIDfile          = "/var/run/mindi.pid";
89
90# Prompt to write images to floppies?
91our $Floppies         = 0;
92
93# Ask before making CD?
94our $PromptCD         = 0;
95
96# Path to mindi logfile
97our $Logfile          = "/var/log/mindi.log";
98
99# Where to put the output by default
100our $ImagesDir        = "/root/images/mindi";
101
102# Size of bigfile slices
103our $ChopSize         = 240;
104
105# Size at which a file is considered "big" (see above)
106our $ChopAt           = 2 * $ChopSize;
107
108# Where's your kernel? (Mindi will try to find it if blank)
109our $Kernel           = "";
110
111# Use my kernel or yours? (0=yours, 1=mine)
112our $KernelSucks      = 0;
113
114# Use LILO if set, SYSLINUX if not.
115our $UseLILO          = 0;
116
117# Extra boot options ('append = foo=bar,baz,0x123')
118our $ExtraBootOptions = "acpi=off apm=off devfs=nomount exec-shield=0";
119
120# Message before boot: prompt
121my        $apostrophe = "'";                       # avoid confusing Perl (really, my editor)
122our $BootMediaMessage = <<END_OF_MESSAGE;
123To format and restore all files automatically, type 'nuke' <enter>.
124To restore some/all files interactively, type 'interactive' <enter>.
125To compare the archives with your filesystem, type 'compare' <enter>.
126To boot to a command-line prompt (expert mode), type 'expert' <enter>.
127
128You may add one or more of the following parameters as well:-
129
130  donteject - mondorestore will not eject the CD; this is useful if, for
131              instance, your PC${apostrophe}s case has a concealed CD-ROM drive
132  noresize  - your mountlist will not be adjusted to use your unallocated
133              hard disk space
134  textonly  - do not call any Newt library routines; this is unattractive
135              but valuable if you find your Newt library has bugs in it
136
137e.g. Type 'nuke donteject textonly' if you have an unstable Newt library and
138a PC whose CD-ROM drive tray would be damaged if it unexpectedly ejected.
139
140END_OF_MESSAGE
141
142# Floppy modules
143our @FloppyMods       = qw(floppy ide-floppy);
144
145# SCSI modules
146our @SCSIMods         = ('53c7,8xx',
147             qw(scsimod scsimon scsi_mon scsi-mon 3w-xxxx
148                3c59x gdth a100u2w advansys aha152x
149                aha1542 aha1740 aic7xxx aic7xxx_mod aic79xx
150                            aic79xx_mod BusLogic cciss dtc eata_dma eata
151                eata_pio fdomain ide-scsi ieee1394 imm initio
152                ips iscsi mptscsih mptbase raw1394 scsi-cd
153                scsi scsi_mod sd sd_mod seagate sr st osst
154                sym53c8xx ht ftape wd7000 fasttrak));
155
156# IDE modules
157our @IDEMods          = qw(ide ide-generic ide-mod ide-disk ide-cd ide-cs paride edd ata_piix libata );
158
159# CD-ROM modules
160our @CDMods           = (@FloppyMods, @IDEMods,
161             qw(af_packet cdrom isocd isofs inflate_fs nls_iso8859-1
162                nls_cp437 sg sr_mod zlib_inflate usb-storage usb-ohci usb-uhci
163                usbcore hid));
164
165# Net Modules
166our @NetMods          = (qw(sunrpc nfs nfsacl lockd loop e100 bcm5700 e1000 eepro100 tg3));
167
168# Extra modules
169our @ExtraMods        = (@CDMods,
170             qw(vfat fat loop linear raid0 raid1 raid5 lvm-mod
171                dm-mod jfs xfs xfs_support pagebuf reiserfs
172                ext2 ext3 minix nfs nfsd lockd sunrpc dm ufs));
173
174### End configuration.
175
176if (-f "/usr/local/etc/mindi.conf") {
177    require "/usr/local/etc/mindi.conf";
178}
179
180if (-f "/etc/mindi.conf") {
181    require "/etc/mindi.conf";
182}
183
184if (-f "/root/.mindi.conf") {
185    require "/root/.mindi.conf";
186}
187
188# All these variables are placeholders for --custom options.
189# Do not edit.
190our $MondoTemp             = "";
191our $TapeDevice            = "";
192our $TapeSize              = 0;
193our $FilesInFilelist       = 0;
194our $UsingLZO              = 0;
195our $CDrecovery            = 0;
196our @ImageDevs             = ();
197our $LastFilelistNum       = 0;
198our $EstNoofSlices         = 0;
199our @ExcludeDevs           = ();
200our $UsingCompression      = 1;
201our $NonBootable           = 0;
202our $DifferentialBkup      = 0;
203# End --custom options.
204
205# Globals - do not edit
206our $called_by_mondo       = -1;
207our @Mountpoints           = ();
208our %Homes                 = ( mindi => "", mondo => "" );
209our $BiggieNum             = 0;
210our $IAmExiting            = 0;
211our $ToolSize              = 0;
212our $KernelVersion         = "";
213our $FailsafeKernelVersion = "";
214our $RamdiskSize           = 24000;            # Why 24000? I don't know...
215our $ModuleSuffix          = "o";
216our $LVMVersion            = 0;
217our $LVMVersionString      = "";
218# End globals.
219
220# Master definition of all symlinks in rootfs
221our %RootfsSymlinks = (
222     'bin/[' => 'busybox',
223     'bin/ash' => 'busybox',
224     'bin/cat' => 'busybox',
225     'bin/chgrp' => 'busybox',
226     'bin/chmod' => 'busybox',
227     'bin/chown' => 'busybox',
228     'bin/chroot' => 'busybox',
229     'bin/cp' => 'busybox',
230     'bin/date' => 'busybox',
231     'bin/dd' => 'busybox',
232     'bin/df' => 'busybox',
233     'bin/dmesg' => 'busybox',
234     'bin/echo' => 'busybox',
235     'bin/false' => 'busybox',
236     'bin/fdflush' => 'busybox',
237     'bin/getopt' => 'busybox',
238     'bin/grep' => 'busybox',
239     'bin/gunzip' => 'busybox',
240     'bin/gzip' => 'busybox',
241     'bin/halt' => 'busybox',
242     'bin/hostname' => 'busybox',
243     'bin/kill' => 'busybox',
244     'bin/ln' => 'busybox',
245     'bin/loadkmap' => 'busybox',
246     'bin/ls' => 'busybox',
247     'bin/mkdir' => 'busybox',
248     'bin/mknod' => 'busybox',
249     'bin/mktemp' => 'busybox',
250     'bin/mv' => 'busybox',
251     'bin/more' => 'busybox',
252     'bin/mount' => 'busybox',
253     'bin/pidof' => 'busybox',
254     'bin/ping' => 'busybox',
255     'bin/ps' => 'busybox',
256     'bin/pwd' => 'busybox',
257     'bin/rm' => 'busybox',
258     'bin/rmdir' => 'busybox',
259     'bin/sed' => 'busybox',
260     'bin/sh' => 'busybox',
261     'bin/sh.busybox' => 'busybox',
262     'bin/sleep' => 'busybox',
263     'bin/stty' => 'busybox',
264     'bin/sync' => 'busybox',
265     'bin/sync.busybox' => 'busybox',
266     'bin/tar' => 'busybox',
267     'bin/touch' => 'busybox',
268     'bin/true' => 'busybox',
269     'bin/umount' => 'busybox',
270     'bin/uname' => 'busybox',
271     'bin/update' => 'busybox',
272     'bin/usleep' => 'busybox',
273     'bin/vi' => 'busybox',
274     'bin/zcat' => 'busybox',
275     'etc/init' => '../sbin/init',
276     'etc/mtab' => '../proc/mounts',
277     'etc/profile' => 'bashrc',
278     'etc/shrc' => 'bashrc',
279     'lib/libcom_err.so.2' => 'libcom_err.so.2.0',
280     'lib/libtermcap.so.2' => 'libtermcap.so.2.0.8',
281     'sbin/halt' => '../bin/busybox',
282     'sbin/hostname' => '../bin/busybox',
283     'sbin/ifconfig' => '../bin/busybox',
284     'sbin/insmod' => '../bin/busybox',
285     'sbin/klogd' => '../bin/busybox',
286     'sbin/loadkmap' => '../bin/busybox',
287     'sbin/losetup' => '../bin/busybox',
288     'sbin/lsmod' => '../bin/busybox',
289     'sbin/makedevs' => '../bin/busybox',
290     'sbin/mkswap' => '../bin/busybox',
291     'sbin/modprobe' => '../bin/busybox',
292     'sbin/poweroff' => '../bin/busybox',
293     'sbin/reboot' => '../bin/busybox',
294     'sbin/rmmod' => '../bin/busybox',
295     'sbin/route' => '../bin/busybox',
296     'sbin/swapoff' => '../bin/busybox',
297     'sbin/swapon' => '../bin/busybox',
298     'sbin/syslogd' => '../bin/busybox',
299     'sbin/update' => '../bin/busybox',
300     'tmp/mondo-restore.log' => '../var/log/mondo-archive.log',
301     'usr/bin/[' => '../../bin/busybox',
302     'usr/bin/basename' => '../../bin/busybox',
303     'usr/bin/chvt' => '../../bin/busybox',
304     'usr/bin/clear' => '../../bin/busybox',
305     'usr/bin/cut' => '../../bin/busybox',
306     'usr/bin/deallocvt' => '../../bin/busybox',
307     'usr/bin/dirname' => '../../bin/busybox',
308     'usr/bin/du' => '../../bin/busybox',
309     'usr/bin/env' => '../../bin/busybox',
310     'usr/bin/expr' => '../../bin/busybox',
311     'usr/bin/find' => '../../bin/busybox',
312     'usr/bin/free' => '../../bin/busybox',
313     'usr/bin/head' => '../../bin/busybox',
314     'usr/bin/hostid' => '../../bin/busybox',
315     'usr/bin/id' => '../../bin/busybox',
316     'usr/bin/killall' => '../../bin/busybox',
317     'usr/bin/length' => '../../bin/busybox',
318     'usr/bin/logger' => '../../bin/busybox',
319     'usr/bin/logname' => '../../bin/busybox',
320     'usr/bin/md5sum' => '../../bin/busybox',
321     'usr/bin/mkfifo' => '../../bin/busybox',
322     'usr/bin/nslookup' => '../../bin/busybox',
323     'usr/bin/printf' => '../../bin/busybox',
324     'usr/bin/reset' => '../../bin/busybox',
325     'usr/bin/sort' => '../../bin/busybox',
326     'usr/bin/tail' => '../../bin/busybox',
327     'usr/bin/tee' => '../../bin/busybox',
328     'usr/bin/telnet' => '../../bin/busybox',
329     'usr/bin/test' => '../../bin/busybox',
330     'usr/bin/tftp' => '../../bin/busybox',
331     'usr/bin/time' => '../../bin/busybox',
332     'usr/bin/tr' => '../../bin/busybox',
333     'usr/bin/traceroute' => '../../bin/busybox',
334     'usr/bin/tty' => '../../bin/busybox',
335     'usr/bin/uniq' => '../../bin/busybox',
336     'usr/bin/uptime' => '../../bin/busybox',
337     'usr/bin/wc' => '../../bin/busybox',
338     'usr/bin/which' => '../../bin/busybox',
339     'usr/bin/whoami' => '../../bin/busybox',
340     'usr/bin/xargs' => '../../bin/busybox',
341     'usr/bin/yes' => '../../bin/busybox',
342     'usr/i386-linux-uclibc/bin/i386-uclibc-addr2line' => '/usr/bin/addr2line',
343     'usr/i386-linux-uclibc/bin/i386-uclibc-ar' => '/usr/bin/ar',
344     'usr/i386-linux-uclibc/bin/i386-uclibc-as' => '/usr/bin/as',
345     'usr/i386-linux-uclibc/bin/i386-uclibc-cpp' => '/usr/bin/cpp',
346     'usr/i386-linux-uclibc/bin/i386-uclibc-nm' => '/usr/bin/nm',
347     'usr/i386-linux-uclibc/bin/i386-uclibc-objcopy' => '/usr/bin/objcopy',
348     'usr/i386-linux-uclibc/bin/i386-uclibc-objdump' => '/usr/bin/objdump',
349     'usr/i386-linux-uclibc/bin/i386-uclibc-ranlib' => '/usr/bin/ranlib',
350     'usr/i386-linux-uclibc/bin/i386-uclibc-size' => '/usr/bin/size',
351     'usr/i386-linux-uclibc/bin/i386-uclibc-strings' => '/usr/bin/strings',
352     'usr/i386-linux-uclibc/bin/i386-uclibc-strip' => '/usr/bin/strip',
353     'usr/i386-linux-uclibc/lib/ld-uClibc.so.0' => 'ld-uClibc-0.9.19.so',
354     'usr/i386-linux-uclibc/lib/libc.so' => 'libuClibc-0.9.19.so',
355     'usr/i386-linux-uclibc/lib/libc.so.0' => 'libuClibc-0.9.19.so',
356     'usr/i386-linux-uclibc/lib/libcrypt.so' => 'libcrypt-0.9.19.so',
357     'usr/i386-linux-uclibc/lib/libcrypt.so.0' => 'libcrypt-0.9.19.so',
358     'usr/i386-linux-uclibc/lib/libdl.so' => 'libdl-0.9.19.so',
359     'usr/i386-linux-uclibc/lib/libdl.so.0' => 'libdl-0.9.19.so',
360     'usr/i386-linux-uclibc/lib/libm.so' => 'libm-0.9.19.so',
361     'usr/i386-linux-uclibc/lib/libm.so.0' => 'libm-0.9.19.so',
362     'usr/i386-linux-uclibc/lib/libnsl.so' => 'libnsl-0.9.19.so',
363     'usr/i386-linux-uclibc/lib/libnsl.so.0' => 'libnsl-0.9.19.so',
364     'usr/i386-linux-uclibc/lib/libpthread.so' => 'libpthread-0.9.19.so',
365     'usr/i386-linux-uclibc/lib/libpthread.so.0' => 'libpthread-0.9.19.so',
366     'usr/i386-linux-uclibc/lib/libresolv.so' => 'libresolv-0.9.19.so',
367     'usr/i386-linux-uclibc/lib/libresolv.so.0' => 'libresolv-0.9.19.so',
368     'usr/i386-linux-uclibc/lib/libutil.so' => 'libutil-0.9.19.so',
369     'usr/i386-linux-uclibc/lib/libutil.so.0' => 'libutil-0.9.19.so',
370     'usr/i386-linux-uclibc/usr/bin/addr2line' => '/usr/bin/addr2line',
371     'usr/i386-linux-uclibc/usr/bin/ar' => '/usr/bin/ar',
372     'usr/i386-linux-uclibc/usr/bin/as' => '/usr/bin/as',
373     'usr/i386-linux-uclibc/usr/bin/c++' => '/usr/i386-linux-uclibc/bin/i386-uclibc-gcc',
374     'usr/i386-linux-uclibc/usr/bin/cc' => '/usr/i386-linux-uclibc/bin/i386-uclibc-gcc',
375     'usr/i386-linux-uclibc/usr/bin/cpp' => '/usr/bin/cpp',
376     'usr/i386-linux-uclibc/usr/bin/g++' => '/usr/i386-linux-uclibc/bin/i386-uclibc-gcc',
377     'usr/i386-linux-uclibc/usr/bin/gcc' => '/usr/i386-linux-uclibc/bin/i386-uclibc-gcc',
378     'usr/i386-linux-uclibc/usr/bin/ld' => '/usr/i386-linux-uclibc/bin/i386-uclibc-ld',
379     'usr/i386-linux-uclibc/usr/bin/ldd' => '/usr/i386-linux-uclibc/bin/i386-uclibc-ldd',
380     'usr/i386-linux-uclibc/usr/bin/nm' => '/usr/bin/nm',
381     'usr/i386-linux-uclibc/usr/bin/objcopy' => '/usr/bin/objcopy',
382     'usr/i386-linux-uclibc/usr/bin/objdump' => '/usr/bin/objdump',
383     'usr/i386-linux-uclibc/usr/bin/ranlib' => '/usr/bin/ranlib',
384     'usr/i386-linux-uclibc/usr/bin/size' => '/usr/bin/size',
385     'usr/i386-linux-uclibc/usr/bin/strings' => '/usr/bin/strings',
386     'usr/i386-linux-uclibc/usr/bin/strip' => '/usr/bin/strip',
387     'usr/lib/libncurses.so.4' => '/usr/lib/libncurses.so.5',
388     'usr/sbin/chroot' => '../../bin/busybox',
389     'usr/sbin/dutmp' => '../../bin/busybox',
390     'usr/sbin/fbset' => '../../bin/busybox',
391);
392
393# Fix up $ENV{'PATH'}
394$ENV{'PATH'} .= ":/sbin:/usr/sbin:/usr/local/sbin";
395
396# Make sure we're running on Linux.
397if ( $^O !~ /linux/i ) {
398    die "Only runnable under Linux, not your $^O system.\n";
399}
400
401############################################################
402# mkdir_p(@) - make some directories + all their parents   #
403#              @_ - list of directories                    #
404############################################################
405sub mkdir_p {
406    my @args = @_; # change pass-by-ref to pass-by-val
407    my @dirs;
408    foreach my $dir (@args) {
409    while ($dir ne "." and $dir ne "/") {
410        unshift @dirs, $dir;
411        $dir = dirname $dir;
412    }
413    }
414    foreach my $dir2make (@dirs) {
415    mkdir $dir2make;
416    }
417}
418
419############################################################
420# twirling_baton($) - twirl the baton! \ | / - \ | / - ... #
421#                     $_[0] - number of times to twirl     #
422############################################################
423sub twirling_baton {
424    my $ntwirls = shift || fatal_error ("Too few arguments to twirling_baton");
425    my $modulo = $ntwirls % 4;
426    $modulo == 0 ? '|' :
427    $modulo == 1 ? '/' :
428    $modulo == 2 ? '-' :
429    $modulo == 3 ? '\\' : '|';
430}
431
432############################################################
433# loggit(@) - log message to logfile                       #
434#             @_  = the message                            #
435############################################################
436sub loggit {
437    unless (defined open LOG, ">> $Mindi::Logfile") {
438    unless ($IAmExiting) {
439        fatal_error ("Could not open $Mindi::Logfile: $!");
440    }
441    }
442    print LOG @_, "\n";
443    close LOG;
444}
445
446############################################################
447# logit(@) - log message to logfile + stderr               #
448#            @_ - the message                              #
449############################################################
450sub logit {
451    loggit @_;
452    print STDERR @_, "\n";
453}
454
455############################################################
456# tar_up_files($@) - Make tarball out of files             #
457#                    $_[0]      = the tarball              #
458#                    rest of @_ = the files to tar up      #
459#                           RET = true for success         #
460############################################################
461sub tar_up_files {
462    my $tarball = shift || fatal_error ("Too few arguments to tar_up_files");
463    my $compression_flag = "";
464    my $pid;
465    my @tarfiles;
466    foreach my $tarfile (@_) {
467    push @tarfiles, $tarfile if -e $tarfile;
468    }
469
470    for ($tarball) {
471    /.*\.t(ar\.)?gz/    and do { $compression_flag = " | gzip -9 > "; last; };
472    /.*\.t(ar\.)?bz2?/  and do { $compression_flag = " | bzip2 -9 >"; last; };
473    /.*\.t(ar\.l)?zop?/ and do { $compression_flag = " | lzop >";     last; };
474    /.*\.ta(r\.)?[zZ]/  and do { $compression_flag = " | compress >"; last; };
475    /.*\.tar/           and do { $compression_flag = " > ";           last; };
476    fatal_error ("Don't recognize $tarball as a valid tarball");
477    }
478    loggit "tar -cf- @tarfiles $compression_flag $tarball 2>> $Logfile";
479    not system "tar -cf- @tarfiles $compression_flag $tarball 2>> $Logfile";
480}
481
482############################################################
483# untar_files($;$$) - Make files out of tarball            #
484#                    $_[0] - tarball                       #
485#         [OPTIONAL] $_[1] - directory to untar in         #
486#         [OPTIONAL] $_[2] - progress msg to enable them   #
487############################################################
488sub untar_files {
489    my $tarball            = shift || fatal_error ("Too few arguments to untar_files");
490    my $directory          = shift || getcwd;
491    my $print_progress     = shift || "";
492    my $print_progress_get; # = shift || not called_by_mondo();
493    my $decompression_flag = "";
494    my $ret                = 1;
495    my $olddir             = getcwd;
496    my ($progress, $noof_lines);
497
498    if (scalar @_) {
499    $print_progress_get = shift;
500    } else {
501    $print_progress_get = not called_by_mondo();
502    }
503
504    mkdir_p $directory;
505
506    for ($tarball) {
507    /.*\.t(ar\.)?gz/   and do { $decompression_flag = "z"; next; };
508    /.*\.t(ar\.)?bz2?/ and do { $decompression_flag = "j"; next; };
509    /.*\.ta(r\.)?[zZ]/ and do { $decompression_flag = "Z"; next; };
510    /.*\.tar/          and do {                            next; };
511    fatal_error ("Don't recognize $tarball as a valid tarball");
512    }
513    chdir $directory;
514    if ($print_progress) {
515    if ($print_progress_get) {
516        print "\r$print_progress", "[", ' ' x 30, "] |"
517          unless called_by_mondo();
518
519        open TAR_T, "/usr/bin/env tar t${decompression_flag}f $tarball |"
520          or fatal_error ("Can't open pipe from tar: $!");
521        while (<TAR_T>) {
522        print_progress (++$noof_lines, -1, $print_progress);
523        }
524        close TAR_T or fatal_error ("Error closing tar pipe: $!");
525
526        if (called_by_mondo()) {
527        $print_progress =~ s/:\t*$/.../;
528        print "$print_progress\n";
529        } else {
530        print "\r$print_progress", "[", ' ' x 30, "] 0", '% |';
531        }
532
533        open TAR_X, "/usr/bin/env tar xv${decompression_flag}f $tarball |"
534          or fatal_error ("Can't open pipe from tar: $!");
535        while (<TAR_X>) {
536        print_progress (++$progress, $noof_lines, $print_progress);
537        }
538        close TAR_X or fatal_error ("Error closing tar pipe: $!");
539    } else {
540        if (called_by_mondo()) {
541        $print_progress =~ s/:\t+/.../;
542        print "$print_progress\n";
543        } else {
544        print "\r$print_progress", "[", ' ' x 30, "] |";
545        }
546
547        open TAR, "/usr/bin/env tar xv${decompression_flag}f $tarball |"
548          or fatal_error ("Can't open pipe from tar: $!");
549        while (<TAR>) {
550        print_progress (++$noof_lines, -1, $print_progress);
551        }
552        close TAR or fatal_error ("Error closing tar pipe: $!");
553        print "\r$print_progress", "[", '=' x 29, ">] Done.\n"
554          unless called_by_mondo();
555    }
556    }
557    else {
558    $ret = not system 'tar', "x${decompression_flag}f", $tarball;
559    }
560    chdir $olddir;
561    $ret;
562}
563
564############################################################
565# fatal_error(@) - Abort with a fatal error                #
566#                  @_  - the error msg                     #
567#                 ~RET - does not return                   #
568############################################################
569sub fatal_error {
570    my $mess = join '', @_;
571    my $stackmess = "";
572    my $stacklevel = 0;
573    my $mountpoint;
574    my ($pkg, $file, $line, $subr, $hasargs, $eval, $require);
575    my (@files, @a);
576
577    $IAmExiting = 1;
578
579    logit "\nMindi version ${Mindi::Version}:";
580    logit "---FATAL ERROR--- $mess";
581    if ($TraceOnFatal) {
582    logit "** Stack backtrace follows. **";
583    } else {
584    loggit "** Stack backtrace follows. **";
585    }
586    # Now for the magical code! (modified from Carp/Heavy.pm)
587    # When we're in `package DB' caller() returns more info
588    while (do {{ package DB; @a = caller $stacklevel }}) {
589    # retrieve return values from caller()
590    ($pkg, $file, $line, $subr, $hasargs, $eval, $require) = @a;
591    if (defined $eval) {                   # it's an eval '...' or a require
592        if ($require) {                # --> it's a require '...'
593        $subr = "require $eval";
594        } elsif ($eval eq "0") {               # --> probably an anonymous sub (or a signal handler)
595        $subr = "[SIGNAL]";
596        } else {                       # --> it's an eval '...'
597        $subr = "eval '$eval'";
598        }
599    } elsif ($subr eq "(eval)") {              # it's an eval { ... }
600        $subr = "eval { ... }";
601    }
602    # otherwise, it's a real subroutine, so we don't have to do anything name-wise
603   
604    # if we were called from a signal handler, everything from the handler up is an
605    # "anonymous" subroutine ... but not us!
606    if ($stacklevel == 0) {
607        $subr = "fatal_error";
608    }
609   
610    # set arguments
611    if ($hasargs) {
612        @a = @DB::args;                # get args from the call
613        if ($subr eq "[SIGNAL]") {
614        if ($#a == 1 and $a[0] =~ /[A-Z]{3,5}/) {
615            $subr = "[handler for SIG$a[0]]";
616        } else {
617            $subr = "[unknown]";
618            goto ARG;
619        }
620        } else {
621          ARG: foreach (@a) {
622            $_ = "undef", next ARG if not defined $_;
623            if (ref $_) {
624            $_ .= '';
625            }
626            s/'/\\'/g;
627            # is it a number or string?
628            if (not /^-?[\d.]+$/) {
629            $_ = "'$_'";               # it's a string
630            }
631            # print extended-ASCII as '\nnn'
632            s/([\200-\377])/sprintf '\%o', ord $1/eg;
633            # print control-chars as ^x
634            s/([\0-\37\177])/sprintf '^%c', ord($1) ^ 64/eg;
635        }
636        $subr .= " (@{[ join ', ' => @a ]})";
637        }
638    } else {
639        $subr .= " ()";
640    }
641    if ($TraceOnFatal) {
642        logit "#$stacklevel in $subr called at $file line $line";
643    } else {
644        loggit "#$stacklevel in $subr called at $file line $line";
645    }
646    ++$stacklevel;
647    }
648    # End stack magic.
649    if ($TraceOnFatal) {
650    logit "** End stack backtrace. **";
651    } else {
652    loggit "** End stack backtrace. **";
653    }
654
655    chdir "/";
656    foreach $mountpoint (@Mountpoints) {
657    loggit "-> Unmounting $mountpoint.";
658    system "$UnmountCmd $mountpoint" and logit "-> ** Could not unmount $mountpoint!";
659    }
660
661    system "rm -rf $TempDir/minidir $TempDir/bigdir $TempDir/tardir";
662    system "rm -rf /tmp/mindi.err.*";
663    mkdir_p "/tmp/mindi.err.$$";
664    chdir "/tmp/mindi.err.$$" or logit "Mindi is exiting -- can't cd to errors dir ($!)", exit 2;
665    $TempDir .= "/mindi/$$" if $TempDir !~ /$$/;
666    for my $file ("/etc/fstab", $Mindi::Logfile, "/var/log/mondo-archive.log") {
667    system 'cp', '-R', "$file", '.' if -e $file;
668    }
669    tar_up_files ("/tmp/mindi.err.$$.tar.gz", "fstab", basename ($Mindi::Logfile, ""), "mondo-archive.log");
670    if ($TempDir =~ /$$/) {
671    system "rm -rf $TempDir" and warn "Could not remove $TempDir!\n";
672    } else {
673    logit "TempDir is $TempDir. This does not contain my PID, so I shan't remove it.";
674    }
675    print "Please e-mail a copy of /tmp/mindi.err.$$.tar.gz to the mailing list.\n",
676      "See http://www.mondorescue.org for more information.\n",
677    "We CANNOT HELP unless you enclose that file!\n";
678    unlink $PIDfile unless $mess =~ /instance/;
679    logit "Mindi is exiting.", exit 1;
680}
681
682############################################################
683# determine_if_called_by_mondo() - Mondo called me?        #
684############################################################
685sub determine_if_called_by_mondo {
686    my $pts = 0;
687    my $fh;
688
689    if (-e "/var/run/monitas-mondo.pid") { ++$pts };
690
691    open $fh, "ps ax |" or fatal_error "Could not open output of ps ax: $!";
692    while (<$fh>) {
693    chomp;
694    if (/mondoarchive/) {
695        ++$pts;
696        last;
697    }
698    }
699    $called_by_mondo = $pts;
700}
701############################################################
702# called_by_mondo() - return $called_by_mondo (must be set)#
703#                     RET - true for yes, false for no     #
704############################################################
705sub called_by_mondo() { $called_by_mondo; }
706
707############################################################
708# find_homes() - find Mindi and Mondo's homedirs           #
709############################################################
710sub find_homes {
711    my $dir;
712    foreach $dir ("/usr/share", "/usr/local", "/usr/local/share", "/usr", "/opt/share") {
713    if (-d "$dir/mindi") {
714        $Homes{'mindi'} = "$dir/mindi";
715    }
716    if (-d "$dir/mondo") {
717        $Homes{'mondo'} = "$dir/mondo";
718    }
719    }
720    if (not $Homes{'mindi'}) {
721    fatal_error "Can't find Mindi's home directory!";
722    }
723    if (not $Homes{'mondo'}) {
724    fatal_error "Can't find Mondo's home directory!"
725      if called_by_mondo;
726    logit "Warning: Could not find Mondo's home directory.";
727    logit "Some features may not work without Mondo installed.";
728    }
729    1;
730}
731
732$Mindi::lastpct = 0;
733
734############################################################
735# print_progress($$@) - print a progress message           #
736#                       $_[0] - progress so far ...        #
737#                       $_[1] - ... out of how much to do  #
738#                  rest of @_ - progress message: inc \t   #
739############################################################
740sub print_progress {
741    my  $progress   = shift || fatal_error "Too few arguments to print_progress (want >2, got 0)";
742    my  $noof_lines = shift || fatal_error "Too few arguments to print_progress (want >2, got 1)";
743    my  $msg        = join "", @_;
744    my ($percent, $nstars, $ndots);
745    our $lastpct;
746
747    if ($noof_lines != -1) {
748    $percent = int ($progress * 100 / $noof_lines);
749    if (not called_by_mondo) {
750        my ($nstars, $ndots);
751        $nstars = int ($percent * 3/10);
752        $ndots = 30 - $nstars;
753        $ndots-- if $nstars == 0;
754        if ($percent == 100) {
755        print "\r$msg", "[", '=' x 29, '>', "] Done. \n";
756        }
757        else {
758        print "\r$msg", "[", '=' x ($nstars - 1), '>',
759          ' ' x $ndots, "] $percent", '%', " @{[ twirling_baton $progress ]}";
760        }
761    } else {
762        if (not $percent % 25) { # it's a multiple of 25
763        if ($percent != $lastpct) {
764            if ($percent == 100) {
765            print "--> Done.\n";
766            }
767            else {
768            print "--> $percent", '%', " done...\n";
769            }
770            $lastpct = $percent;
771        }
772        }
773    }
774    }
775    elsif (not called_by_mondo) {
776    my $pos = 0;
777    my $iter = 0;
778    my $safter = 0;
779    $pos = $progress;
780    $iter = int ($pos / 24) + 1;
781    if (($iter % 2) == 0) {
782        $pos = 24 - ($pos % 24) while $pos > 24;
783    }
784    else {
785        $pos = $pos % 24 while $pos > 24;
786    }
787    $safter = 25 - $pos;
788    print "\r$msg", "[", ' ' x $pos, '<===>', ' ' x $safter, "] @{[ twirling_baton $progress ]}";
789    }
790}
791
792############################################################
793# generate_depends_list() - make giant dependency list     #
794############################################################
795sub generate_depends_list() {
796    my $progress = 0;
797    my $noof_lines = 0;
798    my $mr_found = 0;
799    my $modutils_yet = 0;
800    my @tmpdeps = ();
801    my @deps = ();
802
803    if (called_by_mondo) {
804    print "Analyzing dependency requirements...\n";
805    } else {
806    print "Analyzing dependency requirements:\t[", ' ' x 30, "] 0", '% |';
807    }
808    loggit "--> Analyzing dependency requirements...";
809
810    open DEPLIST, "/etc/mindi/deplist.txt"        or
811      open DEPLIST, "$Homes{'mindi'}/deplist.txt" or fatal_error "Could not open $Homes{'mindi'}/deplist.txt: $!";
812    open TMPLIST, "> $TempDir/deplist.txt"        or fatal_error "Could not open > $TempDir/deplist.txt: $!";
813
814    $progress = $noof_lines = 0;
815
816    ++$noof_lines while <DEPLIST>;
817    seek DEPLIST, 0, SEEK_SET;
818
819    while (<DEPLIST>) {
820    my (@line, $file, $dir, $lib, $entity);
821    next if /^\#/;
822    s/\s+\#.*//;
823    chomp;
824    @line = split;
825    $mr_found = 1 if /mondo.?restore/;
826
827    if (!$modutils_yet and $KernelVersion =~ /2.6/) {
828        @line = ('insmod', 'rmmod', 'modprobe', 'depmod', 'lsmod', 'kallsyms', @line);
829        $modutils_yet = 1;
830    }
831
832    foreach $file (@line) {
833        if (/\.${ModuleSuffix}$/) {
834        if ($KernelSucks) {
835            if (-e "$TempDir/lib/modules/$FailsafeKernelVersion/$file") {
836            print TMPLIST "$TempDir/lib/modules/$FailsafeKernelVersion/$file\n";
837            loggit "GDL: Adding kernel object $file to deplist" if $LogVerbose;
838            } else {
839            loggit "GDL: Ignoring kernel object $file -- does not exist";
840            }
841        } else {
842            if (-e "/lib/modules/".`uname -r | tr -d '\n'`."/$file") {
843            print TMPLIST "/lib/modules/".`uname -r | tr -d '\n'`."/$file\n";
844            loggit "GDL: Adding kernel object $file to deplist" if $LogVerbose;
845            } else {
846            loggit "GDL: Ignoring kernel object $file -- does not exist"
847            }
848        }
849        } else {
850        my($found) = 0;
851        if ($file =~ /^(([lp]v)|vg)/) { # it's an LVM file
852            if (-x "/sbin/lvm-$LVMVersionString/$file") {
853            $found = 1;
854            print TMPLIST "/sbin/lvm-$LVMVersionString/$file\n";
855            } elsif (-x "/lib/lvm-$LVMVersionString/$file") {
856            $found = 1;
857            print TMPLIST "/lib/lvm-$LVMVersionString/$file\n";
858            }
859            if ($found and $LogVerbose) {
860            loggit "GDL: Found LVM-Tool $file in version-specific location";
861            }
862        }
863        if (!$found) {
864          DIR:
865        foreach my $thedir ('/etc', '/lib', '/bin', '/sbin', '/usr', '/usr/bin', '/usr/sbin', '/usr/lib',
866                    '/usr/local/bin', '/usr/local/sbin', '/usr/local/lib',
867                    '/usr/local', '/usr/X11R6', '/usr/X11R6/bin', '/usr/X11R6/lib',
868                        '/opt/bin', '/opt/sbin', '/opt/lib',
869                    '/opt/mondo/bin', '/opt/mondo/sbin', '/opt/mondo/lib') {
870            my $dir = $thedir;
871            if ($dir =~ /lib$/ and $file =~ /^lib/) {
872            foreach $lib (glob "$dir/$file*so") {
873                while (-l $lib) {
874                print TMPLIST "$lib\n";
875                $lib = $dir . '/' . readlink "$lib";
876                }
877                print TMPLIST "$lib\n";
878                loggit "GDL: Adding library $lib to deplist (similar to $file)" if $LogVerbose;
879            }
880            }
881            elsif (-e "$dir/$file") {
882            while (-l "$dir/$file") {
883                print TMPLIST "$dir/$file\n";
884                $file = readlink "$dir/$file";
885                $dir = "" if $file =~ m[^/];
886            }
887            $entity = "$dir/$file";
888            print TMPLIST "$entity\n";
889            loggit "GDL: Adding $entity to deplist" if $LogVerbose;
890            last DIR;
891            }
892        }
893        }}
894        }
895    } continue {
896    print_progress ++$progress, $noof_lines, "Analyzing dependency requirements:\t";
897    }
898    if (not $mr_found) {
899    foreach my $dir ("/usr/bin", "/usr/sbin", "/usr/local/bin", "/usr/local/sbin") {
900        print TMPLIST "$dir/mondorestore",  $mr_found = 1, last if -e "$dir/mondorestore";
901        print TMPLIST "$dir/mondo-restore", $mr_found = 1, last if -e "$dir/mondo-restore";
902    }
903    if ((not $mr_found) and called_by_mondo) {
904        fatal_error "I tried hard but I couldn't find mondorestore!";
905    }
906    }
907    loggit "--> Analyzing dependency requirements...done.";
908    close DEPLIST;
909    close TMPLIST;
910
911    $progress = $noof_lines = 0;
912    if (called_by_mondo) {
913    print "Making complete dependency list...\n";
914    }
915    else {
916    print "Making complete dependency list:\t[", ' ' x 30, "] 0", '% |';
917    }
918    loggit "--> Making complete dependency list...";
919
920    open TMPLIST, "< $TempDir/deplist.txt" or fatal_error "Can't open $TempDir/deplist.txt for reading: $!";
921    ++$noof_lines while <TMPLIST>;
922    seek TMPLIST, 0, 0;
923
924    while (<TMPLIST>) {
925    chomp;
926    next if /^\s*$/;
927    push @tmpdeps, $_;
928    loggit "GDL: Adding $_ to <DEPLIST>" if $LogVerbose;
929    open LDD, "/usr/bin/ldd $_ 2>&1 |";
930    while (<LDD>) {
931        chomp;
932        next unless /^\t.*=>.*\(.*\)/;         # get rid of errors
933        s/^\t.*=>\s*//;
934        s/\s*\(.*\)\s*//;
935        while (-l $_) {
936        push @tmpdeps, $_;
937        my($dir, $link);
938        $dir  = dirname $_;
939        $link = readlink $_;
940        $_    = $dir . '/' . $link;
941        }
942        push @tmpdeps, $_;
943        loggit "--> Adding dependency $_ to <DEPLIST>" if $LogVerbose;
944    }
945    close LDD;
946    } continue {
947    print_progress ++$progress, $noof_lines, "Making complete dependency list\t\t";
948    }
949    @tmpdeps = sort @tmpdeps;
950    @deps = ();
951    for (my $i = 1; $i < scalar @tmpdeps; ++$i) {
952    if ($tmpdeps[$i] ne $tmpdeps[$i-1]) {
953        push @deps, $tmpdeps[$i-1];
954    }
955    }
956    push @deps, $tmpdeps[$#tmpdeps];
957    close TMPLIST;
958
959    open DEPLIST, "> $TempDir/deplist.txt" or fatal_error "Can't open $TempDir/deplist.txt: $!";
960    print DEPLIST join ("\n", @deps), "\n";
961    close DEPLIST;
962    loggit "--> Making complete dependency list...done.";
963}
964
965my($CSID_number_dots) = 0;
966############################################################
967# copy_stuff_into_dir() - copy file/directory into dir     #
968#                         $_[0] - the file/directory       #
969#                         $_[1] - the directory to copy to #
970############################################################
971sub copy_stuff_into_dir {
972    my $thing   = shift || fatal_error "Too few arguments to copy_stuff_into_dir() - want 2 or 3, got 0";
973    my $dir     = shift || fatal_error "Too few arguments to copy_stuff_into_dir() - want 2 or 3, got 1";
974    my $newname = shift || $thing;
975    my ($file, $dirname, $block, $mode);
976    my $sliceno = 0;
977
978    (undef, undef, $mode) = lstat $thing;
979    if (-d _) {
980    loggit "--> CSID: $thing is a directory; recursing..." if $LogVerbose;
981    if ($thing eq ".") {
982        ++$CSID_number_dots;
983        if ($CSID_number_dots > 5) {
984        fatal_error "CSID: Deadlock avoided";
985        $CSID_number_dots = 0;
986        return;
987        }
988    }
989    else {
990        $CSID_number_dots = 0;
991    }
992    opendir my $dirhandle, $thing or fatal_error "Can't opendir $thing: $!";
993    while (defined ($file = readdir $dirhandle)) {
994        next if $file =~ /^\./;
995        copy_stuff_into_dir ($file, $dir);
996    }
997    closedir $dirhandle;
998    }
999    elsif (-e _) {
1000    my($stripped) = 0;
1001
1002    if ($thing =~ /o\.gz$/) {
1003        if ($KernelSucks) {
1004        ($newname = $thing) =~ s/$TempDir//;
1005        }
1006
1007        system "gunzip", $thing;
1008        $thing   =~ s/\.gz//;
1009        $newname =~ s/\.gz//;
1010
1011        (undef, undef, $mode) = lstat $thing;       # it's a new thing, with a new size
1012    } elsif (!-l _ and -f _ and -x _ and $thing !~ m[^/lib/ld.+so]) {
1013        if (system("file '$thing' | grep -q not.stripped") == 0) {
1014        copy $thing, "$TempDir/stripped.file";
1015        if (system("strip --strip-unneeded $TempDir/stripped.file") == 0) {
1016            $stripped = 1;
1017            loggit "CSID: Stripped -$thing-";
1018            lstat "$TempDir/stripped.file";
1019        } else {
1020            unlink "$TempDir/stripped.file";
1021        }
1022        }
1023    }
1024   
1025    $dirname = dirname $newname;
1026    mkdir_p "$dir/$dirname";
1027
1028    if (!-l _ and (-s _) > ($ChopAt * 1024)) {
1029        loggit "--> CSID: Copying $thing into $dir as a biggiefile";
1030        open SLICENAME, "> $dir/slice-$BiggieNum.name" or fatal_error "Can't open $dir/slice-$BiggieNum.name: $!";
1031        print SLICENAME $newname, "\n";
1032        close SLICENAME;
1033
1034        open SLICESIZE, "> $dir/slice-$BiggieNum.size" or fatal_error "Can't open $dir/slice-$BiggieNum.name: $!";
1035        print SLICESIZE int ((-s _) / 1024), "\n";
1036        close SLICESIZE;
1037
1038        sysopen BIGGIE, $stripped ? "$TempDir/stripped.file" : $thing, O_RDONLY or fatal_error "Can't sysopen $thing O_RDONLY: $!";
1039        while (sysread BIGGIE, $block, $ChopSize * 1024) {
1040        sysopen SLICE, "$dir/slice-$BiggieNum.@{[ sprintf '%03d', $sliceno ]}", O_WRONLY | O_TRUNC | O_CREAT, 0644
1041          or fatal_error "Can't sysopen $dir/slice-$BiggieNum.@{[ sprintf '%03d', $sliceno ]} O_WRONLY: $!";
1042        syswrite SLICE, $block or fatal_error "Couldn't write block to SLICE: $!";
1043        close SLICE;
1044        loggit "    --> Slice $sliceno written successfully.";
1045        ++$sliceno;
1046        }
1047        close BIGGIE;
1048        ++$BiggieNum;
1049        loggit "    --> Copied successfully, $sliceno slices.";
1050        $sliceno = 0;
1051    } else {
1052        if (-f _) {
1053        copy $stripped ? "$TempDir/stripped.file" : $thing, "$dir/$newname" or fatal_error "Can't copy $thing to $dir/$newname: $!";
1054        } else {
1055        system "cp", "-a", $thing, "$dir/$newname";
1056        }
1057        chmod $mode & 07777, "$dir/$newname";
1058    }
1059
1060    unlink "$TempDir/stripped.file" if $stripped;
1061    }
1062    else {
1063    loggit "$thing in deplist does not exist";
1064    }
1065}
1066   
1067
1068############################################################
1069# copy_files_into_dir() - chop up and copy files into dir  #
1070#                         $_[0] - the filelist of files    #
1071#                         $_[1] - the directory            #
1072############################################################
1073sub copy_files_into_dir {
1074    my $filelist = shift || fatal_error "Too few arguments to copy_files_into_dir() - want 2, got 0";
1075    my $dir      = shift || fatal_error "Too few arguments to copy_files_into_dir() - want 2, got 1";
1076    my $file;
1077    my ($progress, $noof_lines) = (0, 0);
1078
1079    open FILELIST, $filelist or fatal_error "Cannot open $filelist: $!";
1080    ++$noof_lines while <FILELIST>;
1081    seek FILELIST, 0, 0;
1082    if (called_by_mondo) {
1083    print "Assembling dependency files...\n";
1084    }
1085    else {
1086    print "Assembling dependency files:\t\t[", ' ' x 30, "] 0", '% |';
1087    }
1088    while (<FILELIST>) {
1089    chomp;
1090    next if /^$/;
1091    next if /^#/;
1092    loggit "CFID: Copying $_ into $dir" if $LogVerbose;
1093    copy_stuff_into_dir $_, $dir;
1094    print_progress ++$progress, $noof_lines, "Assembling dependency files:\t\t";
1095    }
1096    close FILELIST or warn "Can't close FILELIST: $!";
1097    print_progress $noof_lines, $noof_lines, "Assembling dependency files:\t\t";
1098}
1099
1100our $count_of_things = 0;
1101
1102############################################################
1103# get_size_of_stuff(\%@) - get size of some files/dirs     #
1104#                          $_[0] - the hash to put sz into #
1105#                          $_[1] - the stuff for sizing    #
1106############################################################
1107sub get_size_of_stuff {
1108    my $sizes   = shift || fatal_error "Too few arguments to get_size_of_stuff() - want 2+, got 0";
1109    my @stuff   = @_;
1110    my $sizeref;
1111    ref $sizes          or fatal_error "You didn't give me a reference!";
1112    $sizeref = ref $sizes;
1113    $sizeref =~ /hash/i or fatal_error "You gave me a @{[ ref $sizes ]} reference, not a HASH reference.";
1114
1115    foreach my $thing (@stuff) {
1116    lstat $thing;
1117    if (-f _) {
1118        my $thingsize = ((-s $thing) / 1024) + 1;
1119        system "cat $thing | gzip > $thing.gz";
1120        my $thingcsize = ((-s "$thing.gz") / 1024) + 1;
1121        $sizes->{$thing} = { NORMAL => $thingsize, COMPRESSED => $thingcsize };
1122        unlink "$thing.gz";
1123        loggit "GSOS: $thing is ${thingsize}K uncompressed, ${thingcsize}K compressed" if $LogVerbose;
1124        ++$count_of_things;
1125        print_progress $count_of_things / 2 + 1, -1, "Dividing data into several groups:\t";
1126    }
1127    elsif (-d _) {
1128        my @files;
1129        $sizes->{$thing}{COMPRESSED}   = 0;
1130        $sizes->{$thing}{UNCOMPRESSED} = 0;
1131        loggit "GSOS: $thing is a directory; recursing..." if $LogVerbose;
1132        opendir THINGDIR, $thing or fatal_error "Can't opendir $thing: $!";
1133        @files = readdir THINGDIR;
1134        closedir THINGDIR;
1135        for (@files) {
1136        next if /^\.+$/;
1137        get_size_of_stuff ($sizes, "$thing/$_");
1138        }
1139    }
1140    else {
1141        # it's a socket, FIFO, symlink, or special file - 0 size
1142        loggit "GSOS: $thing is not a normal file; setting size to 0";
1143        $sizes->{$thing}{COMPRESSED}   = 0;
1144        $sizes->{$thing}{UNCOMPRESSED} = 0;
1145    }
1146    }
1147}
1148
1149############################################################
1150# divide_bigdir_into_minidirs() - splits one big directory #
1151#                                 into a few smaller ones  #
1152#                                 $_[0] - the dir to split #
1153#                                 $_[1] - the size of each #
1154#                                         minidir          #
1155#                                 $_[2] - the minidir root #
1156############################################################
1157sub divide_bigdir_into_minidirs {
1158    my $dir          = shift || fatal_error "Too few arguments to divide_bigdir_into_minidirs() - want 3, got 0";
1159    my $sz           = shift || fatal_error "Too few arguments to divide_bigdir_into_minidirs() - want 3, got 1";
1160    my $minidir_root = shift || fatal_error "Too few arguments to divide_bigdir_into_minidirs() - want 3, got 2";
1161    $sz =~ /-?[\d.]+/        or fatal_error "Size ($sz) should be a number!";
1162    my %filesizes;
1163    my (@filesorter, @current_size, @fileparts, @tmpdirparts);
1164    my ($progress, $total);
1165    if (called_by_mondo) {
1166    print "Dividing data into several groups...\n";
1167    } else {
1168    print "Dividing data into several groups:\t";
1169    }
1170    get_size_of_stuff \%filesizes, $dir;
1171
1172    # If we put the big files on the floppies first, then the small ones, we get very even sorting.
1173    # Example: ($MaxDiskSize = 1325)
1174    # Disk 1 should be about 1324K
1175    # Disk 2 should be about 1324K
1176    # Disk 3 should be about 1324K
1177    # Disk 4 should be about 1324K
1178    # Disk 5 should be about 1324K
1179    # Disk 6 should be about 1324K
1180    # Disk 7 should be about 865K
1181    #
1182    # A reverse sort (put small files on first) gives:
1183    # Disk 1 should be about 1306K
1184    # Disk 2 should be about 1300K
1185    # Disk 3 should be about 1267K
1186    # Disk 4 should be about 1312K
1187    # Disk 5 should be about 1268K
1188    # Disk 6 should be about 1301K
1189    # Disk 7 should be about 1055K
1190    #
1191    # The first choice is obviously preferable.
1192
1193    @filesorter = sort {$filesizes{$Mindi::b}{COMPRESSED} <=> $filesizes{$Mindi::a}{COMPRESSED}} keys %filesizes;
1194    $total = scalar @filesorter;
1195    foreach my $file (@filesorter) {
1196    my $newname;
1197    my $mode;
1198    my $disk_to_file_under = 1;
1199        for (; $disk_to_file_under < scalar @current_size; ++$disk_to_file_under) {
1200        if (($current_size[$disk_to_file_under] + $filesizes{$file}{COMPRESSED}) <= $MaxDiskSize) {
1201        last;
1202        }
1203    }
1204
1205    # If we found a suitable disk, then $disk_to_file_under is set to it. Easy.
1206    # If we *didn't* find a suitable disk, then $disk_to_file_under is set to 1 over the
1207    # last array subscript. The code below will "initialize" that disk, and the files will
1208    # get copied there.
1209   
1210    @fileparts   = split "/", $file;
1211    @tmpdirparts = split "/", $TempDir;
1212    splice @fileparts, 0, scalar @tmpdirparts + 1; # splice out 1 levels under the temp dir (tempdir/bigdir)
1213    $newname = join "/", @fileparts;
1214
1215    (undef, undef, $mode) = lstat $file;
1216    $mode &= 07777;
1217
1218    mkdir_p "$minidir_root/$disk_to_file_under";
1219    $current_size[$disk_to_file_under] += 0;       # create entry if it doesn't exist
1220    mkdir_p "$minidir_root/$disk_to_file_under/@{[ dirname $newname ]}";
1221    if (-f $file && !-l $file) {
1222        copy $file, "$minidir_root/$disk_to_file_under/$newname" or fatal_error "Could not copy $file to $minidir_root/$disk_to_file_under/$newname: $!";
1223        chmod $mode, "$minidir_root/$disk_to_file_under/$newname" or fatal_error "Could not chmod $mode $minidir_root/$disk_to_file_under/$newname: $!";
1224        loggit "DISK $disk_to_file_under: Filed $file ($filesizes{$file}{COMPRESSED}K) under $newname using regular copy()"
1225          if $LogVerbose;
1226    } elsif (-d $file) {
1227        mkdir "$minidir_root/$disk_to_file_under/$newname";
1228        loggit "DISK $disk_to_file_under: Filed $file under $newname using mkdir()"
1229          if $LogVerbose;
1230    } else {                       # weird file type; let the system cp deal with it
1231        system 'cp', '-R', "$file", "$minidir_root/$disk_to_file_under/$newname";
1232        loggit "DISK $disk_to_file_under: Filed $file under $newname using system cp"
1233          if $LogVerbose;
1234    }
1235    $current_size[$disk_to_file_under] += $filesizes{$file}{COMPRESSED};
1236    print_progress ++$progress, $total, "Dividing data into several groups:\t";
1237    }
1238    for (my $diskno = 1; $diskno < scalar @current_size; ++$diskno) {
1239    loggit "Disk $diskno should be about $current_size[$diskno]K";
1240    }
1241    my $cnt;
1242    opendir MINIDIR, $minidir_root or fatal_error "Can't opendir $minidir_root: $!";
1243    ++$cnt while readdir MINIDIR;
1244    closedir MINIDIR;
1245    $cnt -= 2; # account for `.' and `..'
1246    loggit "$cnt disks were created.";
1247    return $cnt;
1248}
1249
1250
1251############################################################
1252# make_data_disks() - make those data disks! require GenDL #
1253############################################################
1254sub make_data_disks {
1255    my ($bigdir, $tardir, $minidir_root, @mpts) =
1256       ("$TempDir/bigdir", "$TempDir/tardir", "$TempDir/minidir", "");
1257    my $noof_disks;
1258
1259    mkdir_p "$bigdir/usr/bin", $minidir_root, "$bigdir/tmp";
1260
1261    open NEEDLIST, ">> $TempDir/deplist.txt" or fatal_error "Can't append to $TempDir/deplist.txt: $!";
1262
1263    if ($KernelSucks) {
1264    my (@kmod_paths, @failsafe_paths, $kver);
1265
1266    (undef, undef, $kver) = uname();
1267
1268    chdir $TempDir;
1269    @kmod_paths     = get_kmod_paths();
1270
1271    find sub {
1272        -f $File::Find::name or return;
1273        my($basename) = basename $File::Find::name, qr{\.${ModuleSuffix}$};
1274        push @kmod_paths, $File::Find::name if grep { m[$basename] } @ExtraMods; # add in extra modules too
1275    }, "/lib/modules/$kver";
1276
1277    @failsafe_paths = make_paths_failsafe (@kmod_paths);
1278    foreach my $module (@failsafe_paths) {
1279        copy_stuff_into_dir $module, $bigdir;      # code in copy_stuff_into_dir will
1280                               # automatically remove the $TempDir part
1281                               # for the destination file
1282    }
1283    } else {
1284    foreach my $kmod_path (get_kmod_paths()) {
1285        loggit "MDD: Adding kmod $kmod_path to NEEDLIST";
1286        print NEEDLIST "$kmod_path\n";
1287    }
1288    }
1289
1290    close NEEDLIST;
1291
1292    loggit "--> Assembling dependency files...";
1293    copy_files_into_dir "$TempDir/deplist.txt", $bigdir;
1294    loggit "--> Assembling dependency files...done.";
1295
1296    if (called_by_mondo) {
1297    make_mondo_config ("$TempDir/mondo-restore.cfg");
1298    copy "$TempDir/mondo-restore.cfg", "$bigdir/tmp/mondo-restore.cfg";
1299    }
1300
1301    # copy io.sys and msdos.sys, if they're there
1302    open MOUNT, "$MountCmd |" or fatal_error "Can't open pipe from $MountCmd: $! [path=$ENV{'PATH'}]";
1303    while (<MOUNT>) {
1304    (undef, undef, my $mpt) = split;
1305    push @mpts, $mpt;
1306    }
1307    close MOUNT;
1308    foreach my $mountpoint (@mpts) {
1309    foreach my $msfile ("io.sys", "msdos.sys") {
1310        if (-e "$mountpoint/$msfile") {
1311        copy ("$mountpoint/$msfile", "$bigdir/$msfile") or
1312          logit "*** Can't copy $mountpoint/$msfile to $bigdir/$msfile: $!";
1313        }
1314    }
1315    }
1316
1317    if (called_by_mondo) {
1318    # MBR too
1319    open BLD, "$MondoTemp/BOOTLOADER.DEVICE" and do {
1320        my $dev = <BLD>;
1321        chomp $dev;
1322        loggit "Backing up ${dev}'s MBR";
1323        system "dd if=$dev of=$bigdir/BOOTLOADER.MBR bs=512 count=1 >>$Logfile 2>>$Logfile" and
1324          logit "*** Can't dd MBR of $dev to $bigdir/BOOTLOADER.MBR: exit $?";
1325        loggit "Creating /dev/boot_device -> $dev";
1326        mkdir_p "$bigdir/dev";
1327        system 'cp', '-pR', "$dev", "$bigdir/dev/boot_device" and
1328          logit "Warning - Can't copy $dev to $bigdir/dev/boot_device";
1329        close BLD;
1330        system "cp $MondoTemp/BOOTLOADER.* $bigdir/ >>$Logfile 2>&1";
1331    };
1332   
1333    # NFS stuff
1334    if (-e "$MondoTemp/start-nfs") {
1335        loggit "NFS backup detected.";
1336        print "Copying NFS settings: ";
1337        copy "$MondoTemp/start-nfs", "$bigdir/sbin/start-nfs" or
1338          fatal_error "Can't copy start-nfs: $!";
1339        chmod 0755, "$bigdir/sbin/start-nfs" or
1340          logit "Warning: Could not chmod +x start-nfs: $!";
1341        print "start-nfs ";
1342        for my $parm ('SERVER-MOUNT', 'SERVER-PATH', 'DEV', 'CLIENT-IPADDR', 'SERVER-IPADDR') {
1343        copy "$MondoTemp/NFS-$parm", "$bigdir/tmp/NFS-$parm" or
1344          fatal_error "Can't copy NFS-$parm: $!";
1345        print "NFS-$parm ";
1346        }
1347        print "done.\n";
1348    }
1349    }
1350
1351    # embleer - phased out
1352    #copy ("$Homes{'mindi'}/embleer.B.bz2", "$bigdir/embleer.B.bz2");
1353    #copy ("$Homes{'mindi'}/embleer.C.bz2", "$bigdir/embleer.C.bz2");
1354    #loggit "MDD: copied embleer";
1355
1356    # aux-tools
1357    if (-d "$Homes{'mindi'}/aux-tools") {
1358    system "cp -R $Homes{'mindi'}/aux-tools/* $bigdir/"
1359      and logit "*** Error copying aux-tools: exit $?";
1360    }
1361    loggit "MDD: copied aux-tools OK";
1362
1363    if ($Homes{'mondo'} and -d "$Homes{'mondo'}/restore-scripts") {
1364    if (system "cp -R $Homes{'mondo'}/restore-scripts/* $bigdir/") {
1365        logit "*** Error copying restore-scripts";
1366        fatal_error "These are needed for Mondo" if called_by_mondo;
1367    } else {
1368        loggit "MDD: copied restore-scripts OK";
1369    }
1370    }
1371
1372    # post-nuke
1373    if (called_by_mondo and -e "$MondoTemp/post-nuke.tgz") {
1374    untar_files "$MondoTemp/post-nuke.tgz", "$bigdir/"
1375      or fatal_error "Error extracting post-nuke tarball";
1376    loggit "MDD: copied post-nuke tarball OK";
1377    } else {
1378    loggit "MDD: no post-nuke tarball to copy";
1379    }
1380
1381    $noof_disks = divide_bigdir_into_minidirs $bigdir, $MaxDiskSize, $minidir_root;
1382    make_mountlist ("$TempDir/mountlist.txt");
1383    mkdir_p "$minidir_root/1/tmp", "$minidir_root/1/proc";
1384    system "touch $minidir_root/$noof_disks/LAST-DISK";
1385    copy "$TempDir/mountlist.txt", "$bigdir/tmp/mountlist.txt";
1386    copy "$TempDir/mountlist.txt", "$minidir_root/1/tmp/mountlist.txt";
1387    if (-d "/proc/lvm" or -d "/dev/mapper") {
1388    open ANALYZE_MY_LVM, "$Homes{'mindi'}/analyze-my-lvm |" or
1389      fatal_error "Can't open pipe from analyze-my-lvm: $!";
1390    open I_WANT_MY_LVM, ">$TempDir/i-want-my-lvm" or
1391      fatal_error "Can't write to $TempDir/i-want-my-lvm: $!";
1392    while (<ANALYZE_MY_LVM>) {
1393        print I_WANT_MY_LVM $_;
1394    }
1395    copy "$TempDir/i-want-my-lvm", "$bigdir/tmp/i-want-my-lvm";
1396    copy "$TempDir/i-want-my-lvm", "$minidir_root/1/tmp/i-want-my-lvm";
1397    }
1398    system 'touch "$minidir_root/$noof_disks/LAST-DISK"';
1399    zip_minidirs_into_tarballs ($minidir_root, $tardir, $noof_disks);
1400#   rejig_symlinks ($minidir_root, $noof_disks);
1401    create_data_disk_images_from_tarballs ($noof_disks, $tardir);
1402    return $noof_disks;
1403}
1404
1405
1406# ############################################################
1407# # move_symlink_sensibly($$$$) - move symlink to right disk #
1408# #              $_[0] - the file                            #
1409# #              $_[1] - the minidir_root                    #
1410# #              $_[2] - the disk it's on                    #
1411# #              $_[3] - the number of disks total           #
1412# ############################################################
1413# sub move_symlink_sensibly {
1414#     my $symlink      = shift || fatal_error "Too few arguments to move_symlink_sensibly - want 4, got 0";
1415#     my $minidir_root = shift || fatal_error "Too few arguments to move_symlink_sensibly - want 4, got 1";
1416#     my $cur_disk     = shift || fatal_error "Too few arguments to move_symlink_sensibly - want 4, got 2";
1417#     my $noof_disks   = shift || fatal_error "Too few arguments to move_symlink_sensibly - want 4, got 3";
1418#     my $tmp_disk;
1419
1420#     for ($tmp_disk = 1; $tmp_disk <= $noof_disks; ++$tmp_disk) {
1421#   loggit "Warning! move_symlink_sensibly() was called, but I don't know what to do!";
1422#   # XXX What is this function supposed to do?
1423#     }
1424# }
1425
1426# ############################################################
1427# # rejig_symlinks($$) - fix symlinks on minidirs            #
1428# #                      $_[0] - minidir root                #
1429# #                      $_[1] - number of disks             #
1430# ############################################################
1431# sub rejig_symlinks {
1432#     my $minidir_root = shift || fatal_error "Too few arguments to rejig_symlinks - want 2, got 0";
1433#     my $noof_disks   = shift || fatal_error "Too few arguments to rejig_symlinks - want 2, got 1";
1434#     my $cur_disk;
1435
1436#     for ($cur_disk = 1; $cur_disk <= $noof_disks; ++$cur_disk) {
1437#   chdir "$minidir_root/$cur_disk"
1438#     or fatal_error "Can't chdir to $minidir_root/$cur_disk: $!";
1439   
1440#   find (sub { move_symlink_sensibly $_, $minidir_root, $cur_disk, $noof_disks if -l }, '.');
1441#     }
1442# }
1443
1444############################################################
1445# find_exec() - can I find some program?                   #
1446#               $_[0] - the program                        #
1447#                 RET - true (found) or false (not)        #
1448############################################################
1449sub find_exec {
1450    my @path = split /:/, $ENV{'PATH'};
1451    my $prog = shift || fatal_error "Too few arguments to find_exec()";
1452    foreach my $pathitem (@path) {
1453    return "$pathitem/$prog" if -e "$pathitem/$prog";
1454    }
1455    return;
1456}
1457
1458############################################################
1459# check_system() - some basic sanity checks                #
1460############################################################
1461sub check_system {
1462    if (determine_if_called_by_mondo) {
1463    find_exec ("afio") or fatal_error "Mondo needs afio. Mondo called me. I shall abort.";
1464    }
1465
1466    find_exec "strings" or fatal_error "Please install binutils - you have no strings.";
1467    find_exec "mke2fs"  or fatal_error "Where's mke2fs?";
1468    find_exec "mkdosfs"
1469      or find_exec "mkfs.vfat" or fatal_error "Where's mkfs.vfat? [Please install dosfstools.]";
1470    find_exec "syslinux" or do {
1471    logit "WARNING - you have no syslinux; boot disk will use LILO";
1472    $UseLILO = 1;
1473    };
1474
1475    -e "/etc/modules.conf" or fatal_error "What happened to /etc/modules.conf?";
1476
1477    find_isolinux_path();
1478
1479    $MountCmd = find_exec "mount";
1480    $UnmountCmd = find_exec "umount";
1481    fatal_error "Can't find mount" if not defined $MountCmd;
1482    fatal_error "Can't find umount" if not defined $UnmountCmd;
1483    loggit "System passed Mindi's sanity check.";
1484}
1485
1486############################################################
1487# usage() - print a usage message                          #
1488############################################################
1489
1490sub usage {
1491    my $fh;
1492    if (-t STDOUT) {
1493    open $fh, "| @{[ $ENV{'PAGER'} || 'less' ]}";
1494    } else {
1495    $fh = *STDOUT{IO};
1496    }
1497    print $fh <<EOU;
1498Usage: mindi [-CFMPXcfhiklmptx2]
1499       mindi --custom <temp-dir> <image-dir> <kernel> <tape-device | ''>
1500                      <tape-size | ''> <files-in-filelist> <use-lzo?->
1501                      <cdrecovery?-> <image-devs+> '' <last-filelist-num>
1502                      <est-noof-slices> <exclude-devs+> <using-compression?+>
1503                      <use-lilo?-> <nonbootable?-> <differential>
1504       For mindi --custom, all arguments with a ? at the end should be 'yes'
1505       or 'no'. Default is 'no' if there is a minus sign at the end of the
1506       help for it, 'yes' if there is a plus sign. Lists are indicated with
1507       a plus sign at the end of the help for that item and should be
1508       separated by '|', ' ', or ','. Any argument can be omitted by
1509       making it empty ('').
1510
1511-c [--prompt-cd]     : prompt if CD image wanted, otherwise just make it
1512-C [--max-csize] SIZ : max size of stuff to put on floppy (Kb) [1200]
1513-f [--make-floppies] : ask if floppies wanted as well as CD, otherwise skip
1514-F [--max-fsize] SIZ : maximum size of file before it is chopped [$ChopAt]
1515-h [--help] [-?]     : this screen
1516-i [--image-dir] DIR : directory to put generated images in [$ImagesDir]
1517-l [--log[file]] LOG : logfile [$Logfile]
1518-L [--use-lilo]      : use LILO (not syslinux) for boot disk.
1519-k [--kernel] KERNEL : path to your kernel [will try to find]
1520-m [--mondo]         : assume called by mondo
1521-M [--nomondo]       : assume not called by mondo
1522-o [--option] K=V    : Set a mindi.conf option (K is the option name, V is the value)
1523-p [--pid-file] FILE : file to register PID in
1524-P [--force-pid]     : kill other Mindi processes instead of aborting
1525-t [--temp-dir]  DIR : temporary directory to use [$TempDir]
1526-v [--log-verbose]   : log a LOT of information to the logfile (as much as 33K!)
1527-x [--extra-space] X : amount of extra space on the ramdisk [$ExtraSpace]
1528-X [--chop-size] SIZ : size of file slices [$ChopSize]
1529-2 [--only-2880]     : Only make 2880k floppies, no 1722k ones
1530EOU
1531    close $fh if -t STDOUT;
1532    exit 1;
1533}
1534
1535############################################################
1536# add_to_cfg($*) - add file to Mondo configuration file    #
1537#                  $_[0] - config var to add               #
1538#                  $_[1] - reference to cfg filehandle     #
1539############################################################
1540sub add_to_cfg {
1541    my $var  = shift || fatal_error "Too few arguments to add_to_cfg - want 2, got 0";
1542    my $fh   = shift || fatal_error "Too few arguments to add_to_cfg - want 2, got 1";
1543    my $file = "$MondoTemp/@{[ uc $var ]}";
1544    my $res;
1545    if ((ref $fh) !~ /IO/) {
1546    fatal_error "You gave me a @{[ ref $fh ]} reference, but I want an IO::Handle reference.\nTry `*CFG{IO}'.";
1547    }
1548    return unless -e $file;
1549    open VAR, $file or fatal_error "$file exists, but I couldn't open it: $!";
1550    $res = <VAR>;
1551    chomp $res;
1552    print $fh "$var $res\n";
1553    close VAR;
1554}
1555
1556############################################################
1557# make_mondo_config($) - make Mondo configuration file     #
1558#                       $_[0] - place to put it            #
1559############################################################
1560sub make_mondo_config {
1561    open CFG, "> $_[0]" or fatal_error "MMC: can't open $_[0]: $!";
1562   
1563    print CFG "media-size $TapeSize\n"                  if $TapeSize;
1564    print CFG "media-dev $TapeDevice\n"                 if $TapeDevice;
1565    print CFG "files-in-filelist $FilesInFilelist\n"    if $FilesInFilelist;
1566    print CFG "last-filelist-number $LastFilelistNum\n" if $LastFilelistNum;
1567    if ($UsingLZO) {
1568    print CFG "use-lzo yes\n";
1569    } else {
1570    print CFG "use-lzo no\n";
1571    }
1572    if ($UsingCompression) {
1573    print CFG "use-comp yes\n";
1574    } else {
1575    print CFG "use-comp no\n";
1576    }
1577    print CFG "datestamp @{[ time ]}\n";
1578    print CFG "total-slices $EstNoofSlices\n"           if $EstNoofSlices;
1579    add_to_cfg "nfs-client-ipaddr",      *CFG{IO};
1580    add_to_cfg "nfs-server-mount",       *CFG{IO};
1581    add_to_cfg "nfs-server-path",        *CFG{IO};
1582    add_to_cfg "nfs-dev",                *CFG{IO};
1583    add_to_cfg "nfs-server-ipaddr",      *CFG{IO};
1584    add_to_cfg "iso-dev",                *CFG{IO};
1585    add_to_cfg "isodir",                 *CFG{IO};
1586    add_to_cfg "bootloader.device",      *CFG{IO};
1587    add_to_cfg "bootloader.name",        *CFG{IO};
1588    add_to_cfg "keymap-lives-here",      *CFG{IO};
1589    add_to_cfg "tapedev-has-data-disks", *CFG{IO};
1590    add_to_cfg "backup-media-type",      *CFG{IO};
1591    add_to_cfg "differential",           *CFG{IO};
1592    close CFG;
1593}
1594
1595############################################################
1596# get_ptype_of($) - get partition type of a partition      #
1597# Returns - the two-letter hexcode                         #
1598############################################################
1599sub get_ptype_of {
1600    my $device = shift;
1601    my $ptno = $device;
1602    my $disk = $device;
1603    $ptno =~ s/.*(\d+)$/$1/;
1604    $disk =~ s/\d+$//;
1605    sprintf "%02x", get_partition_type($disk, $ptno);
1606}
1607
1608############################################################
1609# make_mountlist($) - create mountlist of partitions       #
1610#                     $_[0] - the file to put it in        #
1611############################################################
1612sub make_mountlist {
1613    my (@partitions, @raid_partitions);
1614    my @ignorepatterns = ("/dev/a?f(d|loppy/)[0-9]+([.uhH][0-9]+)?", # floppy drive (including IDE Zip)
1615              "/dev/([as]?cd(rom)?(s/cdrom)?.*|sr)[0-9]*", # CD-ROMs
1616              "/dev/(sa|ht|st|ast)[0-9]+", # tape drives (why could they show up? beyond me)
1617              "(([0-9]{1,3}\.){3}[0-9]{1,3}|[a-z.-]+):/.*", # NFS mounts (both DNS and IP)
1618             );
1619    my (%curline, @curline);
1620    my @fstab_lines;
1621    my @unmount_these;
1622    my $mountlist = shift || fatal_error "Too few args to make_mountlist() - want 1, got 0";
1623
1624    if (-d "/proc/lvm" or -d "/dev/mapper") {
1625    open LVM_ANALYZER, "$Homes{'mindi'}/analyze-my-lvm |" or fatal_error "Can't open from analyze-my-lvm: $!";
1626    while (<LVM_ANALYZER>) {
1627        chomp;
1628        if (/>>>/) {
1629        s/^>+\s*//;
1630        @partitions = (@partitions, split);
1631        }
1632    }
1633    close LVM_ANALYZER;
1634    }
1635
1636    open FSTAB, "/etc/fstab" or fatal_error "Can't open /etc/fstab: $!";
1637    while (<FSTAB>) {
1638    my $comment = '#';
1639    next if /^$comment/;
1640    next if /^$/;
1641    next if /(floppy|fd)|cd(rom)?|dvd|zip|media|^\/proc|^\/dev\s+|^\/dev\/pts|devpts/;
1642    next if m[^/sys\s+] && $KernelVersion =~ /2.6/;
1643    chomp;
1644    push @fstab_lines, $_;
1645    @curline = split;
1646    next if $curline[3] =~ /noauto/ and $curline[1] !~ /boot/;
1647    if ($curline[0] =~ /dev/) {
1648        push @partitions, $curline[0];
1649    } elsif ($curline[0] =~ m|LABEL=([a-zA-Z0-9./-]+)|) {
1650        my $label = $1;
1651        my $found = 0;
1652        my $needs_umount = 0;
1653        unless (grep { m[$label] } `mount -l`) {
1654        system "$MountCmd $curline[1]" and fatal_error "LABEL=$label is not mounted and I can't mount it.";
1655        $needs_umount = 1;
1656        }
1657        open MOUNT_L, "$MountCmd -l |" or fatal_error "Can't open pipe from mount to read labels: $!";
1658        while (<MOUNT_L>) {
1659        chomp;
1660        if (/\[$label\]/) {
1661            if (/^(.*) on .* type .*/) {
1662            $found = 1;
1663            push @partitions, $1;
1664            push(@unmount_these, $1), $needs_umount = 0 if $needs_umount;
1665            }
1666        }
1667        }
1668        close MOUNT_L;
1669
1670        if (not $found) {
1671        fatal_error "Couldn't resolve LABEL=$label; are you *sure* that device is mounted?";
1672        }
1673    }
1674    }
1675    close FSTAB;
1676
1677    if (-e "/etc/raidtab") {
1678    if (open RAIDTAB, "/etc/raidtab") {
1679        while (<RAIDTAB>) {
1680        my @raidtabline;
1681        next unless /^\s*device/;
1682        chomp;
1683        s/^\s+//;
1684        @raidtabline = split;
1685        if (scalar @raidtabline >= 2) {
1686            push @partitions,      $raidtabline[1];
1687            push @raid_partitions, $raidtabline[1];
1688        }
1689        }
1690        close RAIDTAB;
1691    } else {
1692        logit "*** Can't open /etc/raidtab [$!] -- not sure about your RAID configuration";
1693    }
1694    }
1695
1696    foreach my $imagedev (@ImageDevs) {
1697    if (grep { m[$imagedev] } `mount`) {
1698        fatal_error "Cannot imagedev $imagedev when it's mounted! Please unmount it and try again.";
1699    }
1700    push @partitions, $imagedev
1701      unless grep { m[$imagedev] } @partitions;
1702    }
1703
1704    open MOUNTLIST, "> $mountlist" or fatal_error "Can't open $mountlist for writing: $!";
1705    logit "\nYour mountlist will look like this:-";
1706#                                                                             |
1707#                                                            80th column here v
1708    format STDOUT_TOP =
1709@<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<< @<<<<<<<< @>>>>>>>>>>>> @>>>>>>>>>>>>>>>>
1710"PARTITION",        "MOUNTPOINT",    "FSTYPE", "SIZE (KB)",  "[LABEL]"
1711------------------- ----------------- --------- ------------- -----------------
1712.
1713
1714    format STDOUT =
1715@<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<< @<<<<<<<< @>>>>>>>>>>>> @<<<<<<<<<<<<<<<<
1716$curline{'partition'}, $curline{'mountpoint'}, $curline{'fstype'}, $curline{'size'}, $curline{'label'}
1717.
1718
1719  PARTITION:
1720    foreach (@partitions) {
1721    foreach my $ipat (@ignorepatterns) {
1722        next PARTITION if /$ipat/;
1723    }
1724    next PARTITION if grep { m[$_] } @ExcludeDevs;
1725
1726    my $partition   = $_;
1727    my $search_with = $_;
1728    my $neededmount = 0;
1729
1730    open MOUNT_L, "$MountCmd -l |" or fatal_error "Can't open pipe from mount to read labels: $!";
1731    while (<MOUNT_L>) {
1732        chomp;
1733        if (/$partition/) {
1734        if (/\[(.*)\]/) {
1735            $search_with .= "|LABEL=$1"; # regex: alternatives (partition|LABEL=label)
1736        }
1737        }
1738    }
1739    close MOUNT_L;
1740
1741    $_ = $partition;
1742    my($alternative) = "";
1743
1744    if ($LVMVersion == 2 and -d "/dev/mapper") {
1745        ($alternative = $partition) =~ s[/mapper/vg([0-9]*)-][/vg$1/]g;
1746        $alternative =~ s/\-\-/-/g;
1747        if ($alternative ne $partition) {
1748        $search_with .= "|$alternative";
1749        }
1750    }
1751
1752    $curline{'found'}              = 0;
1753
1754    # LVM is completely braindead about its return code...
1755    if (grep { /Logical volume/ } `lvdisplay $_ 2>/dev/null`) {
1756        $curline{'size'}           = "lvm";
1757    } elsif ($LVMVersion == 2 and -d "/dev/mapper" and grep { /.../ } `lvm lvdisplay $_ 2>/dev/null`) {
1758        $curline{'size'}           = "lvm";
1759    } else {
1760        $curline{'size'}           = get_size_of_disk $_;
1761    }
1762   
1763    foreach my $fstab_line (@fstab_lines) {
1764        if ($fstab_line =~ /^($search_with)\s/) {
1765        $fstab_line =~ s/\s+/ /;
1766        @curline = split ' ', $fstab_line;
1767        $curline{'found'}      = 1;
1768        $curline{'partition'}  = $_;
1769        $curline{'mountpoint'} = $curline[1];
1770        $curline{'fstype'}     = $curline[2];
1771        if ($search_with =~ /LABEL/) {
1772            $search_with =~ s/.*LABEL=//;
1773            $curline{'label'}  = $search_with;
1774        }
1775        last;
1776        }
1777    }
1778
1779    if (not $curline{'found'}) {
1780        if (grep { m[$_] } @raid_partitions) {
1781        $curline{'found'}      = 1;
1782        $curline{'partition'}  = $_;
1783        $curline{'mountpoint'} = "raid";
1784        $curline{'fstype'}     = "raid";
1785        }
1786    }
1787   
1788    if (not $curline{'found'}) {
1789        if (grep { /Physical volume/ } `pvdisplay $_ 2>/dev/null` or
1790            grep { /.../ } `lvm pvdisplay $_ 2>/dev/null`) {
1791        $curline{'found'}      = 1;
1792        $curline{'fstype'}     = "lvm";
1793        $curline{'mountpoint'} = "lvm";
1794        $curline{'partition'}  = $_;
1795        }
1796    }
1797
1798    if (grep { m[$_] } @ImageDevs) {
1799        $curline{'found'}          = 1;
1800        $curline{'fstype'}         = get_ptype_of $_;
1801        $curline{'partition'}      = $_;
1802        $curline{'mountpoint'}     = "image";
1803        $curline{'size'}++;
1804    }
1805
1806    fatal_error "Can't find $_!"                                 unless $curline{'found'};
1807    fatal_error "Can't find mountpoint of $curline{'partition'}" unless $curline{'mountpoint'};
1808    fatal_error "Can't find fstype of $curline{'partition'}"     unless $curline{'fstype'};
1809    fatal_error "Can't find size of $curline{'partition'}"       unless $curline{'size'};
1810
1811    $curline{'fstype'}             = "swap" if $curline{'mountpoint'} eq "swap";
1812    $curline{'mountpoint'}         = "swap" if $curline{'fstype'}     eq "swap";
1813    $curline{'label'}              = ""     unless defined $curline{'label'};
1814
1815    if ($alternative ne "") {
1816        $curline{'partition'} = $alternative;
1817    }
1818
1819    print MOUNTLIST "$curline{'partition'} $curline{'mountpoint'} $curline{'fstype'} $curline{'size'} $curline{'label'}\n";
1820    loggit "$curline{'partition'} $curline{'mountpoint'} $curline{'fstype'} $curline{'size'} $curline{'label'}";
1821
1822    # We've already written to the logfile + mountlist: let's shorten the stuff a little.
1823    $curline{'label'}      = "[no label]" unless $curline{'label'};
1824
1825    $curline{'partition'}  =~ s/.{4,}(.{16})/...$1/;
1826    $curline{'mountpoint'} =~ s/.{4,}(.{14})/...$1/;
1827    $curline{'label'}      =~ s/.{4,}(.{14})/...$1/;
1828
1829    write;
1830    $curline{'label'} = "";
1831    }
1832
1833    for (@unmount_these) {
1834    loggit "$_ was mounted temporarily. Unmounting... ";
1835    system "$UnmountCmd $_" and logit "Warning: FAILED to unmount $_ again";
1836    }
1837
1838    close MOUNTLIST;
1839}
1840
1841############################################################
1842# write_one_liner($$) - write one line file                #
1843#                       $_[0] - the file                   #
1844#                       $_[1] - the line                   #
1845############################################################
1846sub write_one_liner {
1847    my $file = shift || fatal_error "Too few arguments to write_one_liner - want 2, got 0";
1848    open  WRITER, "> $file" or fatal_error "Can't open $file for writing: $!";
1849    print WRITER @_, "\n";
1850    close WRITER;
1851}
1852
1853############################################################
1854# zip_minidirs_into_tarballs($$$) - self-explanatory       #
1855#              $_[0] - the minidir root                    #
1856#              $_[1] - the tardir                          #
1857#              $_[2] - the number of disks                 #
1858############################################################
1859sub zip_minidirs_into_tarballs {
1860    my $minidir_root = shift || fatal_error "Too few arguments to zip_minidirs_into_tarballs() - want 3, got 0";
1861    my $tardir       = shift || fatal_error "Too few arguments to zip_minidirs_into_tarballs() - want 3, got 1";
1862    my $noof_disks   = shift || fatal_error "Too few arguments to zip_minidirs_into_tarballs() - want 3, got 2";
1863    my $compressed_tool_size;
1864    my @disk_sizes;
1865
1866    mkdir_p $tardir;
1867    mkdir_p "$minidir_root/all/tmp";
1868
1869    print "\nTarring and zipping the groups...\n" if called_by_mondo;
1870    print "\nTarring and zipping the groups:\t\t[>", ' ' x 29, "] 0% |" unless called_by_mondo;
1871
1872    for (my $cur_disk = 1; $cur_disk <= $noof_disks; ++$cur_disk) {
1873    chdir "$minidir_root/$cur_disk" or fatal_error "Can't chdir to $minidir_root/$cur_disk: $!";
1874
1875    tar_up_files ("$tardir/$cur_disk.tar.gz", ".", "Tarring and zipping the groups:\t\t")
1876#           , 2 * $noof_disks, ($cur_disk - 1) * (100 / (2 * $noof_disks)))
1877      or  fatal_error "Can't tar/gzip disk $cur_disk!";
1878
1879    system "cp -pRf * $minidir_root/all/"
1880      and fatal_error "cp -pRf @{[ <*> ]} $minidir_root/all/ failed: exit $?";
1881
1882    copy "$tardir/$cur_disk.tar.gz", "$ImagesDir/$cur_disk.tar.gz"
1883      or  fatal_error "Can't copy $tardir/$cur_disk.tar.gz to $ImagesDir/: $!";
1884    if ($ImagesDir ne "/root/images/mindi") {
1885        copy "$tardir/$cur_disk.tar.gz", "/root/images/mindi/$cur_disk.tar.gz";
1886    }
1887
1888    $disk_sizes[$cur_disk] = `du -sk $tardir/$cur_disk.tar.gz | cut -f1 | tr -d '\n'`;
1889    print_progress $cur_disk, $noof_disks + 1, "Tarring and zipping the groups:\t\t";
1890    }
1891
1892    for (my $cur_disk = 1; $cur_disk <= $noof_disks; ++$cur_disk) {
1893    loggit "Disk $cur_disk is $disk_sizes[$cur_disk]K";
1894    }
1895
1896    chdir "$minidir_root/all" or fatal_error "Can't chdir to $minidir_root/all: $!";
1897    $ToolSize = `du -sk . | cut -f1 | tr -d '\n'`;
1898
1899    if (called_by_mondo) {
1900    copy "$MondoTemp/filelist.full", "tmp/filelist.full"   or fatal_error "Could not copyin filelist.full: $!";
1901    system "gzip tmp/filelist.full"                        or fatal_error "Could not compress filelist.full: $!";
1902    copy "$MondoTemp/biggielist.txt", "tmp/biggielist.txt" or fatal_error "Could not copyin biggielist.txt: $!";
1903    write_one_liner "$minidir_root/all/tmp/FILES-IN-FILELIST",    $FilesInFilelist;
1904    write_one_liner "$minidir_root/all/tmp/LAST-FILELIST-NUMBER", $LastFilelistNum;
1905    }
1906
1907    tar_up_files "$tardir/all.tar.gz", "-b 4096", glob "*"     or fatal_error "Could not tar up all.tar.gz: exit $?";
1908    copy         "$tardir/all.tar.gz", "$ImagesDir/all.tar.gz" or fatal_error "Could not copy all.tar.gz to imagedir: $!";
1909    if ($ImagesDir ne "/root/images/mindi") {
1910    copy         "$tardir/all.tar.gz", "/root/images/mindi/all.tar.gz";
1911    }
1912
1913    print_progress $noof_disks + 1, $noof_disks + 1, "Tarring and zipping the groups:\t\t";
1914
1915    $compressed_tool_size = `du -sk $tardir/all.tar.gz | cut -f1 | tr -d '\n'`;
1916    fatal_error "You have too many tools in your shed!" if $compressed_tool_size >= 30*1024;
1917}
1918
1919############################################################
1920# make_zfile() - make a file full of zeroes                #
1921#                $_[0] - the size in kilobytes             #
1922#                $_[1] - the file                          #
1923############################################################
1924sub make_zfile {
1925    my $size = shift || fatal_error "Too few arguments to make_zfile() - want 2, got 0";
1926    my $file = shift || fatal_error "Too few arguments to make_zfile() - want 2, got 1";
1927
1928    system "dd if=/dev/zero of=$file bs=1k count=$size >/dev/null 2>/dev/null";
1929}
1930
1931############################################################
1932# create_tiny_filesystem() - like it says                  #
1933#                            $_[0] - device to create on   #
1934############################################################
1935sub create_tiny_filesystem {
1936    my $device = shift || fatal_error "Too few arguments to create_tiny_filesystem() - want 1, got 0";
1937    my $inodes = shift || 200;
1938    my $inodes_str = "";
1939    $inodes_str = "-N $inodes" unless $inodes == -1;
1940
1941    system "mke2fs -m 0 $inodes_str -F $device >>$Logfile 2>>$Logfile";
1942}
1943
1944############################################################
1945# create_data_disk_images_from_tarballs() - like it says   #
1946#             $_[0] - number of disks                      #
1947#             $_[1] - tarball directory                    #
1948############################################################
1949sub create_data_disk_images_from_tarballs {
1950    my $noof_disks = shift || fatal_error "Too few arguments to create_data_disk_images_from_tarballs() - want 2, got 0";
1951    my $tardir     = shift || fatal_error "Too few arguments to create_data_disk_images_from_tarballs() - want 2, got 1";
1952    my $cur_disk;
1953
1954    if (called_by_mondo) {
1955    print "Creating data disks...\n";
1956    } else {
1957    print "Creating data disks:\t\t\t[>", ' ' x 29, "] 0% |";
1958    }
1959
1960    for ($cur_disk = 1; $cur_disk <= $noof_disks; ++$cur_disk) {
1961    my ($mountpoint, $file);
1962    make_zfile 1440, "$ImagesDir/mindi-data-$cur_disk.img";
1963
1964    $file       = "$ImagesDir/mindi-data-$cur_disk.img";
1965    $mountpoint = "$TempDir/mountpoint.$$";
1966   
1967    mkdir_p $mountpoint;
1968    create_tiny_filesystem $file, 12;          # 12 inodes
1969
1970    system "$MountCmd -t ext2 -o loop $file $mountpoint"
1971      and fatal_error "Can't loopmount $file at $mountpoint: exit $?";
1972
1973    push @Mountpoints, $mountpoint; # save in case of crash
1974
1975    copy "$tardir/$cur_disk.tar.gz", "$mountpoint/$cur_disk.tar.gz"
1976      or fatal_error "Can't copy $tardir/$cur_disk.tar.gz to $mountpoint/$cur_disk.tar.gz: $!";
1977    system "$UnmountCmd $mountpoint"
1978      and logit "\nWarning - unable to umount $mountpoint (exit $?)";
1979
1980    pop @Mountpoints;
1981
1982    if ($ImagesDir ne "/root/images/mindi") {
1983        copy $file, "/root/images/mindi/";
1984    }
1985
1986    print_progress $cur_disk, $noof_disks, "Creating data disks:\t\t\t";
1987    }
1988}
1989
1990############################################################
1991# replace_line($$@) - replace a line in a file             #
1992#                     $_[0] - the file to look in          #
1993#                     $_[1] - the text in the line to repl #
1994#                     $_[2] - the stuff to replace it with #
1995############################################################
1996sub replace_line {
1997    my $file    = shift || fatal_error "Too few arguments to replace_line() - want 3+, got 0";
1998    my $search  = shift || fatal_error "Too few arguments to replace_line() - want 3+, got 1";
1999    my @replace = @_;
2000
2001    move $file, "$file.bak" or fatal_error "Can't move $file to $file.bak: $!";
2002    open OLD, "$file.bak"   or fatal_error "Can't open $file.bak for reading: $!";
2003    open NEW, "> $file"     or fatal_error "Can't open $file for writing: $!";
2004
2005    while (<OLD>) {
2006    if (/$search/) {
2007        foreach my $line (@replace) {
2008        print NEW $line, "\n";
2009        }
2010    } else {
2011        print NEW $_;
2012    }
2013    }
2014
2015    my $mode = 0;
2016    (undef, undef, $mode) = stat "$file.bak";
2017    if ($mode != 0) {
2018    chmod $mode & 07777, $file;
2019    }
2020
2021    close OLD;
2022    close NEW;
2023}
2024
2025############################################################
2026# make_module_loader($) - make script to load klds         #
2027#                         $_[0] - the script               #
2028############################################################
2029sub make_module_loader {
2030    my $script = shift || fatal_error "Too few arguments to make_module_loader() - want 1, got 0";
2031    my $kver;
2032
2033    open SCRIPT, "> $script" or fatal_error "Can't open $script for writing: $!";
2034    print SCRIPT "#!/bin/sh\n# module loading script\n# generated by mindi $Version\n\n";
2035    print SCRIPT "echo -n 'Loading your modules...'\n\n";
2036
2037    if ($KernelSucks) {
2038    chdir $TempDir;
2039    $kver = $FailsafeKernelVersion;
2040    } else {
2041    chdir "/";
2042    (undef, undef, $kver) = uname();
2043    }
2044
2045    print SCRIPT "for outerloop in 1 2 3; do true;\n";
2046
2047    for my $module (@ExtraMods, get_kmods()) {
2048    my ($params, @modpaths);
2049    $params = "";
2050
2051    open MODULES_CONF, "/etc/modules.conf" or fatal_error "Can't open /etc/modules.conf: $!";
2052    while (<MODULES_CONF>) {
2053        chomp;
2054        next unless /\s*options\s+$module\s+(.*)/;
2055        $params = $1;
2056        last;
2057    }
2058    close MODULES_CONF;
2059
2060    @modpaths = find_specific_kmod_in_path ("lib/modules/$kver", $module);
2061    foreach my $submod (@modpaths) {
2062        $submod =~ s/\.gz$//;
2063
2064        print SCRIPT "\t# Load $submod.$ModuleSuffix\n";
2065        print SCRIPT "\tif test -f $submod; then\n";
2066        print SCRIPT "\t\tif insmod $submod $params >/dev/null 2>&1; then\n";
2067        print SCRIPT "\t\t\techo -n .\n\t\tfi\n\tfi\n\n";
2068    }
2069    }
2070    print SCRIPT "done\n";
2071    print SCRIPT "\necho 'Done.'\n";
2072    close SCRIPT;
2073    chmod 0755, $script;
2074}
2075
2076############################################################
2077# make_ramdisk($$$) - self-explanatory                     #
2078#                     $_[0] - the directory to make from   #
2079#                     $_[1] - the filename of the ramdisk  #
2080#                     $_[2] - the size of the ramdisk      #
2081############################################################
2082sub make_ramdisk {
2083    my $dirname     = shift || fatal_error "Too few arguments to make_ramdisk() - want 3, got 0";
2084    my $ramdisk     = shift || fatal_error "Too few arguments to make_ramdisk() - want 3, got 1";
2085    my $ramsize     = shift || fatal_error "Too few arguments to make_ramdisk() - want 3, got 2";
2086    my $tempram     = "$TempDir/temp.rd";
2087    my $mountpt     = "$TempDir/mountpoint.$$";
2088    my $kver;
2089    my @groovy_mods = @CDMods;
2090
2091    $RamdiskSize = $ramsize;
2092
2093    print "Creating ramdisk...";
2094    print "\n" if called_by_mondo;
2095
2096    make_zfile $ramsize, $tempram;
2097    print "..." unless called_by_mondo;
2098    create_tiny_filesystem $tempram, 65536;
2099    print "..." unless called_by_mondo;
2100    mkdir_p $mountpt;
2101    system "$MountCmd -t ext2 -o loop $tempram $mountpt"
2102      and fatal_error "Can't mount $tempram on $mountpt (exit $?)";
2103
2104    push @Mountpoints, $mountpt;
2105
2106    print "..." unless called_by_mondo;
2107    system "cp -R $dirname/* $mountpt/";
2108    finddepth sub {
2109    system "rm -rf $_"
2110        if /CVS$/;
2111    }, $mountpt;
2112#   system 'find', $mountpt, '-depth', '-name', 'CVS', '-exec', 'rm', '-rf', '{}', ';';
2113    mkdir_p "$mountpt/dev";
2114    untar_files "$dirname/dev/dev-entries.tgz", "$mountpt/dev";
2115    replace_line "$mountpt/sbin/init", "#WHOLIVESINAPINEAPPLEUNDERTHESEA#", @AfterInitialPhase;
2116    replace_line "$mountpt/sbin/init", "#ABSORBENTANDYELLOWANDPOROUSISHE#", @PostBoot;
2117    print "..." unless called_by_mondo;
2118    make_module_loader "$mountpt/sbin/insert-all-my-modules";
2119    print "..." unless called_by_mondo;
2120
2121    mkdir_p "$mountpt/tmp";
2122    if (called_by_mondo) {
2123    make_mondo_config "$mountpt/tmp/mondo-restore.cfg";
2124    copy "$mountpt/tmp/mondo-restore.cfg", "$MondoTemp/mondo-restore.cfg";
2125    copy "$TempDir/mountlist.txt", "$mountpt/tmp/mountlist.txt"
2126      or fatal_error "Can't copy mountlist to ramdisk: $!";
2127
2128    write_one_liner "$mountpt/tmp/TAPEDEV-LIVES-HERE",   $TapeDevice        if $TapeDevice;
2129    write_one_liner "$mountpt/tmp/FILES-IN-FILELIST",    $FilesInFilelist;
2130    write_one_liner "$mountpt/tmp/LAST-FILELIST-NUMBER", $LastFilelistNum;
2131    write_one_liner "$mountpt/tmp/USING-LZO",            "Pras 4 Pres 2004" if $UsingLZO;
2132    write_one_liner "$mountpt/tmp/USING-COMP",           "Compression, yep" if $UsingCompression;
2133    }
2134    write_one_liner     "$mountpt/tmp/2880.siz",             "2880";
2135
2136    open LSMOD, "lsmod |" or fatal_error "Can't open pipe from lsmod: $!";
2137    while (<LSMOD>) {
2138    my @line;
2139
2140    chomp;
2141    next if /Module/;                  # skip header line
2142    @line = split;
2143    push @groovy_mods, $line[0] if grep { /$line[0]/ } @SCSIMods;
2144    }
2145    close LSMOD;
2146
2147    if ($KernelSucks) {
2148    $kver = $FailsafeKernelVersion;
2149    chdir $TempDir;
2150    } else {
2151    (undef, undef, $kver) = uname();
2152    chdir "/";
2153    }
2154
2155    # If NFS/PXE add related modules and start-nfs
2156    if (-e "$MondoTemp/start-nfs") {
2157        @groovy_mods =  (@groovy_mods,@NetMods);
2158        copy "$mountpt/sbin/start-nfs", "$MondoTemp/start-nfs";
2159        # Here we need the net busybox
2160        move "$mountp/busybox.net", "$mountp/busybox";
2161    } else {
2162        unlink "$mountp/busybox.net";
2163
2164    foreach my $module (@groovy_mods) {
2165    my @fullmods = find_specific_kmod_in_path ("lib/modules/$kver", $module);
2166    foreach my $submod (@fullmods) {
2167        copy $submod, "$mountpt/$module.$ModuleSuffix";
2168        if ($submod =~ /\.gz/) {
2169        move "$mountpt/$module.$ModuleSuffix", "$mountpt/$module.$ModuleSuffix.gz";
2170        system "gunzip $mountpt/$module.$ModuleSuffix.gz";
2171        }
2172    }
2173    }
2174
2175    print "..." unless called_by_mondo;
2176
2177    if ($LogVerbose) {
2178    loggit "Creating symlinks... Here is the table:";
2179    loggit Dumper \%RootfsSymlinks;
2180    }
2181    chdir $mountpt;
2182    for my $link (keys %RootfsSymlinks) {
2183    unless (-e $link or -l $link) {
2184        mkdir_p dirname $link;
2185        loggit "Symlink $link => $RootfsSymlinks{$link}" if $LogVerbose;
2186            # weird syntax here: symlink $target, $link  ==>  symlink $link to $target
2187        symlink $RootfsSymlinks{$link}, $link or do {
2188        loggit `ls -lR`;
2189        fatal_error "Can't symlink $link to $RootfsSymlinks{$link}: $!";
2190        }
2191    }
2192    }
2193
2194    chdir "/";
2195    system "$UnmountCmd $mountpt"
2196      and fatal_error "Can't umount $mountpt (exit $?)";
2197
2198    pop @Mountpoints;
2199
2200    system "gzip -9 -c $tempram > $ramdisk";
2201    print " done.\n" unless called_by_mondo;
2202}
2203
2204############################################################
2205# edit_ramdisk(\&$$;$) - do something to the ramdisk       #
2206#                  $_[0] - the thing to do (sub ref)       #
2207#                  $_[1] - the input ramdisk               #
2208#                  $_[2] - the output ramdisk              #
2209#       [OPTIONAL] $_[3] - the progress message            #
2210############################################################
2211sub edit_ramdisk {
2212    my $sub        = shift || fatal_error "Too few arguments to edit_ramdisk - want 3-4, got 0";
2213    my $iram       = shift || fatal_error "Too few arguments to edit_ramdisk - want 3-4, got 1";
2214    my $oram       = shift || fatal_error "Too few arguments to edit_ramdisk - want 3-4, got 2";
2215    my $message    = shift;
2216    my $tempram    = "$TempDir/editram.rd";
2217    my $mountpoint = "$TempDir/editram.$$";
2218    my $reftype    = ref $sub;
2219
2220    fatal_error "You didn't give me a ref for arg 0 (you passed $sub; I want a CODE ref)"
2221      unless defined $reftype;
2222    fatal_error "You gave me a $reftype ref, but I want a CODE ref"
2223      unless $reftype =~ /CODE/i;
2224
2225    if (defined $message) {
2226    print $message;
2227    print "\n" if called_by_mondo;
2228    }
2229
2230    system "gzip -dc $iram > $tempram"
2231      and fatal_error "Can't unzip $iram to $tempram: exit $?";
2232
2233    mkdir_p $mountpoint;
2234    system "$MountCmd -t ext2 -o loop $tempram $mountpoint"
2235      and fatal_error "Can't mount $tempram on $mountpoint: exit $?";
2236
2237    push @Mountpoints, $mountpoint;
2238    chdir  $mountpoint;
2239    $sub->($mountpoint);
2240
2241    chdir "/";
2242    system "$UnmountCmd $mountpoint"
2243      and logit "Warning! Can't umount $tempram (mounted on $mountpoint): exit $?";
2244    pop @Mountpoints;
2245
2246    system "dd if=$tempram 2>>$Logfile | gzip -9 >$oram"
2247      and fatal_error "Can't compress ramdisk: exit $?";
2248
2249    unlink $tempram;
2250
2251    if (defined $message) {
2252    if (called_by_mondo) {
2253        print "Done.\n";
2254    } else {
2255        print " done.\n";
2256    }
2257    }
2258}
2259
2260############################################################
2261# prepare_boot_disks($) - make the boot disk(s)            #
2262#                         $_[0] - ramdisk size             #
2263#                         $_[1] - disk size (1722 or 2880) #
2264############################################################
2265sub prepare_boot_disks {
2266    my $ramsize  = shift || fatal_error "Too few arguments to prepare_boot_disks() - want 2, got 0";
2267    my $disksize = shift || fatal_error "Too few arguments to prepare_boot_disks() - want 2, got 1";
2268    my ($disk, $mountpoint, $distro);
2269    fatal_error "$ramsize is not an integer, so it can't be a ramsize"
2270      if $ramsize !~ /^[-+]?[0-9]+$/;
2271
2272    if ($disksize == 2880) {
2273    make_ramdisk "$Homes{'mindi'}/rootfs", "$TempDir/mindi-2880.rdz", $ramsize;
2274    } elsif ($disksize == 1722) {
2275    edit_ramdisk sub {
2276        my $kver;
2277        our $ramdir = $_[0];
2278        (undef, undef, $kver) = uname();
2279
2280        chdir $_[0];
2281        unlink "sbin/devfsd";
2282        unlink "tmp/2880.siz";
2283        write_one_liner "tmp/1722.siz", "1722";
2284
2285        foreach my $module (@CDMods) {
2286        unlink "$module.$ModuleSuffix";
2287        }
2288
2289        if ($KernelSucks) {
2290        chdir "$TempDir";
2291        $kver = $FailsafeKernelVersion
2292        } else {
2293        chdir "/";
2294        }
2295
2296        foreach my $module (@FloppyMods) {
2297        find sub {
2298            return unless /$module/;
2299            copy $_, $ramdir."/$module.$ModuleSuffix";
2300            if (/\.gz/) {
2301            move "$ramdir/$module.$ModuleSuffix", "$ramdir/$module.$ModuleSuffix.gz";
2302            system "gunzip $ramdir/$module.$ModuleSuffix.gz";
2303            }
2304        }, "lib/modules/$kver";
2305        }
2306    }, "$TempDir/mindi-2880.rdz", "$TempDir/mindi-1722.rdz", "Modifying the ramdisk for a 1722k floppy...";
2307    } else {
2308    fatal_error "Bad size $disksize - want 1722 or 2880";
2309    }
2310
2311    print "Creating ${disksize}k boot disk...";
2312    print "\n" if called_by_mondo;
2313
2314    $disk       = "$ImagesDir/mindi-boot.$disksize.img";
2315    $mountpoint = "$TempDir/mountpoint.$$";
2316
2317    if ($UseLILO) {
2318    make_zfile $disksize, $disk;
2319    create_tiny_filesystem $disk, 26;              # 26 inodes
2320    } elsif ($disksize == 2880) {
2321    make_zfile $disksize, $disk;
2322    system "mkfs.vfat $disk >>$Logfile 2>>$Logfile";
2323    system "syslinux $disk" and fatal_error "Can't syslinux $disk: exit $?";
2324    } else {
2325    system "gunzip -dc $Homes{'mindi'}/sys-disk.raw.gz > $disk";
2326    }
2327
2328    mkdir_p $mountpoint;
2329   
2330    my($fstype) = $UseLILO ? "ext2" : "msdos";
2331
2332    system "$MountCmd -t $fstype -o loop $disk $mountpoint"
2333      and fatal_error "Can't mount $disk on $mountpoint: exit $?";
2334    push @Mountpoints, $mountpoint;
2335
2336    mkdir "$mountpoint/etc";
2337    untar_files "$Homes{'mindi'}/dev.tgz", "$mountpoint/" or fatal_error "Can't untar $Homes{'mindi'}/dev.tgz" if $UseLILO;
2338
2339    if ($UseLILO) {
2340    if (not system "losetup /dev/loop0 >/dev/null 2>/dev/null") {
2341        if (system "losetup /dev/loop0 -d") {
2342        fatal_error "/dev/loop0 is being used, and Mindi couldn't free it. Please unmount\n/dev/loop0, deconfigure it (losetup /dev/loop0 -d), and re-run Mindi. If all else fails, reboot.";
2343        }
2344    }
2345   
2346    my ($sectors, $cylinders);
2347
2348    if ($disksize == 2880) {
2349        $sectors = 36;
2350        $cylinders = 80;
2351    } else {
2352        $sectors = 21;
2353        $cylinders = 82;
2354    }
2355
2356    open LILOCONF, "> $mountpoint/etc/lilo.conf" or fatal_error "Can't write to $mountpoint/etc/lilo.conf: $!";
2357    print LILOCONF <<EOF;
2358boot=/dev/loop0
2359disk=/dev/loop0
2360bios=0x00
2361sectors=$sectors
2362heads=2
2363cylinders=$cylinders
2364install=/boot.b
2365map=/boot.map
2366vga=normal
2367prompt
2368backup=/dev/null
2369message=/message.txt
2370EOF
2371        if ($CDrecovery) {
2372        print LILOCONF "timeout=300\n";        # give ample time to realize their
2373                               # hard drive is about to be a goner
2374        print LILOCONF "default=RESTORE\n";
2375    } elsif (called_by_mondo) {
2376        if (-e "$MondoTemp/start-nfs") {
2377        print LILOCONF "default=iso\n";
2378        } else {
2379        print LILOCONF "default=interactive\n";
2380        }
2381    } else {
2382        print LILOCONF "default=expert\n";
2383    }
2384
2385    my @options;
2386    if ($CDrecovery) {
2387        @options = qw/RESTORE/;
2388    } elsif (called_by_mondo) {
2389        @options = qw/interactive compare iso nuke isonuke/;
2390    }
2391    push @options, "expert";
2392
2393    foreach my $option (@options) {
2394        my $param;
2395        ($param = $option) =~ s/RESTORE/nuke/;
2396        print LILOCONF <<EOF;
2397
2398image=vmlinuz
2399    label=$option
2400    root=/dev/ram0
2401    initrd=/initrd.img
2402    append=" rw ramdisk=$ramsize ramdisk_size=$ramsize ${param}_mode $ExtraBootOptions"
2403EOF
2404        }
2405
2406    close LILOCONF;
2407    copy "/boot/boot.b", "$mountpoint/boot.b" or fatal_error "Can't copy /boot/boot.b to $mountpoint: $!";
2408    } else {
2409    if ($CDrecovery) {
2410        copy "$Homes{'mindi'}/syslinux-H.cfg", "$mountpoint/syslinux.ofg";
2411    } else {
2412        copy "$Homes{'mindi'}/syslinux.cfg", "$mountpoint/syslinux.ofg";
2413    }
2414
2415    open SYSLINUX_READ, "< $mountpoint/syslinux.ofg" or fatal_error "Can't open < $mountpoint/syslinux.ofg: $!";
2416    open SYSLINUX_WRITE, "> $mountpoint/syslinux.cfg"    or fatal_error "Can't open > $mountpoint/syslinux.cfg: $!";
2417
2418    while (<SYSLINUX_READ>) {
2419        chomp;
2420        s/24000/$ramsize/;
2421        if (/append/) {
2422        $_ .= " $ExtraBootOptions";
2423        }
2424        if (-e "$MondoTemp/start-nfs") {
2425        s/interactive/iso/;
2426        }
2427        print SYSLINUX_WRITE "$_\n";
2428    }
2429
2430    close SYSLINUX_READ;
2431    close SYSLINUX_WRITE;
2432       
2433    unlink "$mountpoint/syslinux.ofg";
2434    }
2435
2436    copy "$TempDir/mindi-${disksize}.rdz", "$ImagesDir/";
2437    copy "$TempDir/mindi-${disksize}.rdz", "$mountpoint/initrd.img" or do {
2438    if (($disksize == 2880) && called_by_mondo) {
2439        fatal_error "Can't copy $TempDir/mindi-${disksize}.rdz to ./mindi.rdz: $!\nPlease unload some of your modules and try again.";
2440    }
2441    logit "WARNING - Can't copy $TempDir/mindi-${disksize}.rdz to ./mindi.rdz: $!\nPlease unload some of your modules and try again.\n(This error is non-fatal, BTW).";
2442    return;                        # only if !fatal_error
2443    };
2444
2445    $distro = "";
2446    open ISSUE_NET, "/etc/issue.net" and do {
2447    while (<ISSUE_NET>) {
2448        chomp;
2449        if (/linux/i) {
2450        $distro = $_;
2451        }
2452    }
2453    close ISSUE_NET;
2454    };
2455
2456    open MSG_TXT, "$Homes{'mindi'}/msg-txt"   or fatal_error "Can't open $Homes{'mindi'}/msg-txt: $!";
2457    open MSG_OUT, "> $mountpoint/message.txt" or fatal_error "Can't write to $mountpoint/message.txt: $!";
2458
2459    while (<MSG_TXT>) {
2460    my $kver;
2461    my $date = `date`;
2462    if ($KernelSucks) {
2463        $kver = $FailsafeKernelVersion;
2464    } else {
2465        $kver = `uname -r`;
2466        chomp $kver;
2467    }
2468    chomp $date;
2469
2470    s/ZZZZZ/$Version/;
2471    s/YYYYY/Mondo Rescue/;
2472    s/XXXXX/a cousin of/;
2473    s/DDDDD/$distro/;
2474    s/KKKKK/Kernel $kver/;
2475    s/TTTTT/$date/;
2476    print MSG_OUT $_;
2477    }
2478
2479    close MSG_TXT;
2480
2481    if (called_by_mondo) {
2482    if ($CDrecovery) {
2483        print MSG_OUT <<EOF;
2484To restore your disk to factory defaults, type 'RESTORE' <enter>.
2485CAUTION: THIS WILL ERASE YOUR WHOLE DISK!!!
2486EOF
2487        } elsif (-e "$MondoTemp/start-nfs") {
2488        print MSG_OUT "Press <enter> to continue.\n";
2489    } else {
2490        print MSG_OUT $BootMediaMessage;
2491    }
2492    } else {
2493    print MSG_OUT "FYI, this is _not_ a Mondo Rescue CD.\n";
2494    }
2495
2496    print MSG_OUT "\n\n\n";
2497    close MSG_OUT;
2498
2499    my($need_rootdisk);
2500
2501    loggit "PBD: \$mountpoint is $mountpoint";
2502    copy $Kernel, "$mountpoint/vmlinuz" or do {
2503    if (($disksize == 2880) && called_by_mondo) {
2504        fatal_error "Sorry, your kernel is too big.\nEither recompile it to reduce its size, or use Mindi's failsafe kernel.\nTo use Mindi's failsafe kernel, run Mondo (or Mindi if you're running it standalone) with the -k FAILSAFE switch.";
2505    }                          # if we're here, we're not 2880k w/Mondo
2506    if ($disksize == 2880) {
2507        logit "WARNING - kernel is too big for ${disksize}K floppy";
2508        logit "This error is non-fatal if you are using a Mindi CD (with isolinux)";
2509        system "$UnmountCmd $mountpoint";
2510        pop @Mountpoints;
2511        unlink $disk;
2512        return;
2513    }
2514    # 1722k, didn't fit - make root disk
2515    logit "Kernel is too big for one boot+root floppy";
2516    logit "Therefore, a separate root floppy (1.44MB) will be created.";
2517    unlink "$mountpoint/initrd.img" or fatal_error "Tried to remove ramdisk to copy kernel but the ramdisk vanished!";
2518    copy $Kernel, "$mountpoint/vmlinuz" or do {
2519        print "Kernel doesn't even fit when it's alone! Too big. Goodbye.";
2520        system "$UnmountCmd $mountpoint";
2521        pop @Mountpoints;
2522        unlink $disk;
2523        return;
2524    };
2525    system "rdev -R $Kernel 0"     and fatal_error "Couldn't set kernel to mount rootfs in r/w mode!";
2526    system "rdev -r $Kernel 49152" and fatal_error "Couldn't set kernel to boot from root floppy!";
2527
2528    $need_rootdisk = 1;
2529    };
2530
2531    if ($UseLILO) {
2532    system "lilo -r $mountpoint >>$Logfile 2>>$Logfile" and fatal_error "Error running LILO.";
2533    }
2534
2535    chdir "/";
2536    system "$UnmountCmd $mountpoint"
2537      and fatal_error "Can't umount $disk from $mountpoint: exit $?";
2538    pop @Mountpoints;
2539    if ($ImagesDir ne "/root/images/mindi") {
2540    copy $disk, "/root/images/mindi/";
2541    }
2542
2543    if (called_by_mondo) {
2544    print "Done.\n";
2545    } else {
2546    print " done.\n";
2547    }
2548
2549    if ($need_rootdisk) {
2550    print "Making root disk... ";
2551    copy "$TempDir/mindi-1722.rdz", "$ImagesDir/mindi-root.1440.img";
2552    system "dd if=/dev/zero bs=1 count=" . 1024 - ((-s "$ImagesDir/mindi-root.1440.img") % 1024) .
2553      " >> $ImagesDir/mindi-root.1440.img";
2554    system "dd if=/dev/zero bs=1k count=" . 1440 - ((-s "$ImagesDir/mindi-root.1440.img") / 1024) .
2555      " >> $ImagesDir/mindi-root.1440.img";
2556    if ((-s "$ImagesDir/mindi-root.1440.img") != (1440 * 1024)) {
2557        fatal_error "Root disk is not exactly 1.44 MB!";
2558    }
2559    if ($ImagesDir ne "/root/images/mindi") {
2560        copy "$ImagesDir/mindi-root.1440.img", "/root/images/mindi/";
2561    }
2562    print " done.\n";
2563    }   
2564}
2565
2566############################################################
2567# make_floppy($$$) - copy an image to a real FD disk       #
2568#                    $_[0] - the image                     #
2569#                    $_[1] - the floppy drive              #
2570#                    $_[2] - the description               #
2571############################################################
2572sub make_floppy {
2573    my $image   = shift || fatal_error "Too few arguments to make_floppy: want 3, got 0";
2574    my $fd      = shift || fatal_error "Too few arguments to make_floppy: want 3, got 1";
2575    my $desc    = shift || fatal_error "Too few arguments to make_floppy: want 3, got 2";
2576    my $bufsize = shift || (-s $image) / 40;
2577    my $buf;
2578    my $read    = 0;
2579    my $imgsize = -s $image;
2580    my ($infile, $outfloppy);
2581
2582    print "Please insert a blank or expendable floppy for $desc.\n";
2583    print "Press ENTER when ready, or type QUIT to abort: ";
2584    my $answer = <STDIN>;
2585    return if $answer =~ /q/i;
2586
2587    print "Formatting $desc.\n";
2588
2589    system "fdformat -y $fd"
2590      and fatal_error "Could not format $fd - please check above messages.";
2591
2592    open $infile,    "< $image" or fatal_error "Can't open $image for reading: $!";
2593    open $outfloppy, "> $fd"    or logit "Skipping $desc: Can't open $fd for writing: $!", return;
2594
2595    autoflush $outfloppy 1;
2596
2597    print "Writing $desc...\n" if called_by_mondo;
2598
2599    while (sysread $infile, $buf, $bufsize) {
2600    syswrite $outfloppy, $buf;
2601    $read += $bufsize;
2602    $read = $imgsize if $read > $imgsize;
2603    print_progress $read, $imgsize, "Writing $desc:\t\t\t\t" unless called_by_mondo;
2604    }
2605
2606    close $infile;
2607    close $outfloppy;
2608}
2609
2610############################################################
2611# copy_images_to_floppies($) - self-explanatory            #
2612#                              $_[0] - number of disks     #
2613############################################################
2614sub copy_images_to_floppies {
2615    my $noof_disks = shift || fatal_error "Too few arguments to copy_images_to_floppies() - want 1, got 0";
2616    my $cur_disk;
2617
2618    print "WARNING: THIS WILL ERASE YOUR FLOPPIES!\n";
2619
2620    make_floppy "$ImagesDir/mindi-boot.1722.img", "/dev/fd0u1722", "boot floppy";
2621    make_floppy "$ImagesDir/mindi-root.1440.img", "/dev/fd0", "root floppy" if -e "$ImagesDir/mindi-root.1440.img";
2622
2623    for ($cur_disk = 1; $cur_disk <= $noof_disks; ++$cur_disk) {
2624    make_floppy "$ImagesDir/mindi-data-$cur_disk.img", "/dev/fd0", "data disk #$cur_disk";
2625    }
2626}
2627
2628
2629############################################################
2630# make_bootable_cd() - make a bootable ISO image           #
2631############################################################
2632sub make_bootable_cd {
2633    my $stage = "$TempDir/iso";
2634
2635    logit "Creating bootable CD...";
2636
2637    mkdir_p "$stage/images", "$stage/archives", "$stage/isolinux";
2638    for my $imagefile (<$ImagesDir/*.img>, <$ImagesDir/*.gz>) {
2639    copy $imagefile, "$stage/images/" or fatal_error "Can't copy $imagefile to $stage/images/: $!";
2640    }
2641    copy "$Homes{'mondo'}/autorun", "$stage/";
2642    chmod 0755, "$stage/autorun";
2643
2644    my $distro = "";
2645    open MSG_TXT, "$Homes{'mindi'}/msg-txt"       or fatal_error "Can't open $Homes{'mindi'}/msg-txt: $!";
2646    open MSG_OUT, "> $stage/isolinux/message.txt" or fatal_error "Can't write to $stage/isolinux/message.txt: $!";
2647
2648    while (<MSG_TXT>) {
2649    my $kver;
2650    my $date = `date`;
2651    if ($KernelSucks) {
2652        $kver = $FailsafeKernelVersion;
2653    } else {
2654        $kver = `uname -r`;
2655        chomp $kver;
2656    }
2657    chomp $date;
2658
2659    s/ZZZZZ/$Version/;
2660    s/YYYYY/Mondo Rescue/;
2661    s/XXXXX/a cousin of/;
2662    s/DDDDD/$distro/;
2663    s/KKKKK/Kernel $kver/;
2664    s/TTTTT/$date/;
2665    print MSG_OUT $_;
2666    }
2667
2668    close MSG_TXT;
2669
2670    if (called_by_mondo) {
2671    if ($CDrecovery) {
2672        print MSG_OUT <<EOF;
2673To restore your disk to factory defaults, type 'RESTORE' <enter>.
2674CAUTION: THIS WILL ERASE YOUR WHOLE DISK!!!
2675EOF
2676        } elsif (-e "$MondoTemp/start-nfs") {
2677        print MSG_OUT "Press <enter> to continue.\n";
2678    } else {
2679        print MSG_OUT $BootMediaMessage;
2680    }
2681    } else {
2682    print MSG_OUT "FYI, this is _not_ a Mondo Rescue CD.\n";
2683    }
2684    print MSG_OUT "\n\n\n";
2685    close MSG_OUT;
2686
2687    if ($CDrecovery) {
2688    copy "$Homes{'mindi'}/isolinux-H.cfg", "$stage/isolinux/isolinux.cfg.old";
2689    } else {
2690    copy "$Homes{'mindi'}/isolinux.cfg", "$stage/isolinux/isolinux.cfg.old";
2691    }
2692
2693    open SYSLINUX_READ, "< $stage/isolinux/isolinux.cfg.old" or fatal_error "Can't open < $stage/isolinux/isolinux.cfg.old: $!";
2694    open SYSLINUX_WRITE, "> $stage/isolinux/isolinux.cfg"    or fatal_error "Can't open > $stage/isolinux/isolinux.cfg: $!";
2695
2696    while (<SYSLINUX_READ>) {
2697    chomp;
2698    s/24000/$RamdiskSize/;
2699    if (/append/) {
2700        $_ .= " $ExtraBootOptions";
2701    }
2702    if (-e "$MondoTemp/start-nfs") {
2703        s/interactive/iso/;
2704    }
2705    print SYSLINUX_WRITE "$_\n";
2706    }
2707
2708    close SYSLINUX_READ;
2709    close SYSLINUX_WRITE;
2710
2711    copy $Kernel, "$stage/isolinux/vmlinuz" or fatal_error "Can't copy kernel to bootable ISO: $!";
2712    copy "$TempDir/mindi-2880.rdz", "$stage/isolinux/initrd.img" or fatal_error "Can't copy ramdisk to bootable ISO: $!";
2713    copy $IsolinuxPath, "$stage/isolinux/isolinux.bin" or fatal_error "Can't copy isolinux to bootable ISO: $!";
2714
2715    copy "$stage/isolinux/initrd.img",   "$ImagesDir/../" if called_by_mondo;
2716    copy "$stage/isolinux/isolinux.bin", "$ImagesDir/../" if called_by_mondo;
2717    copy "$stage/isolinux/isolinux.cfg", "$ImagesDir/../" if called_by_mondo;
2718    copy "$stage/isolinux/message.txt",  "$ImagesDir/../" if called_by_mondo;
2719    copy "$stage/isolinux/vmlinuz",      "$ImagesDir/../" if called_by_mondo;
2720
2721    chdir $stage;
2722    system "mkisofs -U -J -r -o $ImagesDir/mindi.iso -b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table . >/dev/null 2>$TempDir/mkisofs.out";
2723    if ($?) {
2724    logit "------------------- mkisofs's errors -----------------------";
2725    open ERRS, "$TempDir/mkisofs.out" or fatal_error "Can't open mkisofs errors file.";
2726    while (<ERRS>) {
2727        chomp;
2728        logit $_;
2729    }
2730    close ERRS;
2731    logit "------------------------------------------------------------";
2732    logit "Failed to create ISO image.";
2733    }
2734    else {
2735    logit "Created bootable ISO image at $ImagesDir/mindi.iso.";
2736    if ($ImagesDir ne "/root/images/mindi") {
2737        copy "$ImagesDir/mindi.iso", "/root/images/mindi/mindi.iso";
2738        logit "... copied to /root/images/mindi/mindi.iso.";
2739    }
2740    }
2741    unlink "$TempDir/mkisofs.out";
2742}
2743
2744############################################################
2745# find_kernel() - try and find the user's kernel           #
2746#       returns - kernel found, or <undef> if none         #
2747############################################################
2748sub find_kernel {
2749    my (@kernels, @duff_kernels);
2750    my ($kver, $kdate);
2751
2752    (undef, undef, $kver, $kdate) = uname();
2753
2754  KERNEL:
2755    foreach my $kernel (glob ("/boot/vmlinuz*"), glob ("/boot/*zImage*"),
2756            glob ("/boot/*kernel*"), glob ("/vmlinuz*"),
2757            glob ("/*zImage*"),      glob ("/*kernel*")) {
2758    next KERNEL unless defined $kernel;
2759    next KERNEL unless -e $kernel;             # skip nonexistent kernels
2760    next KERNEL if -l $kernel;             # skip symlinks
2761    push (@duff_kernels, $kernel), next KERNEL
2762      if $kernel =~ /shipped/;             # use SuSE's extra kernel as a last resort
2763    next KERNEL if system "strings $kernel | grep -qF '$kver'"; # skip kernels ! this version
2764    unless (system "strings $kernel | grep -qF '$kdate'") {
2765        # we found it!
2766        foreach my $existing_kernel (@kernels) {
2767        next KERNEL if $existing_kernel eq $kernel;   # but it's the same one as before :(
2768        }
2769        push @kernels, $kernel;
2770    } else {
2771        # Debian/Gentoo builddate problems, anyone?
2772        foreach my $existing_kernel (@duff_kernels) {
2773        next KERNEL if $existing_kernel eq $kernel;   # already in there
2774        }
2775        push @duff_kernels, $kernel;
2776    }
2777    }
2778
2779    @kernels      = sort @kernels;
2780    @duff_kernels = sort @duff_kernels;
2781
2782    if (scalar (@kernels) == 0) {
2783    logit "Sorry, couldn't find your kernel.";
2784    logit "Are there any duff kernels?";
2785    if (scalar @duff_kernels) {
2786        logit "Lucky you -- found a duff kernel.";
2787        @kernels = @duff_kernels;
2788    } else {
2789        logit "Nope, no duff kernels either.";
2790        return;
2791    }
2792    }
2793    if (scalar (@kernels) == 1) {
2794    loggit "Found one kernel -- $kernels[0] (v$kver)";
2795    return $kernels[0];
2796    } else {
2797    logit "I have found more than one suitable kernel: @kernels";
2798    foreach my $kernel (@kernels) {
2799        if ($kernel =~ /$kver/) {
2800        logit "I'm picking $kernel.";
2801        return $kernel;
2802        }
2803    }
2804    logit "Still don't know which one to pick... I'll return the first one.";
2805    return $kernels[0];
2806    }
2807}
2808
2809############################################################
2810# find_isolinux_path -- self-explanatory                   #
2811#           returns  -- nothing; sets -- $IsolinuxPath     #
2812############################################################
2813sub find_isolinux_path {
2814    if    (-e "/usr/lib/isolinux.bin")                { $IsolinuxPath = "/usr/lib/isolinux.bin" }
2815    elsif (-e "/usr/lib/syslinux/isolinux.bin")       { $IsolinuxPath = "/usr/lib/syslinux/isolinux.bin" }
2816    elsif (-e "/usr/share/syslinux/isolinux.bin")     { $IsolinuxPath = "/usr/share/syslinux/isolinux.bin" }
2817    elsif (-e "/usr/share/lib/syslinux/isolinux.bin") { $IsolinuxPath = "/usr/share/lib/syslinux/isolinux.bin" }
2818    else {
2819    open LOCATE, "locate isolinux.bin |" or fatal_error "Can't find isolinux, and locate isn't helping...";
2820    while (<LOCATE>) {
2821        chomp;
2822        if (m[/.*/isolinux\.bin$ ]x) {         # use /x 'cause the $] at the end would be $PERL_VERSION
2823        $IsolinuxPath = $_;            # otherwise... [I think :-]
2824        }
2825    }
2826
2827    unless ($IsolinuxPath) {
2828        fatal_error "Can't find isolinux. Please make sure you have the right syslinux RPM installed.\nUse the one at mondorescue.org if you're unsure -- that one contains isolinux.";
2829    }
2830    }
2831}
2832
2833############################################################
2834# get_kmods() - return a list of all loaded kernel modules #
2835############################################################
2836sub get_kmods {
2837    my @kmods;
2838
2839    open LSMOD, "lsmod |" or fatal_error "Can't open pipe from lsmod: $!";
2840    while (<LSMOD>) {
2841    my @line;
2842
2843    next if /Module/;                  # skip header line
2844    chomp;
2845    @line = split;
2846    push @kmods, $line[0];
2847    }
2848    close LSMOD;
2849
2850    return @kmods;
2851}
2852
2853############################################################
2854# get_kmod_paths() - return paths to all loaded kmods      #
2855############################################################
2856sub get_kmod_paths {
2857    my @kmod_paths;
2858    my $kver;
2859
2860    foreach my $module (@ExtraMods, get_kmods()) {
2861    (undef, undef, $kver) = uname();
2862    find sub {
2863        push @kmod_paths, $File::Find::name if -f && /$module\.$ModuleSuffix/;
2864    }, "/lib/modules/$kver";
2865    }
2866
2867    find sub { push @kmod_paths, $File::Find::name }, glob "/lib/modules/$kver/modules.*";
2868
2869    return @kmod_paths;
2870}
2871
2872############################################################
2873# make_paths_failsafe() - transform /lib/modules/* into    #
2874#                           the failsafe kernel's modules  #
2875#                    @_ - paths to transform               #
2876#               returns - transformed paths                #
2877############################################################
2878sub make_paths_failsafe {
2879    my @failsafe_mods;
2880    my $kver;
2881    (undef, undef, $kver) = uname();
2882
2883    chdir $TempDir;
2884    foreach my $module (@_) {
2885    foreach my $newmod (find_specific_kmod_in_path ("lib/modules/$FailsafeKernelVersion",
2886                            basename ($module, "NoExtension"))) {
2887        push @failsafe_mods, $newmod;
2888    }
2889    }
2890
2891    return @failsafe_mods;
2892}
2893
2894############################################################
2895# find_specific_kmod_in_path($$)                           #
2896#                        $_[0] - the path                  #
2897#                        $_[1] - the module name           #
2898############################################################
2899sub find_specific_kmod_in_path {
2900    my @matches;
2901    my $path   = shift || fatal_error "Too few arguments to find_specific_kmod_in_path() - want 2, got 0";
2902    my $module = shift || fatal_error "Too few arguments to find_specific_kmod_in_path() - want 2, got 1";
2903
2904    find sub {
2905    /$module/ && push @matches, $File::Find::name;
2906    }, $path;
2907
2908    return @matches;
2909}
2910
2911############################################################
2912#-------------------------- main --------------------------#
2913############################################################
2914
2915$| = 1;
2916my $want_help = 0;
2917my ($tmp_imgdevs, $tmp_exdevs);
2918my $boolval;
2919my $custom = 0;
2920my $force_pid = 0;
2921
2922if ($< && $>) {
2923    die "You are not root. Please su.\n";
2924}
2925
2926&check_system;
2927&find_homes;
2928
2929(undef, undef, $KernelVersion) = uname();
2930loggit "Running kernel version = $KernelVersion";
2931if ($KernelVersion =~ /2.6/) {
2932    logit "Warning: 2.6 kernel detected. Support is untested. Continue at your own risk.";
2933   
2934    $ModuleSuffix = "ko";
2935    foreach my $symlink (keys %RootfsSymlinks) {
2936    if ($symlink =~ m[/sbin/(modprobe|insmod|rmmod|lsmod|depmod|kallsyms)]) {
2937        loggit "Removing $symlink symlink: we will use the non-Busybox version";
2938        delete $RootfsSymlinks{$symlink};
2939    }
2940    }
2941}
2942
2943if (!system "which lvmdiskscan") {
2944    open LVMVER, "lvmdiskscan --version |" and do {
2945    while (<LVMVER>) { chomp;
2946        if (/LVM version:/) {
2947        s/.*LVM version:\s*//;
2948        $LVMVersionString = $_;
2949        s/([0-9]).*/$1/;
2950        $LVMVersion = $_;
2951        }
2952    }
2953    close LVMVER;
2954    }
2955}
2956   
2957GetOptions ("help|h|?"        => \$want_help,
2958        "extra-space|x=i" => \$ExtraSpace,
2959        "max-csize|C=i"   => \$MaxDiskSize,
2960        "temp-dir|t=s"    => \$TempDir,
2961        "make-floppies!"  => \$Floppies,
2962        "f"               => \$Floppies,
2963        "prompt-cd!"      => \$PromptCD,
2964        "c"               => \$PromptCD,
2965        "kernel|k=s"      => \$Kernel,
2966        "logfile|log|l=s" => \$Logfile,
2967        "lilo|use-lilo|L" => \$UseLILO,
2968        "image-dir|i=s"   => \$ImagesDir,
2969        "mondo|m"         => sub { $called_by_mondo = 10 },
2970        # 10 is the sentinel value that shows "set on cmd line"
2971        "nomondo|M"       => sub { $called_by_mondo = 0 },
2972        "option|o=s"      => sub {
2973                                       $_ = $_[1];
2974                       fatal_error "Bad -o option (no = sign): $_"
2975                       unless /=/;
2976                       my @opts = split /,/;
2977                       eval "\$$_" for @opts;
2978                     },
2979        "eval=s"          => sub { eval $_[1] },
2980        "print|I=s"       => sub { # -I for (I)nspect
2981                               $_ = $_[1];
2982                       my @opts = split /,/;
2983                       eval "print '$_ = ', \$$_, \"\\n\"" for @opts;
2984                     },
2985        "do-nothing|n"    => sub { exit 0 },
2986        "makemountlist=s" => sub { make_mountlist $_[1]; exit 0 },
2987        "max-fsize|F=i"   => \$ChopAt,
2988        "chop-size|X=i"   => \$ChopSize,
2989        "pid-file|p=s"    => \$PIDfile,
2990        "force-pid|P"     => \$force_pid,
2991        "log-verbose|v"   => \$LogVerbose,
2992        "version|V"       => sub { print "mindi.pl v$Version\n"; exit 0 },
2993        "findkernel|K"    => sub {
2994                               my @kernels = find_kernel;
2995                       if (scalar @kernels)
2996                     { print "@kernels\n"; exit 0 }
2997                       else
2998                     { exit 1 }
2999                     },
3000        "test-error"      => sub { fatal_error "--$_[0] given" },
3001        "custom"          => sub { $custom = 1; die "!FINISH" },
3002        "only-2880|2|8"   => sub { $Make1722kFloppy = 0 })
3003  or usage();
3004
3005usage() if $want_help;
3006
3007unlink $Logfile;
3008
3009if ($custom) {
3010    unshift @ARGV, "--custom";
3011    loggit "CUSTOM: ", join (" ", @ARGV);
3012    if ((scalar @ARGV) < 15) {
3013    usage();
3014    }
3015
3016    $boolval = sub { # $_[0] - the word, $_[1] - default value
3017    return 0     unless defined $_[0];
3018    return 1     if ($_[0] =~ /y/i);
3019    return 1     if ($_[0] =~ /nonbootable/i); # a special case of the next one
3020    return $_[1] if ($_[0] =~ /null/i); # no value, so default
3021    return 0     if ($_[0] =~ /n/i);
3022    return $_[1];
3023    };
3024
3025    $TempDir          =            $ARGV[1];
3026    $MondoTemp        =            $ARGV[1];
3027    $ImagesDir        =            $ARGV[2];
3028    $Kernel           =            $ARGV[3];
3029    $TapeDevice       =            $ARGV[4];
3030    $TapeSize         =            $ARGV[5];
3031    $FilesInFilelist  =            $ARGV[6];
3032    $UsingLZO         = $boolval->($ARGV[7],  0);
3033    $CDrecovery       = $boolval->($ARGV[8],  0);
3034    $tmp_imgdevs      =            $ARGV[9];
3035    $LastFilelistNum  =            $ARGV[11];
3036    $EstNoofSlices    =            $ARGV[12];
3037    $tmp_exdevs       =            $ARGV[13];
3038    $UsingCompression = $boolval->($ARGV[14], 1);
3039    $NonBootable      = $boolval->($ARGV[16], 0);
3040    $DifferentialBkup =            $ARGV[17];
3041
3042    $tmp_imgdevs =~ s/\(null\)//;
3043    $tmp_exdevs  =~ s/\(null\)//;
3044    $tmp_imgdevs =~ y/|,/  /;
3045    $tmp_exdevs  =~ y/|,/  /;
3046    @ImageDevs   = split ' ', $tmp_imgdevs;
3047    @ExcludeDevs = split ' ', $tmp_exdevs;
3048}
3049
3050our $OldTemp = $TempDir;
3051
3052unlink (<$ImagesDir/*gz>, <$ImagesDir/*img>);
3053
3054loggit "Mindi v$Version";
3055loggit "-----------------------";
3056loggit `uname -a`;
3057loggit "-----------------------";
3058
3059logit "Mindi-Linux mini-distro generator v$Version by Hugo Rabson";
3060logit "Port to Perl done by Joshua Oreman";
3061logit "Mindi uses Busybox, which is available from www.busybox.net";
3062logit '-' x (($ENV{'COLUMNS'} || $ENV{'COLS'} || 80) - 1);
3063system "rm -rf $TempDir/mindi";
3064$TempDir .= "/mindi/$$";
3065mkdir_p $TempDir, $ImagesDir;
3066if (-e $PIDfile) {
3067    if ($force_pid) {
3068    logit "--> Killing Mindi PID @{[ `cat $PIDfile | tr -d '\n'` ]}";
3069    kill 15, `cat $PIDfile | tr -d '\n'`;
3070    unlink $PIDfile;
3071    } else {
3072    logit "Another instance of Mindi, pid @{[ `cat $PIDfile | tr -d '\n'` ]}, is already running.";
3073    logit "If you want to kill this process, use the -P option, or remove /var/run/mindi.pid.\n";
3074    exit 1;
3075    }
3076}
3077open (PID, "> $PIDfile") or die "Can't write to $PIDfile: $!\n";
3078print PID $$, "\n";
3079close PID;
3080
3081if ($NonBootable) {
3082    logit "Just creating mondo-restore.cfg and a small all.tar.gz for Mondo. Nothing else.";
3083    make_mondo_config "$MondoTemp/mondo-restore.cfg";
3084    mkdir_p "$MondoTemp/small-all/tmp";
3085    chdir "$MondoTemp/small-all";
3086    for my $file ("mondo-restore.cfg", "filelist.full", "biggielist.txt") {
3087    copy "$MondoTemp/$file", "./tmp/$file" or fatal_error "Can't copy $file to the small all.tar.gz: $!";
3088    }
3089    tar_up_files ("$MondoTemp/all.tar.gz", "tmp/");
3090    logit "Done copying all.tar.gz - exiting.";
3091    unlink $PIDfile;
3092    exit 0;
3093}
3094
3095$SIG{'HUP'}  = sub { fatal_error "Hangup" };
3096$SIG{'INT'}  = sub { fatal_error "Interrupt" };
3097$SIG{'TERM'} = sub { fatal_error "Terminated" };
3098
3099if ((not $KernelSucks) && ($Kernel !~ /failsafe/i) && ($Kernel !~ /mindi/) && ($Kernel !~ /sucks/i)) {
3100    if ($Kernel) {
3101    if (not -e $Kernel) {
3102        logit "Specified kernel does not exist. I will use your running kernel.";
3103        $Kernel = "";
3104    } else {
3105        if (open KERNSTRINGS, "strings '$Kernel' |") {
3106        while (<KERNSTRINGS>) {
3107            if (/^[2-9].[0-9].[0-9]/) {
3108            my(@line) = split;
3109            $KernelVersion = $line[0];
3110            }
3111        }
3112
3113        unless ($KernelVersion) {
3114            logit "Specified kernel contains no version information. I will use your running kernel.";
3115            $Kernel = "";
3116        }
3117
3118        close KERNSTRINGS;
3119        } else {
3120        logit "Specified kernel could not be opened. I will use your running kernel.";
3121        $Kernel = "";
3122        }
3123    }
3124    }
3125
3126    unless ($Kernel) {
3127    my @kernels = find_kernel;
3128    if (scalar (@kernels) == 0) {
3129        logit "Could not find your kernel. Using mine.";
3130        $Kernel = "$Homes{'mindi'}/vmlinuz";
3131        $KernelSucks = 1;
3132    } else {
3133        $Kernel = $kernels[0];
3134        $KernelSucks = 0;
3135    }
3136    }
3137} else {
3138    logit "Using my kernel 'cause you told me to.";
3139    $Kernel = "$Homes{'mindi'}/vmlinuz";
3140    $KernelSucks = 1;
3141}
3142
3143if ($KernelSucks) {
3144    unless (-f $Kernel) {
3145    fatal_error "Can't find the failsafe kernel. Get the mindi-kernel package from\nhttp://www.mondorescue.org and install it -- you need it.";
3146    }
3147    untar_files "$Homes{'mindi'}/lib.tar.bz2", "$TempDir", "Extracting kernel modules:\t\t", 0
3148      or fatal_error "Can't unzip lib.tar.bz2 -- do you need to install mindi-kernel?";
3149    open STRINGS, "strings $Kernel |" or fatal_error "Can't open pipe from strings: $!";
3150    while (<STRINGS>) {
3151    chomp;
3152    if (/^2\.4/) {
3153        ($FailsafeKernelVersion) = split;
3154    }
3155    }
3156    close STRINGS;
3157}
3158
3159if ($called_by_mondo == -1) {
3160    &determine_if_called_by_mondo;
3161}
3162
3163if ($custom == 1 and $called_by_mondo == 0) {
3164    logit "WARNING! You are not Mondo, but you called me with --custom arguments.";
3165    logit "This is untested behavior.";
3166    print "Continue anyway (y/n)? [n] ";
3167    my $answer = <STDIN>;
3168    chomp $answer;
3169    if ($answer !~ /y/i) {
3170    fatal_error "Aborting at user's request.";
3171    }
3172    logit "--> OK, continuing.";
3173}
3174elsif ($custom == 0 and $called_by_mondo) {
3175    logit "I did not get a --custom argument, but I was seemingly called_by_mondo.";
3176    if ($called_by_mondo == 10) {
3177    logit "Since I was requested to pretend I was called by Mondo on the command line,";
3178    logit "I shall continue this way.";
3179    }
3180    else {
3181    logit "Since this was not due to a command line option, I will assume that you are";
3182    logit "running me at the same time as another Mondo process is running: the two are";
3183    logit "not related. Therefore, I shall unset called_by_mondo.";
3184    $called_by_mondo = 0;
3185    }
3186}
3187
3188&generate_depends_list;
3189my $noof_disks = &make_data_disks;
3190&prepare_boot_disks (($ToolSize + $ExtraSpace) - (($ToolSize + $ExtraSpace) % 4096), 2880);
3191&prepare_boot_disks (($ToolSize + $ExtraSpace) - (($ToolSize + $ExtraSpace) % 4096), 1722) if $Make1722kFloppy;
3192
3193logit "\nYour disks may be found in $ImagesDir:-";
3194system "ls -C $ImagesDir | tee -a $Logfile";
3195print "\n";
3196
3197if (called_by_mondo && $TapeDevice) {
3198    copy "$ImagesDir/all.tar.gz $MondoTemp/all.tar.gz"
3199      or fatal_error "Can't copy $ImagesDir/all.tar.gz to $MondoTemp/all.tar.gz: $!";
3200}
3201
3202if ($Floppies && not called_by_mondo) {
3203    my $answer;
3204    print "Copy the images to floppies (y/n)? [n] ";
3205    $answer = <STDIN>;
3206    chomp $answer;
3207    &copy_images_to_floppies ($noof_disks) if $answer =~ /y/i;
3208}
3209
3210my $mkcd = 1;
3211if ($PromptCD and not called_by_mondo) {
3212    my $ans;
3213    print "Make a bootable ISO image (y/n)? [y] ";
3214    $ans = <STDIN>;
3215    chomp $ans;
3216    if ($ans =~ /n/i) {
3217    $mkcd = 0;
3218    } else {
3219    $mkcd = 1;
3220    }
3221}
3222
3223&make_bootable_cd if $mkcd;
3224
3225sleep 1;
3226print "\nFinished.\n";
3227sleep 1;
3228print "\n";
3229print "One 1722K boot disk, " if -e "$ImagesDir/mindi-boot.1722.img";
3230print "one 1440K root disk, " if -e "$ImagesDir/mindi-root.1440.img";
3231print "one 2880K boot disk, " if -e "$ImagesDir/mindi-boot.2880.img";
3232print "\n";
3233print "and $noof_disks data disk images were created.\n";
3234
3235print "\n";
3236
3237chdir "/";
3238foreach my $mountpoint (@Mountpoints) {
3239    logit "Unmounting $mountpoint.";
3240    system "$UnmountCmd $mountpoint" and logit "WARNING: Could not unmount $mountpoint, please unmount it yourself";
3241}
3242
3243if ($TempDir !~ /$$/i) {
3244    logit "BAD TEMP DIR: $TempDir!";
3245    logit "If I had removed this tempdir, you would not be happy right now!";
3246    logit "Exiting.";
3247    unlink $PIDfile;
3248    exit 1;
3249}
3250
3251system "rm", '-rf', $TempDir and warn "WARNING: could not remove the temp directory\n";
3252
3253if ($OldTemp =~ m[/tmp/mindi.[A-Za-z0-9]{10}.*]) {
3254    system "rm", '-rf', $OldTemp;
3255}
3256
3257unlink $PIDfile;
3258exit 0;
3259
3260__END__
3261
Note: See TracBrowser for help on using the repository browser.