#!/usr/bin/perl # # This program takes an Anvil! ISO and writes it to a USB drive suitable for a USB-based install of a Striker # dashboard machines. From there, other dashboards and Anvil! nodes can be installed via PXE booting the # first Striker dashboard using the 'Install Target' feature. # # (c) Alteeve's Niche! Inc, 2016 - https://alteeve.com # # This program is released under the GPL v2+ # # Return Codes # 0 = Successful run # 1 = Not run as 'root'. # 2 = The '--iso' and/or '--usb' switches were not used. # 3 = The '--iso' passed in wasn't found. # 4 = Specifed USB device appears to be /dev/sda or /dev/vda. # 5 = The passed in USB path doesn't appear to be valid. # 6 = Passed in USB device doesn't exist. # 7 = Passed in USB device doesn't appear to actually be a USB device. # 8 = The USB device is read-only. # 9 = The USB device is 0-byte size; usually a usb device with no media. # 10 = Failed to unmount the USB device. # 11 = One or more required apps are missing. # 12 = Failed to partition the USB device. # 13 = Failed to find 'mbr.bin' from syslinux-nonlinux. # 14 = Failed to mount the new USB partition. # 15 = Failed to mount the source ISO loopback. # 16 = One or more files on the source ISO were too large for 'vfat'. # 17 = Source .c32 syslinux file not found. # 18 = vfat label not seen after format was called, bad format likely. # # NOTE: We do NOT use preset paths to executables to maximize portability between distros. If a user doesn't # have someting in their PATH, the shell call will fail. Oh well. # use strict; use warnings; use IO::Handle; my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0]; my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0]; if (($running_directory =~ /^\./) && ($ENV{PWD})) { $running_directory =~ s/^\./$ENV{PWD}/; } my $conf = { # Executable programs. We will search for these in PATH if not pre-defined (start with '/...'). executable => { curl => "curl", syslinux => "syslinux", }, # Paths to files and directories path => { mbr_bin => "", running_dir => $running_directory, redhat_release => "/etc/redhat-release", os_release => "/etc/os-release", }, sys => { debug => 1, disk_label => "Anvil_USB", required => { mkdosfs => "", # No switch for version, just call it parted => "--version", rsync => "--version", syslinux => "--version", udevadm => "--version", }, source_iso => "", support_url => "https://alteeve.com/w/Support", usb_device => "", usb_partition => "", usb_mount => "/tmp/anvil_usb", iso_mount => "/tmp/anvil_iso", local_os => "", }, url => { 'ldlinux.c32' => "https://alteeve.com/an-repo/misc/syslinux/ldlinux.c32", 'libcom32.c32' => "https://alteeve.com/an-repo/misc/syslinux/libcom32.c32", 'libutil.c32' => "https://alteeve.com/an-repo/misc/syslinux/libutil.c32", 'vesamenu.c32' => "https://alteeve.com/an-repo/misc/syslinux/vesamenu.c32", }, }; system('clear'); print "-=] Anvil! USB Installer\n"; get_switches($conf); $conf->{sys}{debug} = 1 if $conf->{switches}{debug}; if (($conf->{switches}{h}) or ($conf->{switches}{'?'}) or ($conf->{switches}{help})) { print_usage($conf); exit(0); } # Make sure we're running as 'root' # $< == real UID, $> == effective UID if (($< != 0) && ($> != 0)) { print "[ Error ] - I am sorry, but this must be run as the root user.\n"; exit(2); } if ((not $conf->{switches}{iso}) or (not $conf->{switches}{usb})) { print "[ Error ] - Both '--iso' and '--usb' are required. Use '--help' for more information.\n"; exit(1); } # Enable debugging if the user requested it. $conf->{sys}{debug} = 1 if $conf->{switches}{v}; $conf->{sys}{debug} = 2 if $conf->{switches}{vv}; $conf->{sys}{debug} = 3 if $conf->{switches}{vvv}; $conf->{sys}{source_iso} = $conf->{switches}{iso}; $conf->{sys}{usb_device} = $conf->{switches}{usb}; $conf->{sys}{usb_partition} = $conf->{sys}{usb_device}."1"; # Does the ISO exist? if (not -e $conf->{sys}{source_iso}) { print "[ Error ] - The source ISO: [".$conf->{sys}{source_iso}."] wasn't found.\n"; exit(3); } # Is the USB device sane and found? if ($conf->{sys}{usb_device} =~ /\/dev\/[vs]da/) { print "[ Error ] - The USB device was passed as: [".$conf->{sys}{usb_device}."].\n"; print "[ Error ] To avoid accidental data loss, this device can not be used.\n"; exit(4); } # Did the user include a partition number and/or does it look like an actual block device path? if (($conf->{sys}{usb_device} !~ /^\/dev\/[vs]d/) or ($conf->{sys}{usb_device} =~ /\d$/)) { print "[ Error ] - The USB device was passed as: [".$conf->{sys}{usb_device}."],\n"; print "[ Error ] which does not appear to be a valid block device path. Please\n"; print "[ Error ] use '/dev/sdX' (no ending partition number).\n"; exit(5); } # Does the device appear to exist? if (not -e $conf->{sys}{usb_device}) { print "[ Error ] - The USB device was passed as: [".$conf->{sys}{usb_device}."],\n"; print "[ Error ] which does not appear to exist.\n"; exit(6); } # Check the OS type check_os($conf); # Make sure the apps we'll need are installed check_apps($conf); # Is it a USB device? If so, it will report the detail of the device to the user. If not, it will exit. check_if_usb($conf); # Ask the user to confirm. print "=======================================================================\n"; print "[ WARNING ] - This device will be completely erased! All existing data\n"; print "[ WARNING ] will be lost!\n"; print "=======================================================================\n"; print "Proceed? [y/N] "; my $answer = ; chomp($answer); print "$THIS_FILE ".__LINE__."; [ Debug ] - answer: [".$answer."]\n" if $conf->{sys}{debug} >= 2; if ((lc($answer) eq "y") or (lc($answer) eq "yes")) { print "- Thank you, Proceeding\n"; # Make sure we have the 'mbr.bin' file. find_mbr_bin($conf); } else { print "- Exiting!\n"; exit(0); } # Still alive? Wee! force_unmount($conf); # Wipe and partition the device. prep_device($conf); # Write to the device: write_device($conf); # Cleanup cleanup($conf); print "Done! Plug the USB drive into the machine you wish to make a Striker dashboard. Press the key for you system to manually select a boot device (usually or ) and choose this USB drive. Select the appropriate Striker number from the menu. NOTE: The Install performed by this USB drive is fully automated, once started. Any data on the target machine will be erased without further warning! Have fun! "; exit(0); ############################################################################################################# # Functions # ############################################################################################################# # This determines what OS we're on and tunes to run appropriately. sub check_os { my ($conf) = @_; if (-e $conf->{path}{redhat_release}) { # We're a RHEL variant. my $os_string = ""; my $shell_call = $conf->{path}{redhat_release}; print "$THIS_FILE ".__LINE__."; [ Debug ] - Calling: [$shell_call].\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "<$shell_call") or die "Failed to read: [$shell_call]. Error: $!\n"; while (<$file_handle>) { chomp; $os_string = $_; print "$THIS_FILE ".__LINE__."; [ Debug ] - os_string: [$os_string].\n" if $conf->{sys}{debug} >= 2; } close $file_handle; if ($os_string =~ /6\./) { $conf->{sys}{local_os} = "el6"; print "$THIS_FILE ".__LINE__."; [ Debug ] - sys::local_os: [".$conf->{sys}{local_os}."].\n" if $conf->{sys}{debug} >= 2; } elsif ($os_string =~ /7\./) { $conf->{sys}{local_os} = "el7"; print "$THIS_FILE ".__LINE__."; [ Debug ] - sys::local_os: [".$conf->{sys}{local_os}."].\n" if $conf->{sys}{debug} >= 2; } elsif ($os_string =~ /Fedora/) { $conf->{sys}{local_os} = "fedora"; print "$THIS_FILE ".__LINE__."; [ Debug ] - sys::local_os: [".$conf->{sys}{local_os}."].\n" if $conf->{sys}{debug} >= 2; } } elsif (-e $conf->{path}{os_release}) { ### SUSE style # NAME="openSUSE Leap" # VERSION="42.1" # VERSION_ID="42.1" # PRETTY_NAME="openSUSE Leap 42.1 (x86_64)" # ID=opensuse # ANSI_COLOR="0;32" # CPE_NAME="cpe:/o:opensuse:opensuse:42.1" # BUG_REPORT_URL="https://bugs.opensuse.org" # HOME_URL="https://opensuse.org/" # ID_LIKE="suse" # ### Ubuntu style # NAME="Ubuntu" # VERSION="16.04.1 LTS (Xenial Xerus)" # ID=ubuntu # ID_LIKE=debian # PRETTY_NAME="Ubuntu 16.04.1 LTS" # VERSION_ID="16.04" # HOME_URL="http://www.ubuntu.com/" # SUPPORT_URL="http://help.ubuntu.com/" # BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/" # UBUNTU_CODENAME=xenial my $os_name = ""; my $os_version = ""; my $shell_call = $conf->{path}{os_release}; print "$THIS_FILE ".__LINE__."; [ Debug ] - Calling: [$shell_call].\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "<$shell_call") or die "Failed to read: [$shell_call]. Error: $!\n"; while (<$file_handle>) { chomp; my $line = $_; print "$THIS_FILE ".__LINE__."; [ Debug ] - line: [$line].\n" if $conf->{sys}{debug} >= 2; if ($line =~ /NAME="(.*?)"/) { $os_name = lc($line); print "$THIS_FILE ".__LINE__."; [ Debug ] - os_name: [$os_name].\n" if $conf->{sys}{debug} >= 2; } if ($line =~ /VERSION="(.*?)"/) { $os_version = lc($line); print "$THIS_FILE ".__LINE__."; [ Debug ] - os_version: [$os_version].\n" if $conf->{sys}{debug} >= 2; } } close $file_handle; if ($os_name) { if ($os_name =~ /suse/) { $conf->{sys}{local_os} = "suse"; print "$THIS_FILE ".__LINE__."; [ Debug ] - sys::local_os: [".$conf->{sys}{local_os}."].\n" if $conf->{sys}{debug} >= 2; } elsif ($os_name =~ /ubuntu/) { $conf->{sys}{local_os} = "ubuntu"; print "$THIS_FILE ".__LINE__."; [ Debug ] - sys::local_os: [".$conf->{sys}{local_os}."].\n" if $conf->{sys}{debug} >= 2; } } } return(0); } # This cleans up and syncs. sub cleanup { my ($conf) = @_; print "- Cleaning up, please wait while the USB drive is finalized!\n"; print " If the OS cached data being written to the USB, this might take a bit.\n"; my $shell_call = "sync"; print "$THIS_FILE ".__LINE__."; [ Debug ] - Calling: [$shell_call].\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "$shell_call 2>&1 |") or die "Failed to call: [$shell_call]. Error: $!\n"; while (<$file_handle>) { chomp; my $line = $_; print "$THIS_FILE ".__LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; } close $file_handle; # Now unmount. foreach my $mount_point ($conf->{sys}{iso_mount}, $conf->{sys}{usb_mount}) { print "- Unmounting: [$mount_point]\n"; my $shell_call = "umount -f $mount_point"; print "$THIS_FILE ".__LINE__."; [ Debug ] - Calling: [$shell_call].\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "$shell_call 2>&1 |") or die "Failed to call: [$shell_call]. Error: $!\n"; while (<$file_handle>) { chomp; my $line = $_; print "$THIS_FILE ".__LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; } close $file_handle; print "- Removing the temporary mount point: [$mount_point]\n"; rmdir $mount_point or die "Failed to remove the temporary mount point: [$mount_point]. The error was: $!\n"; } return(0); } # This writes out the data to the USB device (and mounts the USB device and ISO temporarily. sub write_device { my ($conf) = @_; my $usb_mount = $conf->{sys}{usb_mount}; my $iso_mount = $conf->{sys}{iso_mount}; if (not -e $usb_mount) { print "- Creating the temporary USB mount point: [$usb_mount]\n"; mkdir $usb_mount or die "Failed to create the mount point: [$usb_mount], the error was: $?\n"; } if (not -e $iso_mount) { print "- Creating the temporary ISO mount point: [$iso_mount]\n"; mkdir $iso_mount or die "Failed to create the mount point: [$iso_mount], the error was: $?\n"; } # Mount the USB device print "- Mounting the USB partition: [".$conf->{sys}{usb_partition}."] on: [".$usb_mount."]\n"; my $shell_call = "mount ".$conf->{sys}{usb_partition}." ".$usb_mount; print "$THIS_FILE ".__LINE__."; [ Debug ] - Calling: [$shell_call].\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "$shell_call 2>&1 |") or die "Failed to call: [$shell_call]. Error: $!\n"; while (<$file_handle>) { chomp; my $line = $_; print "$THIS_FILE ".__LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; } close $file_handle; # Mount the ISO source print "- Mounting the source ISO: [".$conf->{sys}{source_iso}."]\n"; print " on: [".$iso_mount."]\n"; $shell_call = "mount -o loop ".$conf->{sys}{source_iso}." ".$iso_mount; print "$THIS_FILE ".__LINE__."; [ Debug ] - Calling: [$shell_call].\n" if $conf->{sys}{debug} >= 2; open ($file_handle, "$shell_call 2>&1 |") or die "Failed to call: [$shell_call]. Error: $!\n"; while (<$file_handle>) { chomp; my $line = $_; print "$THIS_FILE ".__LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; } close $file_handle; # Sleep for a moment to make sure they register as mounted sleep 2; print "- Verifying they mounted.\n"; my $usb_found = 0; my $iso_found = 0; $shell_call = "mount"; print "$THIS_FILE ".__LINE__."; [ Debug ] - Calling: [$shell_call].\n" if $conf->{sys}{debug} >= 2; open ($file_handle, "$shell_call 2>&1 |") or die "Failed to call: [$shell_call]. Error: $!\n"; while (<$file_handle>) { chomp; my $line = $_; print "$THIS_FILE ".__LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; if ($line =~ /on $usb_mount /) { $usb_found = 1; print "$THIS_FILE ".__LINE__."; [ Debug ] - usb_found: [$usb_found]\n" if $conf->{sys}{debug} >= 2; } if ($line =~ /on $iso_mount /) { $iso_found = 1; print "$THIS_FILE ".__LINE__."; [ Debug ] - iso_found: [$iso_found]\n" if $conf->{sys}{debug} >= 2; } } close $file_handle; if (not $usb_found) { print " [ Error ] - The USB partition: [".$conf->{sys}{usb_partition}."] [ Error ] appears to have not mounted on: [$usb_mount]\n"; exit(14); } if (not $iso_found) { print " [ Error ] - The source ISO: [".$conf->{sys}{source_iso}."] [ Error ] appears to have not mounted on: [$iso_mount]\n"; exit(15); } # Make sure everything in the source is under 4GB print "- Verifying that everything we're about to copy is under 4 GiB for 'vfat'\n"; print " compatibility.\n"; my $too_large = 0; $shell_call = "find $iso_mount -type f -size +4G -exec ls -lh {} \\;"; print "$THIS_FILE ".__LINE__."; [ Debug ] - Calling: [$shell_call].\n" if $conf->{sys}{debug} >= 2; open ($file_handle, "$shell_call 2>&1 |") or die "Failed to call: [$shell_call]. Error: $!\n"; while (<$file_handle>) { chomp; my $line = $_; print "$THIS_FILE ".__LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 1; my $file = ($line =~ /($iso_mount\/.*)$/)[0]; print "[ Error ] - The source file: [$file] is too large to fit on a 'vfat' file\n"; print "[ Error ] system (which we have to use). Unable to proceed.\n"; $too_large = 1; } close $file_handle; # Make sure the source ISO isn't too big, either. my $source_iso_byte_size = (stat($conf->{sys}{source_iso}))[7]; my $max_byte_size = 4*(2**30); if ($source_iso_byte_size > $max_byte_size) { $too_large = 1;; print "[ Error ] - The source ISO: [".$conf->{sys}{source_iso}."] is too large to fit on a 'vfat' file\n"; print "[ Error ] system (which we have to use). Unable to proceed.\n"; } if ($too_large) { exit(16); } # Copy the source ISO files into place. print "- Copying the source ISO contents to the USB device.\n"; $shell_call = "rsync --archive --no-owner --no-perms --no-group --safe-links --modify-window 1 --stats $iso_mount/ $usb_mount/"; print "$THIS_FILE ".__LINE__."; [ Debug ] - Calling: [$shell_call].\n" if $conf->{sys}{debug} >= 2; open ($file_handle, "$shell_call 2>&1 |") or die "Failed to call: [$shell_call]. Error: $!\n"; while (<$file_handle>) { chomp; my $line = $_; print "$THIS_FILE ".__LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; } close $file_handle; # Protect the USB device from accidental format if striker-usb-insert is enabled and set to # remote-usb::luks::force_initialize = 1. print "- Protecting the USB drive in case 'striker-usb-insert' is set to force-initialize.\n"; $shell_call = "touch $usb_mount/.protected"; print "$THIS_FILE ".__LINE__."; [ Debug ] - Calling: [$shell_call].\n" if $conf->{sys}{debug} >= 2; open ($file_handle, "$shell_call 2>&1 |") or die "Failed to call: [$shell_call]. Error: $!\n"; while (<$file_handle>) { chomp; my $line = $_; print "$THIS_FILE ".__LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; } close $file_handle; # Copy the ISO to the drive print "- Copying the source ISO to the USB drive. This might take a bit, please be\n"; print " patient!\n"; $shell_call = "rsync --archive --no-owner --no-perms --no-group --safe-links --modify-window 1 --stats ".$conf->{sys}{source_iso}." $usb_mount/"; print "$THIS_FILE ".__LINE__."; [ Debug ] - Calling: [$shell_call].\n" if $conf->{sys}{debug} >= 2; open ($file_handle, "$shell_call 2>&1 |") or die "Failed to call: [$shell_call]. Error: $!\n"; while (<$file_handle>) { chomp; my $line = $_; print "$THIS_FILE ".__LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; } close $file_handle; # Use the syslinux installer to create a MBR for the partition. #UPDATE THIS WITH NON-HARDCODED BINARIES# print "- Copying the 'syslinux' files into place.\n"; $shell_call = $conf->{executable}{syslinux}." --install ".$conf->{sys}{usb_partition}; print "$THIS_FILE ".__LINE__."; [ Debug ] - Calling: [$shell_call].\n" if $conf->{sys}{debug} >= 2; open ($file_handle, "$shell_call 2>&1 |") or die "Failed to call: [$shell_call]. Error: $!\n"; while (<$file_handle>) { chomp; my $line = $_; print "$THIS_FILE ".__LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; } close $file_handle; # If this is Fedora, we need to copy a few more files. print "$THIS_FILE ".__LINE__."; [ Debug ] - sys::local_os: [".$conf->{sys}{local_os}."].\n" if $conf->{sys}{debug} >= 2; if (($conf->{sys}{local_os} eq "fedora") or ($conf->{sys}{local_os} eq "ubuntu")) { my $mbr_bin_path = $conf->{path}{mbr_bin}; my $syslinux_directory = ($mbr_bin_path =~ /^(.*?)\/mbr\.bin$/)[0]; my $ldlinux_c32_path = $syslinux_directory."/ldlinux.c32"; my $libcom32_c32_path = $syslinux_directory."/libcom32.c32"; my $libutil_c32_path = $syslinux_directory."/libutil.c32"; my $vesamenu_c32_path = $syslinux_directory."/vesamenu.c32"; print "$THIS_FILE ".__LINE__."; [ Debug ] - ldlinux_c32_path: [$ldlinux_c32_path], libcom32_c32_path: [$libcom32_c32_path], libutil_c32_path: [$libutil_c32_path], vesamenu_c32_path: [$vesamenu_c32_path].\n" if $conf->{sys}{debug} >= 2; # Tune for different sources. if ((not -e $ldlinux_c32_path) or (not -e $libcom32_c32_path) or (not -e $libutil_c32_path) or (not -e $vesamenu_c32_path)) { $ldlinux_c32_path = "/tmp/ldlinux.c32"; $libcom32_c32_path = "/tmp/libcom32.c32"; $libutil_c32_path = "/tmp/libutil.c32"; $vesamenu_c32_path = "/tmp/vesamenu.c32"; print "$THIS_FILE ".__LINE__."; [ Debug ] - ldlinux_c32_path: [$ldlinux_c32_path], libcom32_c32_path: [$libcom32_c32_path], libutil_c32_path: [$libutil_c32_path], vesamenu_c32_path: [$vesamenu_c32_path].\n" if $conf->{sys}{debug} >= 2; download_file($conf, $conf->{url}{'ldlinux.c32'}, $ldlinux_c32_path, 0644); download_file($conf, $conf->{url}{'libcom32.c32'}, $libcom32_c32_path, 0644); download_file($conf, $conf->{url}{'libutil.c32'}, $libutil_c32_path, 0644); download_file($conf, $conf->{url}{'vesamenu.c32'}, $vesamenu_c32_path, 0644); } foreach my $source_file ($ldlinux_c32_path, $libcom32_c32_path, $libutil_c32_path, $vesamenu_c32_path) { print "$THIS_FILE ".__LINE__."; [ Debug ] - source_file: [$source_file].\n" if $conf->{sys}{debug} >= 2; if (not -e $source_file) { print " [ Error ] - The source syslinux file: [$source_file] [ Error ] was not found, unable to proceed.\n"; exit(17); } my $shell_call = "rsync --archive --no-owner --no-perms --no-group --safe-links --modify-window 1 --stats $source_file $usb_mount/syslinux/"; print "$THIS_FILE ".__LINE__."; [ Debug ] - Calling: [$shell_call].\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "$shell_call 2>&1 |") or die "Failed to call: [$shell_call]. Error: $!\n"; while (<$file_handle>) { chomp; my $line = $_; print "$THIS_FILE ".__LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; } close $file_handle; } } return(0); } # This looks for the master boot record image we'll need. sub find_mbr_bin { my ($conf) = @_; if (-e "/usr/share/syslinux/mbr.bin") { $conf->{path}{mbr_bin} = "/usr/share/syslinux/mbr.bin"; } else { print "- Searching for 'mbr.bin', this might take a minute.\n"; my $shell_call = "find /"; print "$THIS_FILE ".__LINE__."; [ Debug ] - Calling: [$shell_call].\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "$shell_call 2>&1 |") or die "Failed to call: [$shell_call]. Error: $!\n"; while (<$file_handle>) { chomp; my $line = $_; print "$THIS_FILE ".__LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; if ($line =~ /\/mbr\.bin$/) { $conf->{path}{mbr_bin} = $line; print "- Found it: [".$conf->{path}{mbr_bin}."]\n"; last; } } close $file_handle; if (not $conf->{path}{mbr_bin}) { print " [ Error ] - The file 'mbr.bin' was not found. Is the 'syslinux-nonlinux' [ Error ] package installed?\n"; exit(13); } } return(0); } # This zero's out the USB device's MDB and repartitions it to be a single partition. sub prep_device { my ($conf) = @_; print "- Wiping out existing partition geometry and MBR from: [".$conf->{sys}{usb_device}."]\n"; my $shell_call = "dd if=/dev/zero of=".$conf->{sys}{usb_device}." bs=100M count=1 oflag=direct,sync"; print "$THIS_FILE ".__LINE__."; [ Debug ] - Calling: [$shell_call].\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "$shell_call 2>&1 |") or die "Failed to call: [$shell_call]. Error: $!\n"; while (<$file_handle>) { chomp; my $line = $_; print "$THIS_FILE ".__LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; } close $file_handle; print "- Creating a new partition.\n"; $shell_call = " parted -a opt ".$conf->{sys}{usb_device}." --script -- mklabel msdos parted -a opt ".$conf->{sys}{usb_device}." --script -- mkpart primary fat32 4MiB -1s parted -a opt ".$conf->{sys}{usb_device}." --script -- set 1 boot on parted -a opt ".$conf->{sys}{usb_device}." --script unit MB print "; print "$THIS_FILE ".__LINE__."; [ Debug ] - Calling: [$shell_call].\n" if $conf->{sys}{debug} >= 2; open ($file_handle, "$shell_call 2>&1 |") or die "Failed to call: [$shell_call]. Error: $!\n"; while (<$file_handle>) { chomp; my $line = $_; print "$THIS_FILE ".__LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; } close $file_handle; # Rescan. print "- Verifying that the new partition was created successfully.\n"; $shell_call = "partprobe ".$conf->{sys}{usb_device}; print "$THIS_FILE ".__LINE__."; [ Debug ] - Calling: [$shell_call].\n" if $conf->{sys}{debug} >= 2; open ($file_handle, "$shell_call 2>&1 |") or die "Failed to call: [$shell_call]. Error: $!\n"; while (<$file_handle>) { chomp; my $line = $_; print "$THIS_FILE ".__LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; } close $file_handle; # Sleep for a bit so that partprobe can create the new /dev/ device. sleep 2; # Make sure the new partition exists. if (not -e $conf->{sys}{usb_partition}) { # Failed. print " [ Error ] - It appears the format operation failed. The expected partition: [ Error ] [".$conf->{sys}{usb_partition}."] does not exist.\n"; exit(12); } # Format it. print "- Formatting the new partition: [".$conf->{sys}{usb_partition}."]\n"; #$shell_call = "mkfs.vfat -F 32 ".$conf->{sys}{usb_partition}." -n '".$conf->{sys}{disk_label}."'"; $shell_call = "mkfs.vfat -F 32 ".$conf->{sys}{usb_partition}; print "$THIS_FILE ".__LINE__."; [ Debug ] - Calling: [$shell_call].\n" if $conf->{sys}{debug} >= 2; open ($file_handle, "$shell_call 2>&1 |") or die "Failed to call: [$shell_call]. Error: $!\n"; while (<$file_handle>) { chomp; my $line = $_; print "$THIS_FILE ".__LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; } close $file_handle; # Verify it formatted. # my $formatted = 0; # $shell_call = "fatlabel ".$conf->{sys}{usb_partition}; # print "$THIS_FILE ".__LINE__."; [ Debug ] - Calling: [$shell_call].\n" if $conf->{sys}{debug} >= 2; # open ($file_handle, "$shell_call 2>&1 |") or die "Failed to call: [$shell_call]. Error: $!\n"; # while (<$file_handle>) # { # chomp; # my $line = $_; # $line =~ s/^\s+//; # $line =~ s/\s+$//; # print "$THIS_FILE ".__LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; # if ($line eq $conf->{sys}{disk_label}) # { # $formatted = 1; # print "$THIS_FILE ".__LINE__."; [ Debug ] - formatted: [$formatted]\n" if $conf->{sys}{debug} >= 2; # } # } # close $file_handle; # if (not $formatted) # { # print " # [ Error ] - It appears that the USB partition: [".$conf->{sys}{usb_partition}."] # [ Error ] was not properly formatted. The 'fatlabel' did not return the label # [ Error ] we set: [".$conf->{sys}{disk_label}."]\n"; # exit(18); # } ### TODO: This doesn't create a copy in the backup MBR causing fsck.vfat to complain, fix. # Copy the master boot record. print "- Writing out the master boot record to: [".$conf->{sys}{usb_device}."]\n"; $shell_call = "dd bs=440 count=1 conv=notrunc if=".$conf->{path}{mbr_bin}." of=".$conf->{sys}{usb_device}; print "$THIS_FILE ".__LINE__."; [ Debug ] - Calling: [$shell_call].\n" if $conf->{sys}{debug} >= 2; open ($file_handle, "$shell_call 2>&1 |") or die "Failed to call: [$shell_call]. Error: $!\n"; while (<$file_handle>) { chomp; my $line = $_; print "$THIS_FILE ".__LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; } close $file_handle; # Install syslinux print "- Installing syslinux on: [".$conf->{sys}{usb_partition}."]\n"; $shell_call = "syslinux --install ".$conf->{sys}{usb_partition}; print "$THIS_FILE ".__LINE__."; [ Debug ] - Calling: [$shell_call].\n" if $conf->{sys}{debug} >= 2; open ($file_handle, "$shell_call 2>&1 |") or die "Failed to call: [$shell_call]. Error: $!\n"; while (<$file_handle>) { chomp; my $line = $_; print "$THIS_FILE ".__LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; } close $file_handle; return(0); } # This checks to make sure the apps we'll need are installed. sub check_apps { my ($conf) = @_; my $ok = 1; foreach my $app (sort {$a cmp $b} keys %{$conf->{sys}{required}}) { print "$THIS_FILE ".__LINE__."; [ Debug ] - app: [$app]\n" if $conf->{sys}{debug} >= 2; my $switches = $conf->{sys}{required}{$app}; my $shell_call = $app." ".$switches; print "$THIS_FILE ".__LINE__."; [ Debug ] - Calling: [$shell_call].\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "$shell_call 2>&1 |") or die "[ Error ] - The app: [$app] does not appear to be installed. Please install it and try again.\n"; while (<$file_handle>) { chomp; my $line = $_; print "$THIS_FILE ".__LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; if ($line =~ /command not found/) { $ok = 0; print "[ Error ] - The application: [$app] does not appear to be installed.\n"; } } close $file_handle; } if (not $ok) { print "[ Error ] - Please install the missing app(s) and try again.\n"; exit(11); } return(0); } # This unmounts the USB device, if necessary. sub force_unmount { my ($conf) = @_; # Call 'mount' and see if it is mounted. my $mounted = []; my $usb_device = $conf->{sys}{usb_device}; my $usb_mount = $conf->{sys}{usb_mount}; my $iso_mount = $conf->{sys}{iso_mount}; my $shell_call = "mount"; print "$THIS_FILE ".__LINE__."; [ Debug ] - Calling: [$shell_call].\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "$shell_call 2>&1 |") or die "Failed to call: [$shell_call]. Error: $!\n"; while (<$file_handle>) { chomp; my $line = $_; print "$THIS_FILE ".__LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; if (($line =~ /^($usb_device) /) or ($line =~ /^($usb_device\d+) /) or ($line =~ /on $usb_mount /) or ($line =~ /on $iso_mount /)) { my $this_mount = $1; push @{$mounted}, $this_mount; print "$THIS_FILE ".__LINE__."; [ Debug ] - this_mount: [$this_mount]\n" if $conf->{sys}{debug} >= 2; last; } } close $file_handle; print "$THIS_FILE ".__LINE__."; [ Debug ] - mounted: [".@{$mounted}."]\n" if $conf->{sys}{debug} >= 2; foreach my $device (@{$mounted}) { # Unmount it. next if not $device; print "- Unmounting: [$device]\n"; my $shell_call = "umount -f $device"; print "$THIS_FILE ".__LINE__."; [ Debug ] - Calling: [$shell_call].\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "$shell_call 2>&1 |") or die "Failed to call: [$shell_call]. Error: $!\n"; while (<$file_handle>) { chomp; my $line = $_; print "$THIS_FILE ".__LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; } close $file_handle; # Verify that it is gone. my $still_mounted = 0; $shell_call = "mount"; print "$THIS_FILE ".__LINE__."; [ Debug ] - Calling: [$shell_call].\n" if $conf->{sys}{debug} >= 2; open ($file_handle, "$shell_call 2>&1 |") or die "Failed to call: [$shell_call]. Error: $!\n"; while (<$file_handle>) { chomp; my $line = $_; print "$THIS_FILE ".__LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; if ($line =~ /^$device /) { $still_mounted = 1; print "$THIS_FILE ".__LINE__."; [ Debug ] - still_mounted: [$still_mounted]\n" if $conf->{sys}{debug} >= 2; last; } } close $file_handle; # Still mounted? print "$THIS_FILE ".__LINE__."; [ Debug ] - still_mounted: [$still_mounted]\n" if $conf->{sys}{debug} >= 2; if ($still_mounted) { print "[ Error ] - The USB device is still mounted after trying to unmount it. It is\n"; print "[ Error ] not safe to proceed.\n"; exit(10); } else { print "- Success!\n"; } } return(0); } # This calls udevadm against the passed in USB device and return '1' if it is a USB device and '0' if it is # not. sub check_if_usb { my ($conf) = @_; my $device = ""; my $is_usb = 0; my $shell_call = "ls ".$conf->{sys}{usb_device}; print "$THIS_FILE ".__LINE__."; [ Debug ] - Calling: [$shell_call].\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "$shell_call 2>&1 |") or die "Failed to call: [$shell_call]. Error: $!\n"; while (<$file_handle>) { chomp; my $this_device = $_; next if $device =~ /\d$/; $device = $this_device; print "$THIS_FILE ".__LINE__."; [ Debug ] - device: [$device]\n" if $conf->{sys}{debug} >= 2; last; } close $file_handle; # Now see if this is a USB device and, if so, what we know about it. my $this_device = ""; my $this_usb = ""; my $block_device = ($device =~ /\/dev\/(sd.*)/)[0]; my $removable = 0; my $size = 0; my $read_only = 0; my $device_vendor = "Unknown vendor, "; my $device_model = "Unknown model"; $shell_call = "udevadm info --attribute-walk --name $device"; print "$THIS_FILE ".__LINE__."; [ Debug ] - Calling: [$shell_call].\n" if $conf->{sys}{debug} >= 2; open ($file_handle, "$shell_call 2>&1 |") or die "Failed to call: [$shell_call]. Is 'udevadm' installed? Error was: $!\n"; while (<$file_handle>) { chomp; my $line = $_; print "$THIS_FILE ".__LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; if ($line =~ /looking at parent device '(.*?)'/) { $this_device = $1; print "$THIS_FILE ".__LINE__."; [ Debug ] - this_device: [$this_device].\n" if $conf->{sys}{debug} >= 2; if ((not $this_usb) && ($this_device =~ /usb\d+\/(\d+-\d+)\//)) { $is_usb = 1; $this_usb = $1; print "$THIS_FILE ".__LINE__."; [ Debug ] - is_usb: [$is_usb], this_usb: [$this_usb] parsed from: [$this_device].\n" if $conf->{sys}{debug} >= 2; } next; } if ($line =~ /ATTR\{removable\}=="(\d+)"/) { $removable = $1; print "$THIS_FILE ".__LINE__."; [ Debug ] - removable: [$removable].\n" if $conf->{sys}{debug} >= 2; } if ($line =~ /ATTR\{size\}=="(\d+)"/) { $size = $1; print "$THIS_FILE ".__LINE__."; [ Debug ] - size: [$size].\n" if $conf->{sys}{debug} >= 2; } if ($line =~ /ATTR\{ro\}=="(\d+)"/) { $read_only = $1; print "$THIS_FILE ".__LINE__."; [ Debug ] - read_only: [$read_only].\n" if $conf->{sys}{debug} >= 2; } if ($line =~ /ATTRS\{vendor\}=="(.*)"/) { my $vendor = $1; next if $vendor =~ /^0x/; $device_vendor = $vendor; $device_vendor =~ s/^\s+//; $device_vendor =~ s/\s+$//; print "$THIS_FILE ".__LINE__."; [ Debug ] - device_vendor: [$device_vendor].\n" if $conf->{sys}{debug} >= 2; } if ($line =~ /ATTRS\{model\}=="(.*)"/) { my $model = $1; next if $model =~ /^0x/; $device_model = $model; $device_model =~ s/^\s+//; $device_model =~ s/\s+$//; print "$THIS_FILE ".__LINE__."; [ Debug ] - device_model: [$device_model].\n" if $conf->{sys}{debug} >= 2; } } close $file_handle; my $sector_size = get_sector_size($conf, $block_device); my $size_gib = 0; my $size_gb = 0; my $byte_size = ($sector_size * $size); if ($block_device) { $size_gib = sprintf("%.2f", ($byte_size / (2 ** 30))); $size_gb = sprintf("%.2f", ($byte_size / (10 ** 9))); } print "$THIS_FILE ".__LINE__."; [ Debug ] - this_usb: [$this_usb], block_device: [$block_device ($device)].\n" if $conf->{sys}{debug} >= 2; if (not $this_usb) { print "[ Error ] - The USB device was passed as: [".$conf->{sys}{usb_device}."],\n"; print "[ Error ] which does not appear to actuall be a USB device.\n"; exit(7); } elsif ($read_only) { print "[ Error ] - The USB device was passed as: [".$conf->{sys}{usb_device}."],\n"; print "[ Error ] which is read-only and can not be used.\n"; exit(8); } elsif (not $size) { print "[ Error ] - The USB device was passed as: [".$conf->{sys}{usb_device}."],\n"; print "[ Error ] which appears to be a device with no media in it.\n"; exit(9); } my $yn = ["No", "Yes"]; print "- USB device path: . [$device]\n"; print "- Target drive: .... [$device_vendor $device_model]\n"; print "- The capacity is: . [$size_gib GiB ($size_gb GB)]\n"; print "- The source ISO is: [".$conf->{sys}{source_iso}."]\n"; return(0); } # This reads '/sys/block/${block_device}/queue/hw_sector_size' to get the sector size. sub get_sector_size { my ($conf, $block_device) = @_; print "$THIS_FILE ".__LINE__."; get_sector_size(); block_device: [$block_device]\n" if $conf->{sys}{debug} >= 2; # Return 0 if the sysfs file doesn't exist; return(0) if not -e "/sys/block/${block_device}/queue/hw_sector_size"; my $sector_size = 0; my $shell_call = "/sys/block/${block_device}/queue/hw_sector_size"; print "$THIS_FILE ".__LINE__."; [ Debug ] - Calling: [$shell_call].\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "<$shell_call") or die "Failed to read: [$shell_call]. Error: $!\n"; while (<$file_handle>) { chomp; $sector_size = $_; print "$THIS_FILE ".__LINE__."; [ Debug ] - sector_size: [$sector_size].\n" if $conf->{sys}{debug} >= 2; } close $file_handle; print "$THIS_FILE ".__LINE__."; sector_size: [$sector_size]\n" if $conf->{sys}{debug} >= 2; return($sector_size); } # This collects the command line switches sub get_switches { my ($conf) = @_; my $last_argument = ""; foreach my $argument (@ARGV) { if ($last_argument eq "raw") { # Don't process anything. $conf->{switches}{raw} .= " $argument"; } elsif ($argument =~ /^-/) { # If the argument is just '--', appeand everything after it to 'raw'. $conf->{sys}{switch_count}++; if ($argument eq "--") { $last_argument = "raw"; $conf->{switches}{raw} = ""; } else { ($last_argument) = ($argument =~ /^-{1,2}(.*)/)[0]; if ($last_argument =~ /=/) { # Break up the variable/value. ($last_argument, my $value) = (split /=/, $last_argument, 2); $conf->{switches}{$last_argument} = $value; } else { $conf->{switches}{$last_argument} = "#!SET!#"; } } } else { if ($last_argument) { $conf->{switches}{$last_argument} = $argument; $last_argument = ""; } elsif (($argument eq "start") or ($argument eq "stop") or ($argument eq "status")) { $conf->{switches}{$argument} = 1; } else { # Got a value without an argument. $conf->{switches}{error} = 1; } } } # Clean up the initial space added to 'raw'. if ($conf->{switches}{raw}) { $conf->{switches}{raw} =~ s/^ //; } # Debug if ($conf->{sys}{debug} >= 3) { foreach my $variable (sort {$a cmp $b} keys %{$conf->{switches}}) { print "$THIS_FILE ".__LINE__."; [ Debug ] - Variable: [$variable]\t-> value: [$conf->{switches}{$variable}]\n"; } } return(0); } # This downloads the give file, if it is not already downloaded, and verifies that it is not a curl failure # message. sub download_file { my ($conf, $source, $target, $mode) = @_; $mode = "" if not defined $mode; if (-e $target) { print "- The file: [$target] is already downloaded.\n" if $conf->{sys}{debug} >= 2; } else { # Get the file size so that we can warn the user my $size = ""; my $shell_call = $conf->{executable}{curl}." --head $source"; print "$THIS_FILE ".__LINE__."; [ Debug ] - shell_call: [$shell_call]\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "$shell_call 2>&1 |") or die __LINE__."; \n[ Error ] - Failed to call: [$shell_call]. Received error: $!\n\n"; while(<$file_handle>) { chomp; my $line = $_; $line =~ s/\r//; print "$THIS_FILE ".__LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; if ($line =~ /Content-Length: (\d+)$/) { $size = $1; print "$THIS_FILE ".__LINE__."; [ Debug ] - size: [$size]\n" if $conf->{sys}{debug} >= 2; } } close $file_handle; # If I got a size, make it (crudely) human readable. my $say_size = ""; if ($size =~ /^\d+$/) { $say_size = hr_size($conf, $size); } my $start_time = time; print "- Downloading: [$source] to: [$target], which is: [$say_size]... "; print "\n" if $conf->{sys}{debug} >= 1; ### NOTE: Do NOT use 2>&1 as this pushes the download progress into the download file. If ### debug is enabled, we'll dump the progress to the screen. $shell_call = $conf->{executable}{curl}." $source 2>/dev/null > $target"; print "$THIS_FILE ".__LINE__."; [ Debug ] - shell_call: [$shell_call]\n" if $conf->{sys}{debug} >= 2; if ($conf->{sys}{debug}) { $shell_call = $conf->{executable}{curl}." $source > $target"; } print "$THIS_FILE ".__LINE__."; [ Debug ] - shell_call: [$shell_call]\n" if $conf->{sys}{debug} >= 2; open ($file_handle, "$shell_call |") or die __LINE__."; \n[ Error ] - Failed to call: [$shell_call]. Received error: $!\n\n"; while(<$file_handle>) { chomp; my $line = $_; print "$THIS_FILE ".__LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; } close $file_handle; my $file_ok = 0; if (-e $target) { # If the file is less than 1024 bytes, read it in to make sure it is not a # failure message. my $size = (stat($target))[7]; print "$THIS_FILE ".__LINE__."; [ Debug ] - Downloaded file: [$target] is: [$size] bytes.\n" if $conf->{sys}{debug} >= 1; if ($size < 1024) { # Is it binary or text? If it is text, make sure it is not a failure message. if (not $size) { # File is empty. print "\n" if not $conf->{sys}{debug}; print "[ Error ] - Failed to download: [$target].\n"; print "[ Error ] The downloaded file was 0-bytes.\n"; print "[ Error ] Is the source: [$source] accessible?\n"; } elsif (-B $target) { # Yay! $file_ok = 1; print "$THIS_FILE ".__LINE__."; [ Debug ] - Downloaded file: [$target] is binary, good.\n" if $conf->{sys}{debug} >= 1; } else { # Read it in. my $shell_call = $target; print "$THIS_FILE ".__LINE__."; [ Debug ] - shell_call: [$shell_call]\n" if $conf->{sys}{debug} >= 2; open (my $file_handle, "<$shell_call") or die __LINE__."; \n[ Error ] - Failed to read: [$shell_call]. Received error: $!\n\n"; while(<$file_handle>) { chomp; my $line = $_; print "$THIS_FILE ".__LINE__."; [ Debug ] - line: [$line]\n" if $conf->{sys}{debug} >= 2; if ($line =~ /Could not resolve host/i) { # Bad URL print "\n" if not $conf->{sys}{debug}; print "[ Error ] - Failed to download: [$target].\n"; print "[ Error ] It appears that the domain name is invalid.\n"; } if ($line =~ /404 Not Found<\/title>/i) { # File not found. print "\n" if not $conf->{sys}{debug}; print "[ Error ] - Failed to download: [$target].\n"; print "[ Error ] It appears that the file was not found on the server.\n"; } if ($line =~ /<a href="(.*?)">redirected<\/a>/) { # Redirect my $new_source = $1; # Cleanup close $file_handle; unlink $target or die __LINE__."; [ Error ] - Failed to delete: [$target] (it is a failed download message, not the file). The error was: $!\n"; # Download the new source print "Redirected.\n"; print "$THIS_FILE ".__LINE__."; [ Debug ] - Redirected source: [$source]: to: [$new_source].\n" if $conf->{sys}{debug} >= 1; download_file($conf, $new_source, $target, $mode); return(0); } } close $file_handle; } } else { # Too big to be a failure, so we're probably good. $file_ok = 1; print "$THIS_FILE ".__LINE__."; [ Debug ] - Downloaded file: [$target] is large enough that analysis is not required.\n" if $conf->{sys}{debug} >= 1; } } print "$THIS_FILE ".__LINE__."; [ Debug ] - target: [$target], file_ok: [$file_ok]\n" if $conf->{sys}{debug} >= 1; if ($file_ok) { my $duration = time - $start_time; print "Finished! Took: [$duration] second(s).\n"; } else { # Sadness. print "\n" if not $conf->{sys}{debug}; print "[ Error ] - There was a problem downloading: [$source] to: [$target]\n"; if (-e $target) { # Remove the sadness print "[ Note ] - Failed download target: [$target] will now be removed.\n"; unlink $target or die __LINE__."; [ Error ] - Failed to delete: [$target] (it is a failed download message, not the file). The error was: $!\n"; } exit(7); } } # Set the mode, if needed if ($mode) { chmod $mode, $target; } return(0); } # This converts a raw number of bytes to a human readable size. It always uses base-2. sub hr_size { my ($conf, $hr_size) = @_; if ($hr_size >= (2 ** 80)) { # Yebibyte $hr_size = sprintf("%.3f", ($hr_size /= (2 ** 80)))." YiB"; } elsif ($hr_size >= (2 ** 70)) { # Zebibyte $hr_size = sprintf("%.3f", ($hr_size /= (2 ** 70)))." ZiB"; } elsif ($hr_size >= (2 ** 60)) { # Exbibyte $hr_size = sprintf("%.3f", ($hr_size /= (2 ** 60)))." EiB"; } elsif ($hr_size >= (2 ** 50)) { # Pebibyte $hr_size = sprintf("%.3f", ($hr_size /= (2 ** 50)))." PiB"; } elsif ($hr_size >= (2 ** 40)) { # Tebibyte $hr_size = sprintf("%.2f", ($hr_size /= (2 ** 40)))." TiB"; } elsif ($hr_size >= (2 ** 30)) { # Gibibyte $hr_size = sprintf("%.2f", ($hr_size /= (2 ** 30)))." GiB"; } elsif ($hr_size >= (2 ** 20)) { # Mebibyte $hr_size = sprintf("%.2f", ($hr_size /= (2 ** 20)))." MiB"; } elsif ($hr_size >= (2 ** 10)) { # Kibibyte $hr_size = sprintf("%.1f", ($hr_size /= (2 ** 10)))." KiB"; } else { $hr_size .= " Bytes"; } return($hr_size); } # Print the usage information. sub print_usage { my ($conf) = @_; my $help = " -=] Anvil! - Generate USB Installer DESCRIPTION This program takes an Anvil! ISO build with 'anvil-generate-iso' and writes it to a USB drive. This is not a direct 'dd' of the ISO, additional steps are needed to insure all the files (including a copy of the original ISO itself) are available for the kickstart files. SWITCHES -h, -?, --help Show this dialogue and exit. --iso </path/to/Anvil.iso> This is the path (full or relative) to the ISO to use when generating the USB installer. --usb </dev/sdX> This is the path to the root of the USB device to use. Do not specify a partition. That is, use '/dev/sdb', not '/dev/sdb1'. WARNING: THIS WILL ENTIRELY WIPE ANY AND ALL DATA ON THE TARGET USB DEVICE! Be very certain that you have pointed at the right device! -v, -vv, -vvv Enable debugging output (each additional 'v' increases the verbosity). EXAMPLES Generate a bootable USB device using a RHEL-based Anvil! image writing to a USB drive found at '/dev/sdb'. ./anvil-usb-installer --iso ./Anvil_m2_RHEL_6.7_alpha.iso --usb /dev/sdb SUPPORT $conf->{sys}{support_url} Alteeve's Niche! Inc. "; open (my $file_handle, ">", "/tmp/${THIS_FILE}.help") or die __LINE__."; Couldn't write help to /tmp/, error was: $!\n"; print $file_handle $help; close $file_handle; system("/usr/bin/less /tmp/${THIS_FILE}.help"); return(0); }