OpenSolaris_b135/cmd/vrrpadm/vrrpadm.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <sys/types.h>
#include <sys/varargs.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <errno.h>
#include <locale.h>
#include <libintl.h>
#include <libvrrpadm.h>
#include <ofmt.h>

static vrrp_handle_t	vrrp_vh = NULL;
typedef void cmd_func_t(int, char *[], const char *);

static cmd_func_t do_create, do_delete, do_enable, do_disable,
    do_modify, do_show;

typedef struct {
	char		*c_name;
	cmd_func_t	*c_fn;
	const char	*c_usage;
} cmd_t;

static cmd_t cmds[] = {
	{ "create-router",	do_create,
	    "-V <vrid> -l <link> -A {inet | inet6} [-p <priority>] "
	    "[-i <adv_interval>] [-o <flags>] <router_name>" },
	{ "delete-router",	do_delete,	"<router_name>"		},
	{ "enable-router",	do_enable,	"<router_name>"		},
	{ "disable-router",	do_disable,	"<router_name>"		},
	{ "modify-router",	do_modify,
	    "[-p <priority>] [-i <adv_interval>] [-o <flags>] <router_name>" },
	{ "show-router",	do_show,
	    "[-P | -x] [-o field[,...]] [-p] [<router_name>]"	}
};

static const struct option lopts[] = {
	{"vrid",		required_argument,	0, 'V'},
	{"link",		required_argument,	0, 'l'},
	{"address_family",	required_argument,	0, 'A'},
	{"priority",		required_argument,	0, 'p'},
	{"adv_interval",	required_argument,	0, 'i'},
	{"flags",		required_argument,	0, 'o'},
	{ 0, 0, 0, 0 }
};

static const struct option l_show_opts[] = {
	{"peer",	no_argument,		0, 'P'},
	{"parsable",	no_argument,		0, 'p'},
	{"extended",	no_argument,		0, 'x'},
	{"output",	required_argument,	0, 'o'},
	{ 0, 0, 0, 0 }
};

static ofmt_cb_t sfunc_vrrp_conf;

/*
 * structures for 'dladm show-link -s' (print statistics)
 */
enum {
	ROUTER_NAME,
	ROUTER_VRID,
	ROUTER_LINK,
	ROUTER_VNIC,
	ROUTER_AF,
	ROUTER_PRIO,
	ROUTER_ADV_INTV,
	ROUTER_MODE,
	ROUTER_STATE,
	ROUTER_PRV_STAT,
	ROUTER_STAT_LAST,
	ROUTER_PEER,
	ROUTER_P_PRIO,
	ROUTER_P_INTV,
	ROUTER_P_ADV_LAST,
	ROUTER_M_DOWN_INTV,
	ROUTER_PRIMARY_IP,
	ROUTER_VIRTUAL_IPS,
	ROUTER_VIP_CNT
};

/*
 * structures for 'vrrpadm show-router'
 */
static const ofmt_field_t show_print_fields[] = {
/* name,	field width,	index,			callback */
{ "NAME",		8,	ROUTER_NAME,		sfunc_vrrp_conf	},
{ "VRID",		5,	ROUTER_VRID,		sfunc_vrrp_conf	},
{ "LINK",		8,	ROUTER_LINK,		sfunc_vrrp_conf },
{ "VNIC",		8,	ROUTER_VNIC,		sfunc_vrrp_conf },
{ "AF",			5,	ROUTER_AF,		sfunc_vrrp_conf },
{ "PRIO",		5,	ROUTER_PRIO,		sfunc_vrrp_conf },
{ "ADV_INTV",		9,	ROUTER_ADV_INTV,	sfunc_vrrp_conf },
{ "MODE",		6,	ROUTER_MODE,		sfunc_vrrp_conf	},
{ "STATE",		6,	ROUTER_STATE,		sfunc_vrrp_conf },
{ "PRV_STAT",		9, 	ROUTER_PRV_STAT,	sfunc_vrrp_conf	},
{ "STAT_LAST",		10,	ROUTER_STAT_LAST,	sfunc_vrrp_conf },
{ "PEER",		20,	ROUTER_PEER,		sfunc_vrrp_conf	},
{ "P_PRIO",		7,	ROUTER_P_PRIO,		sfunc_vrrp_conf	},
{ "P_INTV",		9,	ROUTER_P_INTV,		sfunc_vrrp_conf	},
{ "P_ADV_LAST",		11,	ROUTER_P_ADV_LAST,	sfunc_vrrp_conf	},
{ "M_DOWN_INTV",	12,	ROUTER_M_DOWN_INTV,	sfunc_vrrp_conf	},
{ "PRIMARY_IP",		20,	ROUTER_PRIMARY_IP,	sfunc_vrrp_conf	},
{ "VIRTUAL_IPS",	40,	ROUTER_VIRTUAL_IPS,	sfunc_vrrp_conf	},
{ "VIP_CNT",		7,	ROUTER_VIP_CNT,		sfunc_vrrp_conf	},
{ NULL,			0, 	0,			NULL}}
;

static vrrp_err_t do_show_router(const char *, ofmt_handle_t);
static int str2opt(char *opts, uint32_t *, boolean_t *, boolean_t *);
static char *timeval_since_str(int, char *, size_t);

static void usage();
static void warn(const char *, ...);
static void err_exit(const char *, ...);
static void opterr_exit(int, int, const char *);

int
main(int argc, char *argv[])
{
	vrrp_err_t	err;
	int		i;
	cmd_t		*cp;

	(void) setlocale(LC_ALL, "");
	(void) textdomain(TEXT_DOMAIN);

	if (argv[1] == NULL)
		usage();

	if ((err = vrrp_open(&vrrp_vh)) != VRRP_SUCCESS)
		err_exit("operation failed: %s", vrrp_err2str(err));

	for (i = 0; i < sizeof (cmds) / sizeof (cmd_t); i++) {
		cp = &cmds[i];
		if (strcmp(argv[1], cp->c_name) == 0) {
			cp->c_fn(argc - 1, &argv[1], cp->c_usage);
			vrrp_close(vrrp_vh);
			return (EXIT_SUCCESS);
		}
	}

	usage();
	return (EXIT_FAILURE);
}

static void
do_create(int argc, char *argv[], const char *usage)
{
	vrrp_vr_conf_t		conf;
	int			c;
	uint32_t		create_mask = 0, mask;
	char			*endp;
	vrrp_err_t		err;

	/*
	 * default value
	 */
	bzero(&conf, sizeof (vrrp_vr_conf_t));
	conf.vvc_vrid = VRRP_VRID_NONE;
	conf.vvc_af = AF_UNSPEC;
	conf.vvc_pri = VRRP_PRI_DEFAULT;
	conf.vvc_adver_int = VRRP_MAX_ADVER_INT_DFLT;
	conf.vvc_preempt = B_TRUE;
	conf.vvc_accept = B_TRUE;
	conf.vvc_enabled = B_TRUE;

	while ((c = getopt_long(argc, argv, ":V:l:p:i:o:A:f", lopts,
	    NULL)) != EOF) {
		switch (c) {
		case 'l':
			if (strlcpy(conf.vvc_link, optarg,
			    sizeof (conf.vvc_link)) >=
			    sizeof (conf.vvc_link)) {
				err_exit("invalid data-link name %s", optarg);
			}
			break;
		case 'i':
			if (create_mask & VRRP_CONF_INTERVAL)
				err_exit("duplicate '-i' option");

			create_mask |= VRRP_CONF_INTERVAL;
			conf.vvc_adver_int = (uint32_t)strtol(optarg, &endp, 0);
			if ((*endp) != '\0' ||
			    conf.vvc_adver_int < VRRP_MAX_ADVER_INT_MIN ||
			    conf.vvc_adver_int > VRRP_MAX_ADVER_INT_MAX ||
			    (conf.vvc_adver_int == 0 && errno != 0)) {
				err_exit("invalid advertisement interval");
			}
			break;
		case 'p':
			if (create_mask & VRRP_CONF_PRIORITY)
				err_exit("duplicate '-p' option");

			create_mask |= VRRP_CONF_PRIORITY;
			conf.vvc_pri = strtol(optarg, &endp, 0);
			if ((*endp) != '\0' || conf.vvc_pri < VRRP_PRI_MIN ||
			    conf.vvc_pri > VRRP_PRI_OWNER ||
			    (conf.vvc_pri == 0 && errno != 0)) {
				err_exit("invalid priority");
			}
			break;
		case 'o':
			mask = 0;
			if (str2opt(optarg, &mask,
			    &conf.vvc_preempt, &conf.vvc_accept) != 0) {
				err_exit("invalid options: %s", optarg);
			}
			if (mask & create_mask & VRRP_CONF_PREEMPT)
				err_exit("duplicate '-o preempt' option");
			else if (mask & create_mask & VRRP_CONF_ACCEPT)
				err_exit("duplicate '-o accept' option");
			create_mask |= mask;
			break;
		case 'V':
			if (conf.vvc_vrid != VRRP_VRID_NONE)
				err_exit("duplicate '-V' option");

			conf.vvc_vrid = strtol(optarg, &endp, 0);
			if ((*endp) != '\0' || conf.vvc_vrid < VRRP_VRID_MIN ||
			    conf.vvc_vrid > VRRP_VRID_MAX ||
			    (conf.vvc_vrid == 0 && errno != 0)) {
				err_exit("invalid VRID");
			}
			break;
		case 'A':
			if (conf.vvc_af != AF_UNSPEC)
				err_exit("duplicate '-A' option");

			if (strcmp(optarg, "inet") == 0)
				conf.vvc_af = AF_INET;
			else if (strcmp(optarg, "inet6") == 0)
				conf.vvc_af = AF_INET6;
			else
				err_exit("invalid address family");
			break;
		default:
			opterr_exit(optopt, c, usage);
		}
	}

	if (argc - optind > 1)
		err_exit("usage: %s", gettext(usage));

	if (optind != argc - 1)
		err_exit("VRRP name not specified");

	if (strlcpy(conf.vvc_name, argv[optind],
	    sizeof (conf.vvc_name)) >= sizeof (conf.vvc_name)) {
		err_exit("Invalid router name %s", argv[optind]);
	}

	if (conf.vvc_vrid == VRRP_VRID_NONE)
		err_exit("VRID not specified");

	if (conf.vvc_af == AF_UNSPEC)
		err_exit("address family not specified");

	if (strlen(conf.vvc_link) == 0)
		err_exit("link name not specified");

	if (!conf.vvc_accept && conf.vvc_pri == VRRP_PRI_OWNER)
		err_exit("accept_mode must be true for virtual IP owner");

done:
	if ((err = vrrp_create(vrrp_vh, &conf)) == VRRP_SUCCESS)
		return;

	err_exit("create-router failed: %s", vrrp_err2str(err));
}

static void
do_delete(int argc, char *argv[], const char *use)
{
	vrrp_err_t	err;

	if (argc != 2)
		err_exit("usage: %s", gettext(use));

	if ((err = vrrp_delete(vrrp_vh, argv[1])) != VRRP_SUCCESS)
		err_exit("delete-router failed: %s", vrrp_err2str(err));
}

static void
do_enable(int argc, char *argv[], const char *use)
{
	vrrp_err_t	err;

	if (argc != 2)
		err_exit("usage: %s", gettext(use));

	if ((err = vrrp_enable(vrrp_vh, argv[1])) != VRRP_SUCCESS)
		err_exit("enable-router failed: %s", vrrp_err2str(err));
}

static void
do_disable(int argc, char *argv[], const char *use)
{
	vrrp_err_t	err;

	if (argc != 2)
		err_exit("usage: %s", gettext(use));

	if ((err = vrrp_disable(vrrp_vh, argv[1])) != VRRP_SUCCESS)
		err_exit("disable-router failed: %s", vrrp_err2str(err));
}

static void
do_modify(int argc, char *argv[], const char *use)
{
	vrrp_vr_conf_t	conf;
	vrrp_err_t	err;
	uint32_t	modify_mask = 0, mask;
	char		*endp;
	int		c;

	while ((c = getopt_long(argc, argv, ":i:p:o:", lopts, NULL)) != EOF) {
		switch (c) {
		case 'i':
			if (modify_mask & VRRP_CONF_INTERVAL)
				err_exit("duplicate '-i' option");

			modify_mask |= VRRP_CONF_INTERVAL;
			conf.vvc_adver_int = (uint32_t)strtol(optarg, &endp, 0);
			if ((*endp) != '\0' ||
			    conf.vvc_adver_int < VRRP_MAX_ADVER_INT_MIN ||
			    conf.vvc_adver_int > VRRP_MAX_ADVER_INT_MAX ||
			    (conf.vvc_adver_int == 0 && errno != 0)) {
				err_exit("invalid advertisement interval");
			}
			break;
		case 'o':
			mask = 0;
			if (str2opt(optarg, &mask, &conf.vvc_preempt,
			    &conf.vvc_accept) != 0) {
				err_exit("Invalid options");
			}
			if (mask & modify_mask & VRRP_CONF_PREEMPT)
				err_exit("duplicate '-o preempt' option");
			else if (mask & modify_mask & VRRP_CONF_ACCEPT)
				err_exit("duplicate '-o accept' option");
			modify_mask |= mask;
			break;
		case 'p':
			if (modify_mask & VRRP_CONF_PRIORITY)
				err_exit("duplicate '-p' option");

			modify_mask |= VRRP_CONF_PRIORITY;
			conf.vvc_pri = strtol(optarg, &endp, 0);
			if ((*endp) != '\0' || conf.vvc_pri < VRRP_PRI_MIN ||
			    conf.vvc_pri > VRRP_PRI_OWNER ||
			    (conf.vvc_pri == 0 && errno != 0)) {
				err_exit("invalid priority");
			}
			break;
		default:
			opterr_exit(optopt, c, use);
		}
	}

	if (argc - optind > 1)
		err_exit("usage: %s", gettext(use));

	if (optind != argc - 1)
		err_exit("VRRP name not specified.");

	if (strlcpy(conf.vvc_name, argv[optind], sizeof (conf.vvc_name)) >=
	    sizeof (conf.vvc_name)) {
		err_exit("invalid router name %s", argv[optind]);
	}

	if ((modify_mask & VRRP_CONF_ACCEPT) && !conf.vvc_accept &&
	    (modify_mask & VRRP_CONF_PRIORITY) &&
	    conf.vvc_pri == VRRP_PRI_OWNER) {
		err_exit("accept_mode must be true for virtual IP owner");
	}

	if (modify_mask == 0)
		usage();

	err = vrrp_modify(vrrp_vh, &conf, modify_mask);
	if (err != VRRP_SUCCESS)
		err_exit("modify-router failed: %s", vrrp_err2str(err));
}

/*
 * 'show-router' one VRRP router.
 */
static vrrp_err_t
do_show_router(const char *vn, ofmt_handle_t ofmt)
{
	vrrp_queryinfo_t	*vq;
	vrrp_err_t		err;

	if ((err = vrrp_query(vrrp_vh, vn, &vq)) != VRRP_SUCCESS)
		return (err);

	ofmt_print(ofmt, vq);
	free(vq);
	return (VRRP_SUCCESS);
}

static void
do_show(int argc, char *argv[], const char *use)
{
	int			c;
	char			*fields_str = NULL;
	char			*names = NULL, *router;
	uint32_t		i, in_cnt = 0, out_cnt;
	ofmt_status_t		oferr;
	ofmt_handle_t		ofmt;
	uint_t			ofmt_flags = 0;
	vrrp_err_t		err = VRRP_SUCCESS;
	boolean_t		P_opt, x_opt;

	static char		*dft_fields_str =
	    "NAME,VRID,LINK,AF,PRIO,ADV_INTV,MODE,STATE,VNIC";
	static char		*ext_fields_str =
	    "NAME,STATE,PRV_STAT,STAT_LAST,VNIC,PRIMARY_IP,VIRTUAL_IPS";
	static char		*peer_fields_str =
	    "NAME,PEER,P_PRIO,P_INTV,P_ADV_LAST,M_DOWN_INTV";
	/*
	 * If parsable output is requested, add VIP_CNT into the output
	 * for extended output. It is not needed for human-readable
	 * output as it is obvious from the VIRTUAL_IPS list.
	 */
	static char		*ext_parsable_fields_str =
	    "NAME,STATE,PRV_STAT,STAT_LAST,VNIC,PRIMARY_IP,VIP_CNT,"
	    "VIRTUAL_IPS";

	P_opt = x_opt = B_FALSE;
	fields_str = dft_fields_str;
	while ((c = getopt_long(argc, argv, ":Pxpo:", l_show_opts,
	    NULL)) != EOF) {
		switch (c) {
		case 'o':
			fields_str = optarg;
			break;
		case 'p':
			ofmt_flags |= OFMT_PARSABLE;
			break;
		case 'P':
			P_opt = B_TRUE;
			fields_str = peer_fields_str;
			break;
		case 'x':
			x_opt = B_TRUE;
			fields_str = ext_fields_str;
			break;
		default:
			opterr_exit(optopt, c, use);
		}
	}

	if (x_opt && P_opt)
		err_exit("incompatible -P and -x options");

	/*
	 * If parsable output is requested, add VIP_CNT into the output
	 * for extended output.
	 */
	if ((ofmt_flags & OFMT_PARSABLE) && (fields_str == ext_fields_str))
		fields_str = ext_parsable_fields_str;

	if ((oferr = ofmt_open(fields_str, show_print_fields, ofmt_flags,
	    0, &ofmt)) != OFMT_SUCCESS) {
		char buf[OFMT_BUFSIZE];

		/*
		 * If some fields were badly formed in human-friendly mode, we
		 * emit a warning and continue.  Otherwise exit immediately.
		 */
		(void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf));
		if (oferr != OFMT_EBADFIELDS || (ofmt_flags & OFMT_PARSABLE)) {
			ofmt_close(ofmt);
			err_exit(buf);
		} else {
			warn(buf);
		}
	}

	/* Show one router */
	if (optind == argc - 1) {
		err = do_show_router(argv[optind], ofmt);
		goto done;
	}

	/*
	 * Show all routers. First set in_cnt to 0 to find out the number
	 * of vrrp routers.
	 */
again:
	if ((in_cnt != 0) && (names = malloc(in_cnt * VRRP_NAME_MAX)) == NULL) {
		err = VRRP_ENOMEM;
		goto done;
	}

	out_cnt = in_cnt;
	if ((err = vrrp_list(vrrp_vh, VRRP_VRID_NONE, NULL, AF_UNSPEC,
	    &out_cnt, names)) != VRRP_SUCCESS) {
		free(names);
		goto done;
	}

	/*
	 * The VRRP routers has been changed between two vrrp_list()
	 * calls, try again.
	 */
	if (out_cnt > in_cnt) {
		in_cnt = out_cnt;
		free(names);
		goto again;
	}

	/*
	 * Each VRRP router name is separated by '\0`
	 */
	router = names;
	for (i = 0; i < in_cnt; i++) {
		(void) do_show_router(router, ofmt);
		router += strlen(router) + 1;
	}

	free(names);

done:
	ofmt_close(ofmt);

	if (err != VRRP_SUCCESS)
		err_exit(vrrp_err2str(err));
}

/*
 * Callback function to print fields of the configuration information.
 */
static boolean_t
sfunc_vrrp_conf(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
{
	vrrp_queryinfo_t	*qinfo = ofmtarg->ofmt_cbarg;
	uint_t			ofmtid = ofmtarg->ofmt_id;
	vrrp_vr_conf_t		*conf = &qinfo->show_vi;
	vrrp_stateinfo_t	*sinfo = &qinfo->show_vs;
	vrrp_peer_t		*peer = &qinfo->show_vp;
	vrrp_timerinfo_t	*tinfo = &qinfo->show_vt;
	vrrp_addrinfo_t		*ainfo = &qinfo->show_va;

	switch (ofmtid) {
	case ROUTER_NAME:
		(void) snprintf(buf, bufsize, "%s", conf->vvc_name);
		break;
	case ROUTER_VRID:
		(void) snprintf(buf, bufsize, "%d", conf->vvc_vrid);
		break;
	case ROUTER_LINK:
		(void) snprintf(buf, bufsize, "%s", conf->vvc_link);
		break;
	case ROUTER_AF:
		(void) snprintf(buf, bufsize, "IPv%d",
		    conf->vvc_af == AF_INET ? 4 : 6);
		break;
	case ROUTER_PRIO:
		(void) snprintf(buf, bufsize, "%d", conf->vvc_pri);
		break;
	case ROUTER_ADV_INTV:
		(void) snprintf(buf, bufsize, "%d", conf->vvc_adver_int);
		break;
	case ROUTER_MODE:
		(void) strlcpy(buf, "-----", bufsize);
		if (conf->vvc_enabled)
			buf[0] = 'e';
		if (conf->vvc_pri == VRRP_PRI_OWNER)
			buf[1] = 'o';
		if (conf->vvc_preempt)
			buf[2] = 'p';
		if (conf->vvc_accept)
			buf[3] = 'a';
		break;
	case ROUTER_STATE:
		(void) snprintf(buf, bufsize, "%s",
		    vrrp_state2str(sinfo->vs_state));
		break;
	case ROUTER_PRV_STAT:
		(void) snprintf(buf, bufsize, "%s",
		    vrrp_state2str(sinfo->vs_prev_state));
		break;
	case ROUTER_STAT_LAST:
		(void) timeval_since_str(tinfo->vt_since_last_tran, buf,
		    bufsize);
		break;
	case ROUTER_PEER:
		/* LINTED E_CONSTANT_CONDITION */
		VRRPADDR2STR(conf->vvc_af, &peer->vp_addr,
		    buf, bufsize, B_FALSE);
		break;
	case ROUTER_P_PRIO:
		(void) snprintf(buf, bufsize, "%d", peer->vp_prio);
		break;
	case ROUTER_P_INTV:
		(void) snprintf(buf, bufsize, "%d", peer->vp_adver_int);
		break;
	case ROUTER_P_ADV_LAST:
		(void) timeval_since_str(tinfo->vt_since_last_adv, buf,
		    bufsize);
		break;
	case ROUTER_M_DOWN_INTV:
		(void) snprintf(buf, bufsize, "%d", tinfo->vt_master_down_intv);
		break;
	case ROUTER_VNIC:
		(void) snprintf(buf, bufsize, "%s",
		    strlen(ainfo->va_vnic) == 0 ? "--" : ainfo->va_vnic);
		break;
	case ROUTER_PRIMARY_IP:
		/* LINTED E_CONSTANT_CONDITION */
		VRRPADDR2STR(conf->vvc_af, &ainfo->va_primary,
		    buf, bufsize, B_FALSE);
		break;
	case ROUTER_VIRTUAL_IPS: {
		uint32_t i;

		for (i = 0; i < ainfo->va_vipcnt; i++) {
			/* LINTED E_CONSTANT_CONDITION */
			VRRPADDR2STR(conf->vvc_af, &(ainfo->va_vips[i]),
			    buf, bufsize, B_TRUE);
			if (i != ainfo->va_vipcnt - 1)
				(void) strlcat(buf, ",", bufsize);
		}
		break;
	}
	case ROUTER_VIP_CNT:
		(void) snprintf(buf, bufsize, "%d", ainfo->va_vipcnt);
		break;
	default:
		return (B_FALSE);
	}

	return (B_TRUE);
}

static void
usage()
{
	int	i;
	cmd_t	*cp;

	(void) fprintf(stderr, "%s",
	    gettext("usage:  vrrpadm <sub-command> <args> ...\n"));

	for (i = 0; i < sizeof (cmds) / sizeof (cmd_t); i++) {
		cp = &cmds[i];
		if (cp->c_usage != NULL)
			(void) fprintf(stderr, "          %-10s %s\n",
			    gettext(cp->c_name), gettext(cp->c_usage));
	}

	vrrp_close(vrrp_vh);
	exit(EXIT_FAILURE);
}

static void
warn(const char *format, ...)
{
	va_list alist;

	format = gettext(format);
	(void) fprintf(stderr, gettext("warning: "));

	va_start(alist, format);
	(void) vfprintf(stderr, format, alist);
	va_end(alist);
	(void) putc('\n', stderr);
}

static void
err_exit(const char *format, ...)
{
	va_list alist;

	format = gettext(format);
	va_start(alist, format);
	(void) vfprintf(stderr, format, alist);
	va_end(alist);
	(void) putc('\n', stderr);
	vrrp_close(vrrp_vh);
	exit(EXIT_FAILURE);
}

static void
opterr_exit(int opt, int opterr, const char *use)
{
	switch (opterr) {
	case ':':
		err_exit("option '-%c' requires a value\nusage: %s", opt,
		    gettext(use));
		break;
	case '?':
	default:
		err_exit("unrecognized option '-%c'\nusage: %s", opt,
		    gettext(use));
		break;
	}
}

static char *
timeval_since_str(int mill, char *str, size_t len)
{
	int	sec, msec, min;

	msec = mill % 1000;
	sec = mill / 1000;
	min = sec > 60 ? sec / 60 : 0;
	sec %= 60;

	if (min > 0)
		(void) snprintf(str, len, "%4dm%2ds", min, sec);
	else
		(void) snprintf(str, len, "%4d.%03ds", sec, msec);

	return (str);
}

/*
 * Parses options string. The values of the two options will be returned
 * by 'preempt' and 'accept', and the mask 'modify_mask' will be updated
 * accordingly.
 *
 * Returns 0 on success, errno on failures.
 *
 * Used by do_create() and do_modify().
 *
 * Note that "opts" could be modified internally in this function.
 */
static int
str2opt(char *opts, uint32_t *modify_mask, boolean_t *preempt,
    boolean_t *accept)
{
	char		*value;
	int		opt;
	uint32_t	mask = 0;
	enum { o_preempt = 0, o_un_preempt, o_accept, o_no_accept };
	static char	*myopts[] = {
		"preempt",
		"un_preempt",
		"accept",
		"no_accept",
		NULL
	};

	while (*opts != '\0') {
		switch ((opt = getsubopt(&opts, myopts, &value))) {
		case o_preempt:
		case o_un_preempt:
			if (mask & VRRP_CONF_PREEMPT)
				return (EINVAL);

			mask |= VRRP_CONF_PREEMPT;
			*preempt = (opt == o_preempt);
			break;
		case o_accept:
		case o_no_accept:
			if (mask & VRRP_CONF_ACCEPT)
				return (EINVAL);

			mask |= VRRP_CONF_ACCEPT;
			*accept = (opt == o_accept);
			break;
		default:
			return (EINVAL);
		}
	}

	*modify_mask |= mask;
	return (0);
}