/*
 *   Copyright (c) 2000 David Parsons. All rights reserved.
 *   
 *   Redistribution and use in source and binary forms, with or without
 *   modification, are permitted provided that the following conditions
 *   are met:
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution.
 *  3. All advertising materials mentioning features or use of this
 *     software must display the following acknowledgement:
 *     
 *   This product includes software developed by David Parsons
 *   (orc@pell.chi.il.us)
 *
 *  4. My name may not be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *     
 *  THIS SOFTWARE IS PROVIDED BY DAVID PARSONS ``AS IS'' AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 *  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 *  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID
 *  PARSONS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 *  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 *  IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 *  THE POSSIBILITY OF SUCH DAMAGE.
 */
/*
 * ifadmin:  tweak network device settings from the command line.
 */
#include <stdio.h>
#include <basis/Nic.H>
extern "C" {
#include <basis/options.h>
#include <basis/validators.h>
#include <db.h>
}

static char *address = 0;
static char *netmask = 0;
static char *broadcast = 0;
static char *gateway = 0;

static int force = 0;
static int mode = 0;
static char auto_yp = 0;


struct x_option options[] = {
    { 'X',   0, "disable",  0,  "Disable this device" },
    { 'M',   0, "manual",   0,  "Manually configure this device" },
    { 'B',   0, "bootp",    0,  "Use bootp to configure this device" },
    { 'D',   0, "dhcp",     0,  "Use dhcp to configure this device" },
    { 'f', 'f', "force",    0, "Configure this interface, even if it doesn't exist" },
    { '?', '?', "help",     0,  "Get this help message" },
};
#define NR_OPTS	(sizeof options/sizeof options[0])


struct x_option prefix[] = {
    { 'f', 'f', "force",    0, "Configure this interface, even if it doesn't exist" },
    { '?', '?', "help",     0,  "Get this help message" },
} ;
#define NR_PREF (sizeof prefix/sizeof prefix[0])


struct x_option manual[] = {
    { 'f', 'f', "force",    0, "Configure this interface, even if it doesn't exist" },
    { 'a', 'a', "address","ADDR[/BITS]", "Set the ip address to ADDR" },
    { 'm', 'm', "netmask","MASK",        "Set the netmask to ADDR" },
    { 'b', 'b', "broadcast", "ADDR",     "Set the broadcast address to ADDR" },
    { 'g', 'g', "gateway",   "ADDR",     "Set the gateway to ADDR" },
    { 'g',  0 , "gw",        "ADDR",     "Synonym for ``gateway''" },
};
#define NR_MAN	(sizeof manual/sizeof manual[0])


struct x_option disable[] = {
    { 'f', 'f', "force",   0, "Configure this interface, even if it doesn't exist" },
};
#define NR_DIS	(sizeof disable/sizeof disable[0])


struct x_option dynamic[] = {
    { 'f', 'f', "force", 0, "Configure this interface, even if it doesn't exist" },
    { 'y', 'y', "auto-yp", 0, "Get Yellow Pages and dns automatically" },
};
#define NR_DYN	(sizeof dynamic/sizeof dynamic[0])


void
usage(int errcode)
{
    fprintf(stderr, "usage: ifadmin device [flags]\n");
    showopts(stderr, NR_OPTS, options);

    fprintf(stderr, "\nOptions for --manual\n");
    showopts(stderr, NR_MAN, manual);

    fprintf(stderr, "\nOptions for --bootp & --dhcp\n");
    showopts(stderr, NR_DYN, dynamic);

    exit(errcode);
}


char *
getaddress(char *addr)
{
    int field = EOF;
    unsigned long mask;
    unsigned long bcast;
    static char nmbuf[80];

    char * msg = isaddrc(addr, &field);

    if (msg) {
	fprintf(stderr, "ifadmin: %s\n", msg);
	exit(1);
    }

    if (field != EOF) {
	char *p = strrchr(addr, '/');

	if (p) *p = 0;
	mask = ~0;

	mask <<= (32-field);

	sprintf(nmbuf, "%u.%u.%u.%u",
		    (mask >> 24) & 0xff,
		    (mask >> 16) & 0xff,
		    (mask >>  8) & 0xff,
		    (mask      ) & 0xff);
	netmask = nmbuf;
    }
    return addr;
}


char *
getquad(char * addr)
{
    char *msg = isquad(addr, 0);

    if (msg) {
	fprintf(stderr, "ifadmin: %s: %s\n", addr, msg);
	exit(1);
    }
    return addr;
}

void
pick_options(int argc, char **argv, int nr_opts, struct x_option opts[])
{
    int opt;

    /* pick up - prefixed options */
    while ( (opt = x_getopt(argc, argv, nr_opts, opts)) != EOF ) {
	switch (opt) {
	case 'X':
	case 'M':
	case 'D':
	case 'B':
		if (mode != 0)
		    usage(1);

		mode = opt;

		if (mode == 'X') {
		    opts = disable;
		    nr_opts = NR_DIS;
		}
		else if (mode == 'M') {
		    opts = manual;
		    nr_opts = NR_MAN;
		}
		else {
		    opts = dynamic;
		    nr_opts = NR_DYN;
		}
		break;
	case 'a':
		address = getaddress(x_optarg);
		break;
	case 'm':
		netmask = getquad(x_optarg);
		break;
	case 'b':
		broadcast = getquad(x_optarg);
		break;
	case 'g':
		gateway = getquad(x_optarg);
		break;
	case 'y':
		auto_yp++;
		break;
	case 'f':
		force = 1;
		break;
	case '?':
	default:
		usage( (opt == '?') ?  0 : 1);
		break;
	}
    }
}

main(int argc, char **argv)
{
    int opt;
    char *dev;

    struct x_option *opts = options;
    int nr_opts = NR_OPTS;

    x_opterr = 1;

    pick_options(argc, argv, NR_PREF, prefix);

    argc -= x_optind;
    argv += x_optind;

    if (argc < 1)
	usage(1);

    Nic nic(dev = argv[0]);

    pick_options(argc, argv, nr_opts, opts);

    /* look for options without a dash */
    for ( ;x_optind < argc; ++x_optind) {
	int i;
	for (i = 0; i < nr_opts; i++)
	    if (opts[i].has_argument && strcasecmp(argv[x_optind],
						   opts[i].name) == 0)
		break;
	if (i < nr_opts)
	    switch (opts[i].optval) {
	    case 'a':
		    address = getaddress(argv[++x_optind]);
		    break;
	    case 'm':
		    netmask = getquad(argv[++x_optind]);
		    break;
	    case 'b':
		    broadcast = getquad(argv[++x_optind]);
		    break;
	    case 'g':
		    gateway = getquad(argv[++x_optind]);
		    break;
	    }
    }

    if ( !(nic.isvalid() || force) ) {
	fprintf(stderr, "device %s does not exist\n", dev);
	exit(1);
    }

    switch (mode) {
    case  0 :   if (nic.isnew()) {
		    fprintf(stderr, "device %s does not exist\n", dev);
		    exit(1);
		}
		else {
		    printf("Configuration for %s\n", dev);
		    nic.print(stdout);
		    exit(0);
		}
		break;

    case 'M':	nic["CONF"] = "MANUAL";
		if (address) nic["ADDRESS"] = address;
		if (netmask) nic["NETMASK"] = netmask;
		if (broadcast) nic["BROADCAST"] = broadcast;
		if (gateway) nic["GATEWAY"] = gateway;
		break;
    case 'B':	nic["CONF"] = "BOOTP";
		nic["SYSTEM"] = "T";
		break;
    case 'D':	nic["CONF"] = "DHCP";
		if (auto_yp)
		    nic["SYSTEM"] = "T";
		else
		    nic.drop("SYSTEM");
		break;
    case 'X':	nic["CONF"] = "DISABLED";
		break;
    }
    nic.commit();
    exit(0);
}
