-#! /usr/bin/perl
-
-$mem = 4;
-$serial_out = 1;
-while (@ARGV) {
- my ($arg) = shift (@ARGV);
- if ($arg =~ /--(bochs|qemu|gsx)$/) {
- die "$arg conflicts with --$sim\n" if defined $sim;
- $sim = $1;
- } elsif ($arg =~ /--(no-debug|monitor|gdb)$/) {
- die "$debug conflicts with --$debug" if defined $debug;
- $debug = $1;
- } elsif ($arg eq 'run') {
- run_vm (@ARGV);
- exit 0;
- } elsif ($arg =~ /^--mem(?:ory)?=(\d+)/) {
- $mem = $1;
- } elsif ($arg eq '-m') {
- die "-m needs integer argument\n" if !@ARGV || $ARGV[0] !~ /^-?\d+$/;
- $mem = shift (@ARGV);
- } elsif ($arg eq '-j') {
- die "-j need random seed argument\n" if !@ARGV;
- die "-j need integer argument\n" if $ARGV[0] !~ /^-?\d+$/;
- $jitter = shift (@ARGV);
- } elsif ($arg =~ /--jitter=(-?\d+)$/) {
- $jitter = $1;
- } elsif ($arg eq '--no-vga' || $arg eq '-v') {
- print "warning: --no-vga conflicts with --terminal\n"
- if $vga eq 'terminal';
- $vga = 'none';
- } elsif ($arg eq '--no-serial' || $arg eq '-s') {
- $serial_out = 0;
- } elsif ($arg eq '--terminal' || $arg eq '-t') {
- print "warning: --terminal conflicts with --no-vga\n"
- if $vga eq 'none';
- $vga = 'terminal';
- } elsif ($arg eq 'make-disk') {
- usage () if @ARGV != 2;
- my ($file, $mb) = @ARGV;
- usage () if $mb !~ /^\d+(\.\d+)?|\.\d+$/;
- die "$file: already exists\n" if -e $file;
-
- create_disk ($file, int ($mb * 1008));
- exit 0;
- } elsif ($arg eq 'put') {
- usage () if @ARGV != 1 && @ARGV != 2;
- my ($hostfn, $guestfn) = @ARGV;
- $guestfn = $hostfn if !defined $guestfn;
-
- # Create scratch disk from file.
- die "$hostfn: $!\n" if ! -e $hostfn;
- my ($size) = -s _;
- copy_pad ($hostfn, "scratch.dsk", 512);
-
- # Do copy.
- run_vm ("-ci", $guestfn, $size, "-q");
- exit 0;
- } elsif ($arg eq 'get') {
- usage () if @ARGV != 1 && @ARGV != 2;
- my ($guestfn, $hostfn) = @ARGV;
- $hostfn = $guestfn if !defined $hostfn;
- die "$hostfn: already exists\n" if -e $file;
-
- # Create scratch disk big enough for any file in the filesystem
- # (modulo sparse files).
- die "fs.dsk: $!\n" if ! -e "fs.dsk";
- my ($fs_size) = -s _;
- my ($scratch_size) = -s "scratch.dsk";
- $scratch_size = 0 if !defined $scratch_size;
- create_disk ("scratch.dsk", $fs_size / 1024 + 16)
- if $scratch_size < $fs_size + 16384;
-
- # Do copy.
- run_vm ("-co", $guestfn, "-q");
-
- # Read out scratch disk.
- print "copying $guestfn from scratch.dsk to $hostfn...\n";
- open (SRC, "<scratch.dsk") or die "scratch.dsk: open: $!\n";
- open (DST, ">$hostfn") or die "$hostfn: create: $!\n";
- my ($input);
- read (SRC, $input, 512) == 512 or die "scratch.dsk: read error\n";
- my ($size) = unpack ("%V", $input);
- $size != 0xffffffff or die "$guestfn: too big for scratch.dsk?";
- read (SRC, $src, $size) == $size or die "scratch.dsk: read error\n";
- print DST $src or die "$hostfn: write error\n";
- close (DST);
- close (SRC);
-
- exit 0;
- } elsif ($arg eq 'help' || $arg eq '--help') {
- usage (0);
- } else {
- die "unknown option `$arg'\n";
+#! /usr/bin/perl -w
+
+use strict;
+use POSIX;
+use Fcntl;
+use File::Temp 'tempfile';
+use Getopt::Long qw(:config bundling);
+
+# Command-line options.
+our ($sim); # Simulator: bochs, qemu, or gsx.
+our ($debug) = "none"; # Debugger: none, monitor, or gdb.
+our ($mem) = 4; # Physical RAM in MB.
+our ($serial_out) = 1; # Send output to serial port?
+our ($vga); # VGA output: window, terminal, or none.
+our ($jitter); # Seed for random timer interrupts, if set.
+our ($realtime); # Synchronize timer interrupts with real time?
+our ($timeout); # Maximum runtime in seconds, if set.
+our (@puts); # Files to copy into the VM.
+our (@gets); # Files to copy out of the VM.
+our ($as_ref); # Reference to last addition to @gets or @puts.
+our (@kernel_args); # Arguments to pass to kernel.
+our (%disks) = (OS => {FILENAME => 'os.dsk'}, # Disks to give VM.
+ FS => {DEF_FN => 'fs.dsk'},
+ SCRATCH => {DEF_FN => 'scratch.dsk'},
+ SWAP => {DEF_FN => 'swap.dsk'});
+our (@disks_by_iface) = @disks{qw (OS FS SCRATCH SWAP)};
+
+parse_command_line ();
+find_disks ();
+prepare_scratch_disk ();
+prepare_arguments ();
+run_vm ();
+finish_scratch_disk ();
+
+exit 0;
+\f
+# Parses the command line.
+sub parse_command_line {
+ usage (0) if @ARGV == 0 || (@ARGV == 1 && $ARGV[0] eq '--help');
+
+ @kernel_args = @ARGV;
+ if (grep ($_ eq '--', @kernel_args)) {
+ @ARGV = ();
+ while ((my $arg = shift (@kernel_args)) ne '--') {
+ push (@ARGV, $arg);
+ }
+ GetOptions ("sim=s" => sub { set_sim (@_) },
+ "bochs" => sub { set_sim ("bochs") },
+ "qemu" => sub { set_sim ("qemu") },
+ "gsx" => sub { set_sim ("gsx") },
+
+ "debug=s" => sub { set_debug (@_) },
+ "no-debug" => sub { set_debug ("none") },
+ "monitor" => sub { set_debug ("monitor") },
+ "gdb" => sub { set_debug ("gdb") },
+
+ "m|memory=i" => \$mem,
+ "j|jitter=i" => sub { set_jitter (@_) },
+ "r|realtime" => sub { set_realtime () },
+ "T|timeout=i" => \$timeout,
+
+ "v|no-vga" => sub { set_vga ('none'); },
+ "s|no-serial" => sub { $serial_out = 0; },
+ "t|terminal" => sub { set_vga ('terminal'); },
+
+ "p|put-file=s" => sub { add_file (\@puts, $_[1]); },
+ "g|get-file=s" => sub { add_file (\@gets, $_[1]); },
+ "a|as=s" => sub { set_as ($_[1]); },
+
+ "h|help" => sub { usage (0); },
+
+ "os-disk=s" => \$disks{OS}{FILENAME},
+ "fs-disk=s" => \$disks{FS}{FILENAME},
+ "scratch-disk=s" => \$disks{SCRATCH}{FILENAME},
+ "swap-disk=s" => \$disks{SWAP}{FILENAME},
+
+ "0|disk-0|hda=s" => \$disks_by_iface[0]{FILENAME},
+ "1|disk-1|hdb=s" => \$disks_by_iface[1]{FILENAME},
+ "2|disk-2|hdc=s" => \$disks_by_iface[2]{FILENAME},
+ "3|disk-3|hdd=s" => \$disks_by_iface[3]{FILENAME})
+ or exit 1;