root/usr/src/cmd/cmd-inet/usr.sbin/ilbadm/ilbadm_sg.c
/*
 * 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.
 * Copyright 2012 Milan Jurik. All rights reserved.
 */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <strings.h>
#include <stddef.h>
#include <assert.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/list.h>
#include <ofmt.h>
#include <libilb.h>
#include "ilbadm.h"

static ilbadm_key_name_t servrange_keys[] = {
        {ILB_KEY_SERVER, "server", "servers"},
        {ILB_KEY_SERVRANGE, "server", "servers"},
        {ILB_KEY_BAD, "", ""}
};

static ilbadm_key_name_t serverID_keys[] = {
        {ILB_KEY_SERVERID, "server", ""},
        {ILB_KEY_BAD, "", ""}
};

typedef struct sg_export_arg {
        FILE            *fp;
        ilbadm_sgroup_t *sg;
} sg_export_arg_t;

typedef struct arg_struct {
        int             flags;
        char            *o_str;
        ofmt_field_t    *o_fields;
        ofmt_handle_t   oh;
} list_arg_t;

typedef struct sg_srv_o_struct {
        char            *sgname;
        ilb_server_data_t       *sd;
} sg_srv_o_arg_t;

static ofmt_cb_t of_sgname;
static ofmt_cb_t of_srvID;
static ofmt_cb_t of_port;
static ofmt_cb_t of_ip;

static ofmt_field_t sgfields_v4[] = {
        {"SGNAME", ILB_SGNAME_SZ, 0, of_sgname},
        {"SERVERID", ILB_NAMESZ, 0, of_srvID},
        {"MINPORT", 8, 0, of_port},
        {"MAXPORT", 8, 1, of_port},
        {"IP_ADDRESS", 15, 0, of_ip},
        {NULL, 0, 0, NULL}
};
static ofmt_field_t sgfields_v6[] = {
        {"SGNAME", ILB_SGNAME_SZ, 0, of_sgname},
        {"SERVERID", ILB_NAMESZ, 0, of_srvID},
        {"MINPORT", 8, 0, of_port},
        {"MAXPORT", 8, 1, of_port},
        {"IP_ADDRESS", 39, 0, of_ip},
        {NULL, 0, 0, NULL}
};

#define MAXCOLS 80 /* make flexible? */

extern int      optind, optopt, opterr;
extern char     *optarg;

static boolean_t
of_sgname(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
{
        sg_srv_o_arg_t  *l = (sg_srv_o_arg_t *)of_arg->ofmt_cbarg;

        (void) strlcpy(buf, l->sgname, bufsize);
        return (B_TRUE);
}

static boolean_t
of_srvID(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
{
        sg_srv_o_arg_t  *l = (sg_srv_o_arg_t *)of_arg->ofmt_cbarg;

        (void) strlcpy(buf, l->sd->sd_srvID, bufsize);
        return (B_TRUE);
}

static boolean_t
of_port(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
{
        sg_srv_o_arg_t  *l = (sg_srv_o_arg_t *)of_arg->ofmt_cbarg;
        int             port;

        if (of_arg->ofmt_id == 0) {
                port = ntohs(l->sd->sd_minport);
                if (port == 0)
                        *buf = '\0';
                else
                        (void) snprintf(buf, bufsize, "%d", port);
        } else {
                port = ntohs(l->sd->sd_maxport);
                if (port == 0)
                        *buf = '\0';
                else
                        (void) snprintf(buf, bufsize, "%d", port);
        }
        return (B_TRUE);
}

static boolean_t
of_ip(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
{
        sg_srv_o_arg_t  *l = (sg_srv_o_arg_t *)of_arg->ofmt_cbarg;

        ip2str(&l->sd->sd_addr, buf, bufsize, V6_ADDRONLY);
        return (B_TRUE);
}

ilbadm_status_t
i_list_sg_srv_ofmt(char *sgname, ilb_server_data_t *sd, void *arg)
{
        list_arg_t      *larg = (list_arg_t *)arg;
        sg_srv_o_arg_t  line_arg;

        line_arg.sgname = sgname;
        line_arg.sd = sd;
        ofmt_print(larg->oh, &line_arg);
        return (ILBADM_OK);
}

/*
 * This function is always called via ilb_walk_servergroups()
 * and so must return libilb errors.
 * That's why we need to retain currently unused "h" argument
 */
/* ARGSUSED */
static ilb_status_t
ilbadm_list_sg_srv(ilb_handle_t h, ilb_server_data_t *sd, const char *sgname,
    void *arg)
{
        char            ip_str[2*INET6_ADDRSTRLEN + 3] = "";
        char            port_str[INET6_ADDRSTRLEN];
        list_arg_t      *larg = (list_arg_t *)arg;
        ofmt_status_t   oerr;
        int             oflags = 0;
        int             ocols = MAXCOLS;
        int             h_minport, h_maxport;
        static ofmt_handle_t    oh = (ofmt_handle_t)NULL;
        ofmt_field_t    *ofp;

        if (larg->o_str != NULL) {
                if (oh == NULL) {
                        if (sd->sd_addr.ia_af == AF_INET)
                                ofp = sgfields_v6;
                        else
                                ofp = sgfields_v4;

                        if (larg->flags & ILBADM_LIST_PARSE)
                                oflags |= OFMT_PARSABLE;

                        oerr = ofmt_open(larg->o_str, ofp, oflags, ocols, &oh);
                        if (oerr != OFMT_SUCCESS) {
                                char    e[80];

                                ilbadm_err(gettext("ofmt_open failed: %s"),
                                    ofmt_strerror(oh, oerr, e, sizeof (e)));
                                return (ILB_STATUS_GENERIC);
                        }
                        larg->oh = oh;
                }


                (void) i_list_sg_srv_ofmt((char *)sgname, sd, arg);
                return (ILB_STATUS_OK);
        }

        ip2str(&sd->sd_addr, ip_str, sizeof (ip_str), 0);

        h_minport = ntohs(sd->sd_minport);
        h_maxport = ntohs(sd->sd_maxport);
        if (h_minport == 0)
                *port_str = '\0';
        else if (h_maxport > h_minport)
                (void) sprintf(port_str, ":%d-%d", h_minport, h_maxport);
        else
                (void) sprintf(port_str, ":%d", h_minport);

        (void) printf("%s: id:%s %s%s\n", sgname,
            sd->sd_srvID, ip_str, port_str);
        return (ILB_STATUS_OK);
}

ilb_status_t
ilbadm_list_sg(ilb_handle_t h, ilb_sg_data_t *sg, void *arg)
{
        if (sg->sgd_srvcount == 0) {
                ilb_server_data_t       tmp_srv;

                bzero(&tmp_srv, sizeof (tmp_srv));
                return (ilbadm_list_sg_srv(h, &tmp_srv, sg->sgd_name, arg));
        }

        return (ilb_walk_servers(h, ilbadm_list_sg_srv, sg->sgd_name, arg));
}

static char *def_fields = "SGNAME,SERVERID,MINPORT,MAXPORT,IP_ADDRESS";

/* ARGSUSED */
ilbadm_status_t
ilbadm_show_servergroups(int argc, char *argv[])
{
        ilb_handle_t    h = ILB_INVALID_HANDLE;
        ilb_status_t    rclib = ILB_STATUS_OK;
        ilbadm_status_t rc = ILBADM_OK;
        int             c;
        char            optstr[] = ":po:";

        boolean_t       o_opt = B_FALSE, p_opt = B_FALSE;
        list_arg_t      larg = {0, def_fields, NULL, NULL};

        while ((c = getopt(argc, argv, optstr)) != -1) {
                switch ((char)c) {
                case 'p': p_opt = B_TRUE;
                        larg.flags |= ILBADM_LIST_PARSE;
                        break;
                case 'o': larg.o_str = optarg;
                        o_opt = B_TRUE;
                        break;
                case ':': ilbadm_err(gettext("missing option argument"
                            " for %c"), (char)optopt);
                        rc = ILBADM_LIBERR;
                        goto out;
                default: unknown_opt(argv, optind-1);
                        /* not reached */
                        break;
                }
        }

        if (p_opt && !o_opt) {
                ilbadm_err(gettext("option -p requires -o"));
                exit(1);
        }

        if (p_opt && larg.o_str != NULL &&
            (strcasecmp(larg.o_str, "all") == 0)) {
                ilbadm_err(gettext("option -p requires explicit field"
                    " names for -o"));
                exit(1);
        }

        rclib = ilb_open(&h);
        if (rclib != ILB_STATUS_OK)
                goto out;

        if (optind >= argc) {
                rclib = ilb_walk_servergroups(h, ilbadm_list_sg, NULL,
                    (void*)&larg);
                if (rclib != ILB_STATUS_OK)
                        rc = ILBADM_LIBERR;
        } else {
                while (optind < argc) {
                        rclib = ilb_walk_servergroups(h, ilbadm_list_sg,
                            argv[optind++], (void*)&larg);
                        if (rclib != ILB_STATUS_OK) {
                                rc = ILBADM_LIBERR;
                                break;
                        }
                }
        }

        if (larg.oh != NULL)
                ofmt_close(larg.oh);
out:
        if (h != ILB_INVALID_HANDLE)
                (void) ilb_close(h);

        if (rclib != ILB_STATUS_OK) {
                /*
                 * The show function returns ILB_STATUS_GENERIC after printing
                 * out an error message.  So we don't need to print it again.
                 */
                if (rclib != ILB_STATUS_GENERIC)
                        ilbadm_err(ilb_errstr(rclib));
                rc = ILBADM_LIBERR;
        }

        return (rc);
}

ilbadm_servnode_t *
i_new_sg_elem(ilbadm_sgroup_t *sgp)
{
        ilbadm_servnode_t *s;

        s = (ilbadm_servnode_t *)calloc(sizeof (*s), 1);
        if (s != NULL) {
                list_insert_tail(&sgp->sg_serv_list, s);
                sgp->sg_count++;
        }
        return (s);
}

static ilbadm_status_t
i_parse_servrange_list(char *arg, ilbadm_sgroup_t *sgp)
{
        ilbadm_status_t rc;
        int             count;

        rc = i_parse_optstring(arg, (void *) sgp, servrange_keys,
            OPT_VALUE_LIST|OPT_IP_RANGE|OPT_PORTS, &count);
        return (rc);
}

static ilbadm_status_t
i_parse_serverIDs(char *arg, ilbadm_sgroup_t *sgp)
{
        ilbadm_status_t rc;
        int             count;

        rc = i_parse_optstring(arg, (void *) sgp, serverID_keys,
            OPT_VALUE_LIST|OPT_PORTS, &count);
        return (rc);
}

static ilbadm_status_t
i_mod_sg(ilb_handle_t h, ilbadm_sgroup_t *sgp, ilbadm_cmd_t cmd,
    int flags)
{
        ilbadm_servnode_t       *sn;
        ilb_server_data_t       *srv;
        ilb_status_t            rclib = ILB_STATUS_OK;
        ilbadm_status_t         rc = ILBADM_OK;

        if (h == ILB_INVALID_HANDLE && cmd != cmd_enable_server &&
            cmd != cmd_disable_server)
                return (ILBADM_LIBERR);

        sn = list_head(&sgp->sg_serv_list);
        while (sn != NULL) {
                srv = &sn->s_spec;

                srv->sd_flags |= flags;
                if (cmd == cmd_create_sg || cmd == cmd_add_srv) {
                        rclib = ilb_add_server_to_group(h, sgp->sg_name,
                            srv);
                        if (rclib != ILB_STATUS_OK) {
                                char    buf[INET6_ADDRSTRLEN + 1];

                                rc = ILBADM_LIBERR;
                                ip2str(&srv->sd_addr, buf, sizeof (buf),
                                    V6_ADDRONLY);
                                ilbadm_err(gettext("cannot add %s to %s: %s"),
                                    buf, sgp->sg_name, ilb_errstr(rclib));
                                /* if we created the SG, we bail out */
                                if (cmd == cmd_create_sg)
                                        return (rc);
                        }
                } else {
                        assert(cmd == cmd_rem_srv);
                        rclib = ilb_rem_server_from_group(h, sgp->sg_name,
                            srv);
                        /* if we fail, we tell user and continue */
                        if (rclib != ILB_STATUS_OK) {
                                rc = ILBADM_LIBERR;
                                ilbadm_err(
                                    gettext("cannot remove %s from %s: %s"),
                                    srv->sd_srvID, sgp->sg_name,
                                    ilb_errstr(rclib));
                        }
                }

                /*
                 * list_next returns NULL instead of cycling back to head
                 * so we don't have to check for list_head explicitly.
                 */
                sn = list_next(&sgp->sg_serv_list, sn);
        };

        return (rc);
}

static void
i_ilbadm_alloc_sgroup(ilbadm_sgroup_t **sgp)
{
        ilbadm_sgroup_t *sg;

        *sgp = sg = (ilbadm_sgroup_t *)calloc(sizeof (*sg), 1);
        if (sg == NULL)
                return;
        list_create(&sg->sg_serv_list, sizeof (ilbadm_servnode_t),
            offsetof(ilbadm_servnode_t, s_link));
}

static void
i_ilbadm_free_sgroup(ilbadm_sgroup_t *sg)
{
        ilbadm_servnode_t       *s;

        while ((s = list_remove_head(&sg->sg_serv_list)) != NULL)
                free(s);

        list_destroy(&sg->sg_serv_list);
}

ilbadm_status_t
ilbadm_create_servergroup(int argc, char *argv[])
{
        ilb_handle_t    h = ILB_INVALID_HANDLE;
        ilb_status_t    rclib = ILB_STATUS_OK;
        ilbadm_status_t rc = ILBADM_OK;
        ilbadm_sgroup_t *sg;
        int             c;
        int             flags = 0;

        i_ilbadm_alloc_sgroup(&sg);

        while ((c = getopt(argc, argv, ":s:")) != -1) {
                switch ((char)c) {
                case 's':
                        rc = i_parse_servrange_list(optarg, sg);
                        break;
                case ':':
                        ilbadm_err(gettext("missing option-argument for"
                            " %c"), (char)optopt);
                        rc = ILBADM_LIBERR;
                        break;
                case '?':
                default:
                        unknown_opt(argv, optind-1);
                        /* not reached */
                        break;
                }

                if (rc != ILBADM_OK)
                        goto out;
        }

        if (optind >= argc) {
                ilbadm_err(gettext("missing mandatory arguments - please refer"
                    " to 'create-servergroup' subcommand"
                    "  description in ilbadm(8)"));
                rc = ILBADM_LIBERR;
                goto out;
        }

        if (strlen(argv[optind]) > ILB_SGNAME_SZ - 1) {
                ilbadm_err(gettext("servergroup name %s is too long -"
                    " must not exceed %d chars"), argv[optind],
                    ILB_SGNAME_SZ - 1);
                rc = ILBADM_LIBERR;
                goto out;
        }

        sg->sg_name = argv[optind];

        rclib = ilb_open(&h);
        if (rclib != ILB_STATUS_OK)
                goto out;

        rclib = ilb_create_servergroup(h, sg->sg_name);
        if (rclib != ILB_STATUS_OK)
                goto out;

        /* we create a servergroup with all servers enabled */
        ILB_SET_ENABLED(flags);
        rc = i_mod_sg(h, sg, cmd_create_sg, flags);

        if (rc != ILBADM_OK)
                (void) ilb_destroy_servergroup(h, sg->sg_name);

out:
        i_ilbadm_free_sgroup(sg);
        if (h != ILB_INVALID_HANDLE)
                (void) ilb_close(h);

        if (rclib != ILB_STATUS_OK) {
                ilbadm_err(ilb_errstr(rclib));
                rc = ILBADM_LIBERR;
        }
        if ((rc != ILBADM_OK) && (rc != ILBADM_LIBERR))
                ilbadm_err(ilbadm_errstr(rc));

        return (rc);
}

ilbadm_status_t
ilbadm_add_server_to_group(int argc, char **argv)
{
        ilb_handle_t    h = ILB_INVALID_HANDLE;
        ilb_status_t    rclib = ILB_STATUS_OK;
        ilbadm_status_t rc = ILBADM_OK;
        ilbadm_sgroup_t *sg;
        int             c;
        int             flags = 0;

        i_ilbadm_alloc_sgroup(&sg);

        while ((c = getopt(argc, argv, ":s:")) != -1) {
                switch ((char)c) {
                case 's':
                        rc = i_parse_servrange_list(optarg, sg);
                        break;
                case ':':
                        ilbadm_err(gettext("missing option-argument for"
                            " %c"), (char)optopt);
                        rc = ILBADM_LIBERR;
                        break;
                case '?':
                default: unknown_opt(argv, optind-1);
                        /* not reached */
                        break;
                }

                if (rc != ILBADM_OK)
                        goto out;
        }

        if (optind >= argc) {
                ilbadm_err(gettext("missing mandatory arguments - please refer"
                    " to 'add-server' subcommand description in ilbadm(8)"));
                rc = ILBADM_LIBERR;
                goto out;
        }

        sg->sg_name = argv[optind];

        rclib = ilb_open(&h);
        if (rclib != ILB_STATUS_OK)
                goto out;

        /* A server is added enabled */
        ILB_SET_ENABLED(flags);
        rc = i_mod_sg(h, sg, cmd_add_srv, flags);
out:
        i_ilbadm_free_sgroup(sg);
        if (h != ILB_INVALID_HANDLE)
                (void) ilb_close(h);

        if ((rc != ILBADM_OK) && (rc != ILBADM_LIBERR))
                ilbadm_err(ilbadm_errstr(rc));
        return (rc);
}

/* ARGSUSED */
static ilbadm_status_t
ilbadm_Xable_server(int argc, char *argv[], ilbadm_cmd_t cmd)
{
        ilb_handle_t            h = ILB_INVALID_HANDLE;
        ilbadm_status_t         rc = ILBADM_OK;
        ilb_status_t            rclib = ILB_STATUS_OK;
        int                     i;

        if (argc < 2) {
                ilbadm_err(gettext("missing required argument"
                    " (server specification)"));
                rc = ILBADM_LIBERR;
                goto out;
        }

        rclib = ilb_open(&h);
        if (rclib != ILB_STATUS_OK)
                goto out;

        /* enable-server and disable-server only accepts serverids */
        for (i = 1; i < argc && rclib == ILB_STATUS_OK; i++) {
                ilb_server_data_t       srv;

                if (argv[i][0] != ILB_SRVID_PREFIX) {
                        rc = ILBADM_INVAL_SRVID;
                        goto out;
                }

                bzero(&srv, sizeof (srv));
                /* to do: check length */
                (void) strlcpy(srv.sd_srvID, argv[i], sizeof (srv.sd_srvID));
                switch (cmd) {
                case cmd_enable_server:
                        rclib = ilb_enable_server(h, &srv, NULL);
                        break;
                case cmd_disable_server:
                        rclib = ilb_disable_server(h, &srv, NULL);
                        break;
                }

                /* if we can't find a given server ID, just plough on */
                if (rclib == ILB_STATUS_ENOENT) {
                        const char *msg = ilb_errstr(rclib);

                        rc = ILBADM_LIBERR;
                        ilbadm_err("%s: %s", msg, argv[i]);
                        rclib = ILB_STATUS_OK;
                        continue;
                }
                if (rclib != ILB_STATUS_OK)
                        break;
        }
out:
        if (h != ILB_INVALID_HANDLE)
                (void) ilb_close(h);

        if (rclib != ILB_STATUS_OK) {
                ilbadm_err(ilb_errstr(rclib));
                rc = ILBADM_LIBERR;
        }

        if ((rc != ILBADM_OK) && (rc != ILBADM_LIBERR))
                ilbadm_err(ilbadm_errstr(rc));
        return (rc);
}

ilbadm_status_t
ilbadm_disable_server(int argc, char *argv[])
{
        return (ilbadm_Xable_server(argc, argv, cmd_disable_server));
}

ilbadm_status_t
ilbadm_enable_server(int argc, char *argv[])
{
        return (ilbadm_Xable_server(argc, argv, cmd_enable_server));
}

/* ARGSUSED */
ilbadm_status_t
ilbadm_rem_server_from_group(int argc, char *argv[])
{
        ilb_handle_t    h = ILB_INVALID_HANDLE;
        ilb_status_t    rclib = ILB_STATUS_OK;
        ilbadm_status_t rc = ILBADM_OK;
        ilbadm_sgroup_t *sg;
        int             c;

        i_ilbadm_alloc_sgroup(&sg);

        while ((c = getopt(argc, argv, ":s:")) != -1) {
                switch ((char)c) {
                case 's':
                        rc = i_parse_serverIDs(optarg, sg);
                        break;
                case ':':
                        ilbadm_err(gettext("missing option-argument for"
                            " %c"), (char)optopt);
                        rc = ILBADM_LIBERR;
                        break;
                case '?':
                default: unknown_opt(argv, optind-1);
                        /* not reached */
                        break;
                }
                if (rc != ILBADM_OK)
                        goto out;
        }

        /* we need servergroup name and at least one serverID to remove */
        if (optind >= argc || sg->sg_count == 0) {
                rc = ILBADM_ENOOPTION;
                goto out;
        }

        sg->sg_name = argv[optind];

        rclib = ilb_open(&h);
        if (rclib != ILB_STATUS_OK)
                goto out;

        rc = i_mod_sg(h, sg, cmd_rem_srv, 0);
out:
        i_ilbadm_free_sgroup(sg);

        if (h != ILB_INVALID_HANDLE)
                (void) ilb_close(h);
        if ((rc != ILBADM_OK) && (rc != ILBADM_LIBERR))
                ilbadm_err(ilbadm_errstr(rc));
        return (rc);
}

ilbadm_status_t
ilbadm_destroy_servergroup(int argc, char *argv[])
{
        ilb_handle_t    h = ILB_INVALID_HANDLE;
        ilb_status_t    rclib = ILB_STATUS_OK;
        ilbadm_status_t rc = ILBADM_OK;
        char            *sgname;

        if (argc != 2) {
                ilbadm_err(gettext("usage:ilbadm"
                    " delete-servergroup groupname"));
                rc = ILBADM_LIBERR;
                goto out;
        }

        sgname = argv[1];

        rclib = ilb_open(&h);
        if (rclib != ILB_STATUS_OK)
                goto out;

        rclib = ilb_destroy_servergroup(h, sgname);
out:
        if (h != ILB_INVALID_HANDLE)
                (void) ilb_close(h);

        if (rclib != ILB_STATUS_OK) {
                ilbadm_err(ilb_errstr(rclib));
                rc = ILBADM_LIBERR;
        }

        return (rc);
}

#define BUFSZ   1024

static int
export_srv_spec(ilb_server_data_t *srv, char *buf, const int bufsize)
{
        int     len = 0, bufsz = (int)bufsize;

        ip2str(&srv->sd_addr, buf, bufsz, 0);

        len += strlen(buf);
        bufsz -= len;

        if (srv->sd_minport != 0) {
                in_port_t       h_min, h_max;
                int             inc;

                h_min = ntohs(srv->sd_minport);
                h_max = ntohs(srv->sd_maxport);

                /* to do: if service name was given, print that, not number */
                if (h_max <= h_min)
                        inc = snprintf(buf+len, bufsz, ":%d", h_min);
                else
                        inc = snprintf(buf+len, bufsz, ":%d-%d", h_min, h_max);

                if (inc > bufsz) /* too little space */
                        return (-1);
                len += inc;
        }

        return (len);
}


/*
 * this is called by ilb_walk_servers(), therefore we return ilb_status_t
 * not ilbadm_status, and retain an unused function argument
 */
/* ARGSUSED */
ilb_status_t
ilbadm_export_a_srv(ilb_handle_t h, ilb_server_data_t *srv, const char *sgname,
    void *arg)
{
        sg_export_arg_t *larg = (sg_export_arg_t *)arg;
        FILE            *fp = larg->fp;
        char            linebuf[BUFSZ]; /* XXXms make that dynamic */
        int             sz = BUFSZ;

        if (export_srv_spec(srv, linebuf, sz) == -1)
                return (ILB_STATUS_OK);

        (void) fprintf(fp, "add-server -s server=");

        (void) fprintf(fp, "%s %s\n", linebuf, sgname);
        return (ILB_STATUS_OK);
}

ilb_status_t
ilbadm_export_sg(ilb_handle_t h, ilb_sg_data_t *sg, void *arg)
{
        ilb_status_t    rc = ILB_STATUS_OK;
        sg_export_arg_t *larg = (sg_export_arg_t *)arg;
        FILE            *fp = larg->fp;

        (void) fprintf(fp, "create-servergroup %s\n", sg->sgd_name);
        if (sg->sgd_srvcount == 0)
                return (ILB_STATUS_OK);

        rc = ilb_walk_servers(h, ilbadm_export_a_srv, sg->sgd_name, arg);
        if (rc != ILB_STATUS_OK)
                goto out;

        if (fflush(fp) == EOF)
                rc = ILB_STATUS_WRITE;

out:
        return (rc);
}

ilbadm_status_t
ilbadm_export_servergroups(ilb_handle_t h, FILE *fp)
{
        ilb_status_t    rclib = ILB_STATUS_OK;
        ilbadm_status_t rc = ILBADM_OK;
        sg_export_arg_t arg;

        arg.fp = fp;
        arg.sg = NULL;

        rclib = ilb_walk_servergroups(h, ilbadm_export_sg, NULL, (void *)&arg);
        if (rclib != ILB_STATUS_OK) {
                ilbadm_err(ilb_errstr(rclib));
                rc = ILBADM_LIBERR;
        }

        return (rc);
}