source: MondoRescue/trunk/mindi/mindi.pl

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