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