xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/ilbadm/ilbadm_sg.c (revision e8d80663e4f91871f843bb8ad9108dc0b76dfcf3)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  * Copyright 2012 Milan Jurik. All rights reserved.
26  */
27 
28 #include <stdio.h>
29 #include <unistd.h>
30 #include <stdlib.h>
31 #include <strings.h>
32 #include <stddef.h>
33 #include <assert.h>
34 #include <errno.h>
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <netinet/in.h>
38 #include <arpa/inet.h>
39 #include <sys/list.h>
40 #include <ofmt.h>
41 #include <libilb.h>
42 #include "ilbadm.h"
43 
44 static ilbadm_key_name_t servrange_keys[] = {
45 	{ILB_KEY_SERVER, "server", "servers"},
46 	{ILB_KEY_SERVRANGE, "server", "servers"},
47 	{ILB_KEY_BAD, "", ""}
48 };
49 
50 static ilbadm_key_name_t serverID_keys[] = {
51 	{ILB_KEY_SERVERID, "server", ""},
52 	{ILB_KEY_BAD, "", ""}
53 };
54 
55 typedef struct sg_export_arg {
56 	FILE		*fp;
57 	ilbadm_sgroup_t	*sg;
58 } sg_export_arg_t;
59 
60 typedef struct arg_struct {
61 	int		flags;
62 	char		*o_str;
63 	ofmt_field_t	*o_fields;
64 	ofmt_handle_t	oh;
65 } list_arg_t;
66 
67 typedef struct sg_srv_o_struct {
68 	char		*sgname;
69 	ilb_server_data_t	*sd;
70 } sg_srv_o_arg_t;
71 
72 static ofmt_cb_t of_sgname;
73 static ofmt_cb_t of_srvID;
74 static ofmt_cb_t of_port;
75 static ofmt_cb_t of_ip;
76 
77 static ofmt_field_t sgfields_v4[] = {
78 	{"SGNAME", ILB_SGNAME_SZ, 0, of_sgname},
79 	{"SERVERID", ILB_NAMESZ, 0, of_srvID},
80 	{"MINPORT", 8, 0, of_port},
81 	{"MAXPORT", 8, 1, of_port},
82 	{"IP_ADDRESS", 15, 0, of_ip},
83 	{NULL, 0, 0, NULL}
84 };
85 static ofmt_field_t sgfields_v6[] = {
86 	{"SGNAME", ILB_SGNAME_SZ, 0, of_sgname},
87 	{"SERVERID", ILB_NAMESZ, 0, of_srvID},
88 	{"MINPORT", 8, 0, of_port},
89 	{"MAXPORT", 8, 1, of_port},
90 	{"IP_ADDRESS", 39, 0, of_ip},
91 	{NULL, 0, 0, NULL}
92 };
93 
94 #define	MAXCOLS	80 /* make flexible? */
95 
96 extern int	optind, optopt, opterr;
97 extern char	*optarg;
98 
99 static boolean_t
100 of_sgname(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
101 {
102 	sg_srv_o_arg_t	*l = (sg_srv_o_arg_t *)of_arg->ofmt_cbarg;
103 
104 	(void) strlcpy(buf, l->sgname, bufsize);
105 	return (B_TRUE);
106 }
107 
108 static boolean_t
109 of_srvID(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
110 {
111 	sg_srv_o_arg_t	*l = (sg_srv_o_arg_t *)of_arg->ofmt_cbarg;
112 
113 	(void) strlcpy(buf, l->sd->sd_srvID, bufsize);
114 	return (B_TRUE);
115 }
116 
117 static boolean_t
118 of_port(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
119 {
120 	sg_srv_o_arg_t	*l = (sg_srv_o_arg_t *)of_arg->ofmt_cbarg;
121 	int		port;
122 
123 	if (of_arg->ofmt_id == 0) {
124 		port = ntohs(l->sd->sd_minport);
125 		if (port == 0)
126 			*buf = '\0';
127 		else
128 			(void) snprintf(buf, bufsize, "%d", port);
129 	} else {
130 		port = ntohs(l->sd->sd_maxport);
131 		if (port == 0)
132 			*buf = '\0';
133 		else
134 			(void) snprintf(buf, bufsize, "%d", port);
135 	}
136 	return (B_TRUE);
137 }
138 
139 static boolean_t
140 of_ip(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
141 {
142 	sg_srv_o_arg_t	*l = (sg_srv_o_arg_t *)of_arg->ofmt_cbarg;
143 
144 	ip2str(&l->sd->sd_addr, buf, bufsize, V6_ADDRONLY);
145 	return (B_TRUE);
146 }
147 
148 ilbadm_status_t
149 i_list_sg_srv_ofmt(char *sgname, ilb_server_data_t *sd, void *arg)
150 {
151 	list_arg_t	*larg = (list_arg_t *)arg;
152 	sg_srv_o_arg_t	line_arg;
153 
154 	line_arg.sgname = sgname;
155 	line_arg.sd = sd;
156 	ofmt_print(larg->oh, &line_arg);
157 	return (ILBADM_OK);
158 }
159 
160 /*
161  * This function is always called via ilb_walk_servergroups()
162  * and so must return libilb errors.
163  * That's why we need to retain currently unused "h" argument
164  */
165 /* ARGSUSED */
166 static ilb_status_t
167 ilbadm_list_sg_srv(ilb_handle_t h, ilb_server_data_t *sd, const char *sgname,
168     void *arg)
169 {
170 	char		ip_str[2*INET6_ADDRSTRLEN + 3] = "";
171 	char		port_str[INET6_ADDRSTRLEN];
172 	list_arg_t	*larg = (list_arg_t *)arg;
173 	ofmt_status_t	oerr;
174 	int		oflags = 0;
175 	int		ocols = MAXCOLS;
176 	int		h_minport, h_maxport;
177 	static ofmt_handle_t	oh = (ofmt_handle_t)NULL;
178 	ofmt_field_t	*ofp;
179 
180 	if (larg->o_str != NULL) {
181 		if (oh == NULL) {
182 			if (sd->sd_addr.ia_af == AF_INET)
183 				ofp = sgfields_v6;
184 			else
185 				ofp = sgfields_v4;
186 
187 			if (larg->flags & ILBADM_LIST_PARSE)
188 				oflags |= OFMT_PARSABLE;
189 
190 			oerr = ofmt_open(larg->o_str, ofp, oflags, ocols, &oh);
191 			if (oerr != OFMT_SUCCESS) {
192 				char	e[80];
193 
194 				ilbadm_err(gettext("ofmt_open failed: %s"),
195 				    ofmt_strerror(oh, oerr, e, sizeof (e)));
196 				return (ILB_STATUS_GENERIC);
197 			}
198 			larg->oh = oh;
199 		}
200 
201 
202 		(void) i_list_sg_srv_ofmt((char *)sgname, sd, arg);
203 		return (ILB_STATUS_OK);
204 	}
205 
206 	ip2str(&sd->sd_addr, ip_str, sizeof (ip_str), 0);
207 
208 	h_minport = ntohs(sd->sd_minport);
209 	h_maxport = ntohs(sd->sd_maxport);
210 	if (h_minport == 0)
211 		*port_str = '\0';
212 	else if (h_maxport > h_minport)
213 		(void) sprintf(port_str, ":%d-%d", h_minport, h_maxport);
214 	else
215 		(void) sprintf(port_str, ":%d", h_minport);
216 
217 	(void) printf("%s: id:%s %s%s\n", sgname,
218 	    sd->sd_srvID?sd->sd_srvID:"(null)", ip_str, port_str);
219 	return (ILB_STATUS_OK);
220 }
221 
222 ilb_status_t
223 ilbadm_list_sg(ilb_handle_t h, ilb_sg_data_t *sg, void *arg)
224 {
225 	if (sg->sgd_srvcount == 0) {
226 		ilb_server_data_t	tmp_srv;
227 
228 		bzero(&tmp_srv, sizeof (tmp_srv));
229 		return (ilbadm_list_sg_srv(h, &tmp_srv, sg->sgd_name, arg));
230 	}
231 
232 	return (ilb_walk_servers(h, ilbadm_list_sg_srv, sg->sgd_name, arg));
233 }
234 
235 static char *def_fields = "SGNAME,SERVERID,MINPORT,MAXPORT,IP_ADDRESS";
236 
237 /* ARGSUSED */
238 ilbadm_status_t
239 ilbadm_show_servergroups(int argc, char *argv[])
240 {
241 	ilb_handle_t	h = ILB_INVALID_HANDLE;
242 	ilb_status_t	rclib = ILB_STATUS_OK;
243 	ilbadm_status_t	rc = ILBADM_OK;
244 	int		c;
245 	char		optstr[] = ":po:";
246 
247 	boolean_t	o_opt = B_FALSE, p_opt = B_FALSE;
248 	list_arg_t	larg = {0, def_fields, NULL, NULL};
249 
250 	while ((c = getopt(argc, argv, optstr)) != -1) {
251 		switch ((char)c) {
252 		case 'p': p_opt = B_TRUE;
253 			larg.flags |= ILBADM_LIST_PARSE;
254 			break;
255 		case 'o': larg.o_str = optarg;
256 			o_opt = B_TRUE;
257 			break;
258 		case ':': ilbadm_err(gettext("missing option argument"
259 			    " for %c"), (char)optopt);
260 			rc = ILBADM_LIBERR;
261 			goto out;
262 		default: unknown_opt(argv, optind-1);
263 			/* not reached */
264 			break;
265 		}
266 	}
267 
268 	if (p_opt && !o_opt) {
269 		ilbadm_err(gettext("option -p requires -o"));
270 		exit(1);
271 	}
272 
273 	if (p_opt && larg.o_str != NULL &&
274 	    (strcasecmp(larg.o_str, "all") == 0)) {
275 		ilbadm_err(gettext("option -p requires explicit field"
276 		    " names for -o"));
277 		exit(1);
278 	}
279 
280 	rclib = ilb_open(&h);
281 	if (rclib != ILB_STATUS_OK)
282 		goto out;
283 
284 	if (optind >= argc) {
285 		rclib = ilb_walk_servergroups(h, ilbadm_list_sg, NULL,
286 		    (void*)&larg);
287 		if (rclib != ILB_STATUS_OK)
288 			rc = ILBADM_LIBERR;
289 	} else {
290 		while (optind < argc) {
291 			rclib = ilb_walk_servergroups(h, ilbadm_list_sg,
292 			    argv[optind++], (void*)&larg);
293 			if (rclib != ILB_STATUS_OK) {
294 				rc = ILBADM_LIBERR;
295 				break;
296 			}
297 		}
298 	}
299 
300 	if (larg.oh != NULL)
301 		ofmt_close(larg.oh);
302 out:
303 	if (h != ILB_INVALID_HANDLE)
304 		(void) ilb_close(h);
305 
306 	if (rclib != ILB_STATUS_OK) {
307 		/*
308 		 * The show function returns ILB_STATUS_GENERIC after printing
309 		 * out an error message.  So we don't need to print it again.
310 		 */
311 		if (rclib != ILB_STATUS_GENERIC)
312 			ilbadm_err(ilb_errstr(rclib));
313 		rc = ILBADM_LIBERR;
314 	}
315 
316 	return (rc);
317 }
318 
319 ilbadm_servnode_t *
320 i_new_sg_elem(ilbadm_sgroup_t *sgp)
321 {
322 	ilbadm_servnode_t *s;
323 
324 	s = (ilbadm_servnode_t *)calloc(sizeof (*s), 1);
325 	if (s != NULL) {
326 		list_insert_tail(&sgp->sg_serv_list, s);
327 		sgp->sg_count++;
328 	}
329 	return (s);
330 }
331 
332 static ilbadm_status_t
333 i_parse_servrange_list(char *arg, ilbadm_sgroup_t *sgp)
334 {
335 	ilbadm_status_t	rc;
336 	int		count;
337 
338 	rc = i_parse_optstring(arg, (void *) sgp, servrange_keys,
339 	    OPT_VALUE_LIST|OPT_IP_RANGE|OPT_PORTS, &count);
340 	return (rc);
341 }
342 
343 static ilbadm_status_t
344 i_parse_serverIDs(char *arg, ilbadm_sgroup_t *sgp)
345 {
346 	ilbadm_status_t	rc;
347 	int		count;
348 
349 	rc = i_parse_optstring(arg, (void *) sgp, serverID_keys,
350 	    OPT_VALUE_LIST|OPT_PORTS, &count);
351 	return (rc);
352 }
353 
354 static ilbadm_status_t
355 i_mod_sg(ilb_handle_t h, ilbadm_sgroup_t *sgp, ilbadm_cmd_t cmd,
356     int flags)
357 {
358 	ilbadm_servnode_t	*sn;
359 	ilb_server_data_t	*srv;
360 	ilb_status_t		rclib = ILB_STATUS_OK;
361 	ilbadm_status_t		rc = ILBADM_OK;
362 
363 	if (h == ILB_INVALID_HANDLE && cmd != cmd_enable_server &&
364 	    cmd != cmd_disable_server)
365 		return (ILBADM_LIBERR);
366 
367 	sn = list_head(&sgp->sg_serv_list);
368 	while (sn != NULL) {
369 		srv = &sn->s_spec;
370 
371 		srv->sd_flags |= flags;
372 		if (cmd == cmd_create_sg || cmd == cmd_add_srv) {
373 			rclib = ilb_add_server_to_group(h, sgp->sg_name,
374 			    srv);
375 			if (rclib != ILB_STATUS_OK) {
376 				char	buf[INET6_ADDRSTRLEN + 1];
377 
378 				rc = ILBADM_LIBERR;
379 				ip2str(&srv->sd_addr, buf, sizeof (buf),
380 				    V6_ADDRONLY);
381 				ilbadm_err(gettext("cannot add %s to %s: %s"),
382 				    buf, sgp->sg_name, ilb_errstr(rclib));
383 				/* if we created the SG, we bail out */
384 				if (cmd == cmd_create_sg)
385 					return (rc);
386 			}
387 		} else {
388 			assert(cmd == cmd_rem_srv);
389 			rclib = ilb_rem_server_from_group(h, sgp->sg_name,
390 			    srv);
391 			/* if we fail, we tell user and continue */
392 			if (rclib != ILB_STATUS_OK) {
393 				rc = ILBADM_LIBERR;
394 				ilbadm_err(
395 				    gettext("cannot remove %s from %s: %s"),
396 				    srv->sd_srvID, sgp->sg_name,
397 				    ilb_errstr(rclib));
398 			}
399 		}
400 
401 		/*
402 		 * list_next returns NULL instead of cycling back to head
403 		 * so we don't have to check for list_head explicitly.
404 		 */
405 		sn = list_next(&sgp->sg_serv_list, sn);
406 	};
407 
408 	return (rc);
409 }
410 
411 static void
412 i_ilbadm_alloc_sgroup(ilbadm_sgroup_t **sgp)
413 {
414 	ilbadm_sgroup_t	*sg;
415 
416 	*sgp = sg = (ilbadm_sgroup_t *)calloc(sizeof (*sg), 1);
417 	if (sg == NULL)
418 		return;
419 	list_create(&sg->sg_serv_list, sizeof (ilbadm_servnode_t),
420 	    offsetof(ilbadm_servnode_t, s_link));
421 }
422 
423 static void
424 i_ilbadm_free_sgroup(ilbadm_sgroup_t *sg)
425 {
426 	ilbadm_servnode_t	*s;
427 
428 	while ((s = list_remove_head(&sg->sg_serv_list)) != NULL)
429 		free(s);
430 
431 	list_destroy(&sg->sg_serv_list);
432 }
433 
434 ilbadm_status_t
435 ilbadm_create_servergroup(int argc, char *argv[])
436 {
437 	ilb_handle_t	h = ILB_INVALID_HANDLE;
438 	ilb_status_t	rclib = ILB_STATUS_OK;
439 	ilbadm_status_t	rc = ILBADM_OK;
440 	ilbadm_sgroup_t	*sg;
441 	int		c;
442 	int		flags = 0;
443 
444 	i_ilbadm_alloc_sgroup(&sg);
445 
446 	while ((c = getopt(argc, argv, ":s:")) != -1) {
447 		switch ((char)c) {
448 		case 's':
449 			rc = i_parse_servrange_list(optarg, sg);
450 			break;
451 		case ':':
452 			ilbadm_err(gettext("missing option-argument for"
453 			    " %c"), (char)optopt);
454 			rc = ILBADM_LIBERR;
455 			break;
456 		case '?':
457 		default:
458 			unknown_opt(argv, optind-1);
459 			/* not reached */
460 			break;
461 		}
462 
463 		if (rc != ILBADM_OK)
464 			goto out;
465 	}
466 
467 	if (optind >= argc) {
468 		ilbadm_err(gettext("missing mandatory arguments - please refer"
469 		    " to 'create-servergroup' subcommand"
470 		    "  description in ilbadm(1M)"));
471 		rc = ILBADM_LIBERR;
472 		goto out;
473 	}
474 
475 	if (strlen(argv[optind]) > ILB_SGNAME_SZ - 1) {
476 		ilbadm_err(gettext("servergroup name %s is too long -"
477 		    " must not exceed %d chars"), argv[optind],
478 		    ILB_SGNAME_SZ - 1);
479 		rc = ILBADM_LIBERR;
480 		goto out;
481 	}
482 
483 	sg->sg_name = argv[optind];
484 
485 	rclib = ilb_open(&h);
486 	if (rclib != ILB_STATUS_OK)
487 		goto out;
488 
489 	rclib = ilb_create_servergroup(h, sg->sg_name);
490 	if (rclib != ILB_STATUS_OK)
491 		goto out;
492 
493 	/* we create a servergroup with all servers enabled */
494 	ILB_SET_ENABLED(flags);
495 	rc = i_mod_sg(h, sg, cmd_create_sg, flags);
496 
497 	if (rc != ILBADM_OK)
498 		(void) ilb_destroy_servergroup(h, sg->sg_name);
499 
500 out:
501 	i_ilbadm_free_sgroup(sg);
502 	if (h != ILB_INVALID_HANDLE)
503 		(void) ilb_close(h);
504 
505 	if (rclib != ILB_STATUS_OK) {
506 		ilbadm_err(ilb_errstr(rclib));
507 		rc = ILBADM_LIBERR;
508 	}
509 	if ((rc != ILBADM_OK) && (rc != ILBADM_LIBERR))
510 		ilbadm_err(ilbadm_errstr(rc));
511 
512 	return (rc);
513 }
514 
515 ilbadm_status_t
516 ilbadm_add_server_to_group(int argc, char **argv)
517 {
518 	ilb_handle_t	h = ILB_INVALID_HANDLE;
519 	ilb_status_t	rclib = ILB_STATUS_OK;
520 	ilbadm_status_t	rc = ILBADM_OK;
521 	ilbadm_sgroup_t	*sg;
522 	int		c;
523 	int		flags = 0;
524 
525 	i_ilbadm_alloc_sgroup(&sg);
526 
527 	while ((c = getopt(argc, argv, ":s:")) != -1) {
528 		switch ((char)c) {
529 		case 's':
530 			rc = i_parse_servrange_list(optarg, sg);
531 			break;
532 		case ':':
533 			ilbadm_err(gettext("missing option-argument for"
534 			    " %c"), (char)optopt);
535 			rc = ILBADM_LIBERR;
536 			break;
537 		case '?':
538 		default: unknown_opt(argv, optind-1);
539 			/* not reached */
540 			break;
541 		}
542 
543 		if (rc != ILBADM_OK)
544 			goto out;
545 	}
546 
547 	if (optind >= argc) {
548 		ilbadm_err(gettext("missing mandatory arguments - please refer"
549 		    " to 'add-server' subcommand description in ilbadm(1M)"));
550 		rc = ILBADM_LIBERR;
551 		goto out;
552 	}
553 
554 	sg->sg_name = argv[optind];
555 
556 	rclib = ilb_open(&h);
557 	if (rclib != ILB_STATUS_OK)
558 		goto out;
559 
560 	/* A server is added enabled */
561 	ILB_SET_ENABLED(flags);
562 	rc = i_mod_sg(h, sg, cmd_add_srv, flags);
563 out:
564 	i_ilbadm_free_sgroup(sg);
565 	if (h != ILB_INVALID_HANDLE)
566 		(void) ilb_close(h);
567 
568 	if ((rc != ILBADM_OK) && (rc != ILBADM_LIBERR))
569 		ilbadm_err(ilbadm_errstr(rc));
570 	return (rc);
571 }
572 
573 /* ARGSUSED */
574 static ilbadm_status_t
575 ilbadm_Xable_server(int argc, char *argv[], ilbadm_cmd_t cmd)
576 {
577 	ilb_handle_t		h = ILB_INVALID_HANDLE;
578 	ilbadm_status_t		rc = ILBADM_OK;
579 	ilb_status_t		rclib = ILB_STATUS_OK;
580 	int			i;
581 
582 	if (argc < 2) {
583 		ilbadm_err(gettext("missing required argument"
584 		    " (server specification)"));
585 		rc = ILBADM_LIBERR;
586 		goto out;
587 	}
588 
589 	rclib = ilb_open(&h);
590 	if (rclib != ILB_STATUS_OK)
591 		goto out;
592 
593 	/* enable-server and disable-server only accepts serverids */
594 	for (i = 1; i < argc && rclib == ILB_STATUS_OK; i++) {
595 		ilb_server_data_t	srv;
596 
597 		if (argv[i][0] != ILB_SRVID_PREFIX) {
598 			rc = ILBADM_INVAL_SRVID;
599 			goto out;
600 		}
601 
602 		bzero(&srv, sizeof (srv));
603 		/* to do: check length */
604 		(void) strlcpy(srv.sd_srvID, argv[i], sizeof (srv.sd_srvID));
605 		switch (cmd) {
606 		case cmd_enable_server:
607 			rclib = ilb_enable_server(h, &srv, NULL);
608 			break;
609 		case cmd_disable_server:
610 			rclib = ilb_disable_server(h, &srv, NULL);
611 			break;
612 		}
613 
614 		/* if we can't find a given server ID, just plough on */
615 		if (rclib == ILB_STATUS_ENOENT) {
616 			const char *msg = ilb_errstr(rclib);
617 
618 			rc = ILBADM_LIBERR;
619 			ilbadm_err("%s: %s", msg, argv[i]);
620 			rclib = ILB_STATUS_OK;
621 			continue;
622 		}
623 		if (rclib != ILB_STATUS_OK)
624 			break;
625 	}
626 out:
627 	if (h != ILB_INVALID_HANDLE)
628 		(void) ilb_close(h);
629 
630 	if (rclib != ILB_STATUS_OK) {
631 		ilbadm_err(ilb_errstr(rclib));
632 		rc = ILBADM_LIBERR;
633 	}
634 
635 	if ((rc != ILBADM_OK) && (rc != ILBADM_LIBERR))
636 		ilbadm_err(ilbadm_errstr(rc));
637 	return (rc);
638 }
639 
640 ilbadm_status_t
641 ilbadm_disable_server(int argc, char *argv[])
642 {
643 	return (ilbadm_Xable_server(argc, argv, cmd_disable_server));
644 }
645 
646 ilbadm_status_t
647 ilbadm_enable_server(int argc, char *argv[])
648 {
649 	return (ilbadm_Xable_server(argc, argv, cmd_enable_server));
650 }
651 
652 /* ARGSUSED */
653 ilbadm_status_t
654 ilbadm_rem_server_from_group(int argc, char *argv[])
655 {
656 	ilb_handle_t	h = ILB_INVALID_HANDLE;
657 	ilb_status_t	rclib = ILB_STATUS_OK;
658 	ilbadm_status_t	rc = ILBADM_OK;
659 	ilbadm_sgroup_t	*sg;
660 	int		c;
661 
662 	i_ilbadm_alloc_sgroup(&sg);
663 
664 	while ((c = getopt(argc, argv, ":s:")) != -1) {
665 		switch ((char)c) {
666 		case 's':
667 			rc = i_parse_serverIDs(optarg, sg);
668 			break;
669 		case ':':
670 			ilbadm_err(gettext("missing option-argument for"
671 			    " %c"), (char)optopt);
672 			rc = ILBADM_LIBERR;
673 			break;
674 		case '?':
675 		default: unknown_opt(argv, optind-1);
676 			/* not reached */
677 			break;
678 		}
679 		if (rc != ILBADM_OK)
680 			goto out;
681 	}
682 
683 	/* we need servergroup name and at least one serverID to remove */
684 	if (optind >= argc || sg->sg_count == 0) {
685 		rc = ILBADM_ENOOPTION;
686 		goto out;
687 	}
688 
689 	sg->sg_name = argv[optind];
690 
691 	rclib = ilb_open(&h);
692 	if (rclib != ILB_STATUS_OK)
693 		goto out;
694 
695 	rc = i_mod_sg(h, sg, cmd_rem_srv, 0);
696 out:
697 	i_ilbadm_free_sgroup(sg);
698 
699 	if (h != ILB_INVALID_HANDLE)
700 		(void) ilb_close(h);
701 	if ((rc != ILBADM_OK) && (rc != ILBADM_LIBERR))
702 		ilbadm_err(ilbadm_errstr(rc));
703 	return (rc);
704 }
705 
706 ilbadm_status_t
707 ilbadm_destroy_servergroup(int argc, char *argv[])
708 {
709 	ilb_handle_t	h = ILB_INVALID_HANDLE;
710 	ilb_status_t	rclib = ILB_STATUS_OK;
711 	ilbadm_status_t	rc = ILBADM_OK;
712 	char		*sgname;
713 
714 	if (argc != 2) {
715 		ilbadm_err(gettext("usage:ilbadm"
716 		    " delete-servergroup groupname"));
717 		rc = ILBADM_LIBERR;
718 		goto out;
719 	}
720 
721 	sgname = argv[1];
722 
723 	rclib = ilb_open(&h);
724 	if (rclib != ILB_STATUS_OK)
725 		goto out;
726 
727 	rclib = ilb_destroy_servergroup(h, sgname);
728 out:
729 	if (h != ILB_INVALID_HANDLE)
730 		(void) ilb_close(h);
731 
732 	if (rclib != ILB_STATUS_OK) {
733 		ilbadm_err(ilb_errstr(rclib));
734 		rc = ILBADM_LIBERR;
735 	}
736 
737 	return (rc);
738 }
739 
740 #define	BUFSZ	1024
741 
742 static int
743 export_srv_spec(ilb_server_data_t *srv, char *buf, const int bufsize)
744 {
745 	int	len = 0, bufsz = (int)bufsize;
746 
747 	ip2str(&srv->sd_addr, buf, bufsz, 0);
748 
749 	len += strlen(buf);
750 	bufsz -= len;
751 
752 	if (srv->sd_minport != 0) {
753 		in_port_t	h_min, h_max;
754 		int		inc;
755 
756 		h_min = ntohs(srv->sd_minport);
757 		h_max = ntohs(srv->sd_maxport);
758 
759 		/* to do: if service name was given, print that, not number */
760 		if (h_max <= h_min)
761 			inc = snprintf(buf+len, bufsz, ":%d", h_min);
762 		else
763 			inc = snprintf(buf+len, bufsz, ":%d-%d", h_min, h_max);
764 
765 		if (inc > bufsz) /* too little space */
766 			return (-1);
767 		len += inc;
768 	}
769 
770 	return (len);
771 }
772 
773 
774 /*
775  * this is called by ilb_walk_servers(), therefore we return ilb_status_t
776  * not ilbadm_status, and retain an unused function argument
777  */
778 /* ARGSUSED */
779 ilb_status_t
780 ilbadm_export_a_srv(ilb_handle_t h, ilb_server_data_t *srv, const char *sgname,
781     void *arg)
782 {
783 	sg_export_arg_t	*larg = (sg_export_arg_t *)arg;
784 	FILE		*fp = larg->fp;
785 	char		linebuf[BUFSZ]; /* XXXms make that dynamic */
786 	int		sz = BUFSZ;
787 
788 	if (export_srv_spec(srv, linebuf, sz) == -1)
789 		return (ILB_STATUS_OK);
790 
791 	(void) fprintf(fp, "add-server -s server=");
792 
793 	(void) fprintf(fp, "%s %s\n", linebuf, sgname);
794 	return (ILB_STATUS_OK);
795 }
796 
797 ilb_status_t
798 ilbadm_export_sg(ilb_handle_t h, ilb_sg_data_t *sg, void *arg)
799 {
800 	ilb_status_t	rc = ILB_STATUS_OK;
801 	sg_export_arg_t	*larg = (sg_export_arg_t *)arg;
802 	FILE		*fp = larg->fp;
803 
804 	(void) fprintf(fp, "create-servergroup %s\n", sg->sgd_name);
805 	if (sg->sgd_srvcount == 0)
806 		return (ILB_STATUS_OK);
807 
808 	rc = ilb_walk_servers(h, ilbadm_export_a_srv, sg->sgd_name, arg);
809 	if (rc != ILB_STATUS_OK)
810 		goto out;
811 
812 	if (fflush(fp) == EOF)
813 		rc = ILB_STATUS_WRITE;
814 
815 out:
816 	return (rc);
817 }
818 
819 ilbadm_status_t
820 ilbadm_export_servergroups(ilb_handle_t h, FILE *fp)
821 {
822 	ilb_status_t	rclib = ILB_STATUS_OK;
823 	ilbadm_status_t	rc = ILBADM_OK;
824 	sg_export_arg_t	arg;
825 
826 	arg.fp = fp;
827 	arg.sg = NULL;
828 
829 	rclib = ilb_walk_servergroups(h, ilbadm_export_sg, NULL, (void *)&arg);
830 	if (rclib != ILB_STATUS_OK) {
831 		ilbadm_err(ilb_errstr(rclib));
832 		rc = ILBADM_LIBERR;
833 	}
834 
835 	return (rc);
836 }
837