#!/usr/bin/perl -w

#
# Simple program to prepare an entire disk, safely, to be a FreeBSD volume.
# Derived from phk's prep.fla.sh, but converted to perl
#
# Copyright (c) 2000 M. Warner Losh.  All Rights Reserved.
#
# "THE BEER-WARE LICENSE" (Revision 42): (stolen shamelessly from phk)
# <imp@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
# can do whatever you want with this stuff. If we meet some day, and you think
# this stuff is worth it, you can buy me a beer in return.   Warner Losh.
#

# make sure that we're root.
sub check_root {
    die "You are not root!\n" if $<;
}

# set variables that can be overriden.  They are overriden in files read
# in by -config
sub variables {
    $cyl = -1;
    $hd = -1;
    $sec = -1;

    $size = -1;

    $fsize = 512;
    $bsize = 4096;
    $cpg = 16;

    $hog_part = 'a';
    $all_part = 'c';
    $minfree = 8;
    $opt_perf = "time";

    $part{a}{type} = "4.2BSD";
    $part{c}{type} = "unknown";

    @parts = ("a", "b", "c", "d", "e", "f", "g", "h");
}

# And how do I use this again?
sub usage {
    warn <<USAGE;
usage: diskprep [-config fn] drive
USAGE
    exit 1;
}

# Parse the command line
sub parse {
    local (@argv) = @_;

    while ($_ = $argv[0], /^-/) {
	shift @argv;
	last if /^--$/;
	if    (/^--?(c|config)$/)	{ do $argv[0]; shift @argv }
	else				{ &usage }
    }
    &usage if $#argv < 0;
    $drive = $argv[0];
}

# Inititalize the disk
#   we go the extra mile and dd zeros on the the first 64k.  When we don't
#   do this, we cannot actually boot the disk.
sub init_fdisk {
    local ($dev);

    $dev = "/dev/r${drive}";
    system "/bin/dd if=/dev/zero of=$dev count=128 > /dev/null 2>&1";
    system "/sbin/fdisk -I $drive";
    exit ($?) if $?;
}

# Ask fdisk about the parameters
sub get_params {
    local($junk);

    open FD, "/sbin/fdisk -s $drive |" || exit(1);
    $_ = <FD>;
    chop;
    ($junk, $cyl, $junk, $hd, $junk, $sec, $junk) = split;
    $_ = <FD>;
    chop;
    $_ = <FD>;
    chop;
    ($junk, $junk, $size, $junk, $junk) = split;
    close FD;
}

# Fake up a disklabel based on what we know about the drive.
sub init_disklabel {
    local ($off);

    open DL, "> /tmp/__";
    print DL "# $drive\n";
    $_ = $drive;
    if    (/^fla/)	{ print DL "type: DOC2K\n"; }
    elsif (/^(ad|wd)/)	{ print DL "type: ESDI\n"; }
    elsif (/^(da)/)	{ print DL "type: SCSI\n"; }
    else		{ print DL "type: unknown\n"; }
    print DL "disk: $drive\n";
    print DL "label: mkflash generated\n";
    print DL "flags:\n";
    print DL "bytes/sector: 512\n";
    print DL "sectors/track: $sec\n";
    print DL "tracks/cylinder: $hd\n";
    print DL "sectors/cylinder: ", $sec*$hd, "\n";
    print DL "cylinders: $cyl\n";
    print DL "sectors/unit: $size\n";
    print DL "rpm: 42000\n";
    print DL "interleave: 1\n";
    print DL "trackskew: 0\n";
    print DL "cylinderskew: 0\n";
    print DL "headswitch: 0\n";
    print DL "track-to-track seek: 0\n";
    print DL "drivedata: 0\n";
    print DL "\n";
    print DL "8 partitions:\n";
    
    # Calc the hog size
    $hog_size = $size;
    foreach $p (@parts) {
	next if ($p eq $hog_part); 
	next if ($p eq $all_part); 
	$hog_size -= $part{$p}{size} if (defined($part{$p}));
    }
    $part{$hog_part}{size} = $hog_size;

    # Calc 'c' partition.  It is special.  It is the whole slice
    $part{c}{size} = $size;
    $part{c}{offset} = 0;

    # Calc the rest of the partitions
    $off = 0;
    foreach $p (@parts) {
	next if ($p eq $all_part); 
	if (defined($part{$p})) {
	    $part{$p}{offset} = $off;
	    $off += $part{$p}{size};
	}
    }

    # Dump the partitions
    foreach $p (@parts) {
	if (defined($part{$p})) {
	    print DL "$p: $part{$p}{size} $part{$p}{offset} $part{$p}{type} $fsize $bsize $cpg\n";
	}
    }
    close DL;

    # Label the disk!
    system "/sbin/disklabel -R -r $drive /tmp/__";
    system "/sbin/disklabel -B -r $drive";
    system "/bin/rm /tmp/__";
}

# Ah, what the heck, go ahead and newfs the partitions.
sub newfs {
    local($d);

    foreach $p (@parts) {
	next if ($p eq $all_part); 
	if (defined($part{$p})) {
	    $d = "/dev/r${drive}s1$p";
	    system "/sbin/newfs $d";
	    system "/sbin/tunefs -o $opt_perf -m $minfree $d > /dev/null 2>&1";
	}
    }
}

# Start here
&check_root;
&variables;
&parse(@ARGV);
&init_fdisk;
&get_params;
&init_disklabel;
&newfs;
