/*
* 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);
}