/* * 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 <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?sd->sd_srvID:"(null)", 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; /* not reached */ break; 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(1M)")); 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(1M)")); 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); }