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 <sys/types.h>
28 #include <sys/socket.h>
29 #include <sys/list.h>
30 #include <netinet/in.h>
31 #include <stdio.h>
32 #include <unistd.h>
33 #include <stdlib.h>
34 #include <strings.h>
35 #include <errno.h>
36 #include <ofmt.h>
37 #include <libilb.h>
38 #include "ilbadm.h"
39 
40 extern int	optind, optopt, opterr;
41 extern char	*optarg;
42 
43 typedef struct hc_export_arg {
44 	FILE	*fp;
45 } hc_export_arg_t;
46 
47 /* Maximum columns for printing hc output. */
48 #define	SHOW_HC_COLS	80
49 
50 /* OFMT call back to print out a hc server result field. */
51 static boolean_t print_hc_result(ofmt_arg_t *, char *, uint_t);
52 
53 /* ID to indicate which field to be printed. */
54 enum hc_print_id {
55 	hc_of_rname, hc_of_hname, hc_of_sname, hc_of_status, hc_of_fail_cnt,
56 	hc_of_lasttime, hc_of_nexttime, hc_of_rtt,
57 	hc_of_name, hc_of_timeout, hc_of_count, hc_of_interval, hc_of_def_ping,
58 	hc_of_test
59 };
60 
61 /*
62  * Fields of a hc server result.  The sum of all fields' width is SHOW_HC_COLS.
63  */
64 static ofmt_field_t hc_results[] = {
65 	{"RULENAME",	14,	hc_of_rname,	print_hc_result},
66 	{"HCNAME",	14,	hc_of_hname,	print_hc_result},
67 	{"SERVERID",	14,	hc_of_sname,	print_hc_result},
68 	{"STATUS",	9,	hc_of_status,	print_hc_result},
69 	{"FAIL",	5,	hc_of_fail_cnt,	print_hc_result},
70 	{"LAST",	9,	hc_of_lasttime,	print_hc_result},
71 	{"NEXT",	9,	hc_of_nexttime,	print_hc_result},
72 	{"RTT",		6,	hc_of_rtt,	print_hc_result},
73 	{NULL,		0,	0,		NULL}
74 };
75 
76 /* OFMT call back to print out a hc info field. */
77 static boolean_t print_hc(ofmt_arg_t *, char *, uint_t);
78 
79 /*
80  * Fields of a hc info.  The sume of all fields' width is SHOW_HC_COLS.
81  */
82 static ofmt_field_t hc_fields[] = {
83 	{"HCNAME",	14,	hc_of_name,	print_hc},
84 	{"TIMEOUT",	8,	hc_of_timeout,	print_hc},
85 	{"COUNT",	8,	hc_of_count,	print_hc},
86 	{"INTERVAL",	9,	hc_of_interval,	print_hc},
87 	{"DEF_PING",	9,	hc_of_def_ping,	print_hc},
88 	{"TEST",	32,	hc_of_test,	print_hc},
89 	{NULL,		0,	0,		NULL}
90 };
91 
92 static boolean_t
print_hc(ofmt_arg_t * of_arg,char * buf,uint_t bufsize)93 print_hc(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
94 {
95 	enum hc_print_id id = of_arg->ofmt_id;
96 	ilb_hc_info_t *info = (ilb_hc_info_t *)of_arg->ofmt_cbarg;
97 
98 	switch (id) {
99 	case hc_of_name:
100 		(void) strlcpy(buf, info->hci_name, bufsize);
101 		break;
102 	case hc_of_timeout:
103 		(void) snprintf(buf, bufsize, "%d", info->hci_timeout);
104 		break;
105 	case hc_of_count:
106 		(void) snprintf(buf, bufsize, "%d", info->hci_count);
107 		break;
108 	case hc_of_interval:
109 		(void) snprintf(buf, bufsize, "%d", info->hci_interval);
110 		break;
111 	case hc_of_def_ping:
112 		(void) snprintf(buf, bufsize, "%c",
113 		    info->hci_def_ping ? 'Y' : 'N');
114 		break;
115 	case hc_of_test:
116 		(void) snprintf(buf, bufsize, "%s", info->hci_test);
117 		break;
118 	}
119 	return (B_TRUE);
120 }
121 
122 /* Call back to ilb_walk_hc(). */
123 /* ARGSUSED */
124 static ilb_status_t
ilbadm_print_hc(ilb_handle_t h,ilb_hc_info_t * hc_info,void * arg)125 ilbadm_print_hc(ilb_handle_t h, ilb_hc_info_t *hc_info, void *arg)
126 {
127 	ofmt_handle_t	ofmt_h = arg;
128 
129 	ofmt_print(ofmt_h, hc_info);
130 	return (ILB_STATUS_OK);
131 }
132 
133 /*
134  * Print out health check objects given their name.
135  * Or print out all health check objects if no name given.
136  */
137 /* ARGSUSED */
138 ilbadm_status_t
ilbadm_show_hc(int argc,char * argv[])139 ilbadm_show_hc(int argc, char *argv[])
140 {
141 	ilb_handle_t	h = ILB_INVALID_HANDLE;
142 	ilb_status_t	rclib;
143 	ofmt_handle_t	ofmt_h;
144 	ofmt_status_t	ofmt_ret;
145 
146 	if ((ofmt_ret = ofmt_open("all", hc_fields, 0, SHOW_HC_COLS,
147 	    &ofmt_h)) != OFMT_SUCCESS) {
148 		char err_buf[SHOW_HC_COLS];
149 
150 		ilbadm_err(gettext("ofmt_open failed: %s"),
151 		    ofmt_strerror(ofmt_h, ofmt_ret, err_buf, SHOW_HC_COLS));
152 		return (ILBADM_LIBERR);
153 	}
154 	rclib = ilb_open(&h);
155 	if (rclib != ILB_STATUS_OK)
156 		goto out;
157 
158 	if (argc == 1) {
159 		rclib = ilb_walk_hc(h, ilbadm_print_hc, ofmt_h);
160 	} else {
161 		ilb_hc_info_t hc_info;
162 		int i;
163 
164 		for (i = 1; i < argc; i++) {
165 			rclib = ilb_get_hc_info(h, argv[i], &hc_info);
166 			if (rclib == ILB_STATUS_OK)
167 				ofmt_print(ofmt_h, &hc_info);
168 			else
169 				break;
170 		}
171 	}
172 out:
173 	ofmt_close(ofmt_h);
174 
175 	if (h != ILB_INVALID_HANDLE)
176 		(void) ilb_close(h);
177 
178 	if (rclib != ILB_STATUS_OK) {
179 		ilbadm_err(ilb_errstr(rclib));
180 		return (ILBADM_LIBERR);
181 	}
182 
183 	return (ILBADM_OK);
184 }
185 
186 static boolean_t
print_hc_result(ofmt_arg_t * of_arg,char * buf,uint_t bufsize)187 print_hc_result(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
188 {
189 	enum hc_print_id id = of_arg->ofmt_id;
190 	ilb_hc_srv_t *srv = (ilb_hc_srv_t *)of_arg->ofmt_cbarg;
191 	struct tm tv;
192 
193 	switch (id) {
194 	case hc_of_rname:
195 		(void) strlcpy(buf, srv->hcs_rule_name, bufsize);
196 		break;
197 	case hc_of_hname:
198 		(void) strlcpy(buf, srv->hcs_hc_name, bufsize);
199 		break;
200 	case hc_of_sname:
201 		(void) strlcpy(buf, srv->hcs_ID, bufsize);
202 		break;
203 	case hc_of_status:
204 		switch (srv->hcs_status) {
205 		case ILB_HCS_UNINIT:
206 			(void) strlcpy(buf, "un-init", bufsize);
207 			break;
208 		case ILB_HCS_UNREACH:
209 			(void) strlcpy(buf, "unreach", bufsize);
210 			break;
211 		case ILB_HCS_ALIVE:
212 			(void) strlcpy(buf, "alive", bufsize);
213 			break;
214 		case ILB_HCS_DEAD:
215 			(void) strlcpy(buf, "dead", bufsize);
216 			break;
217 		case ILB_HCS_DISABLED:
218 			(void) strlcpy(buf, "disabled", bufsize);
219 			break;
220 		}
221 		break;
222 	case hc_of_fail_cnt:
223 		(void) snprintf(buf, bufsize, "%u", srv->hcs_fail_cnt);
224 		break;
225 	case hc_of_lasttime:
226 		if (localtime_r(&srv->hcs_lasttime, &tv) == NULL)
227 			return (B_FALSE);
228 		(void) snprintf(buf, bufsize, "%02d:%02d:%02d", tv.tm_hour,
229 		    tv.tm_min, tv.tm_sec);
230 		break;
231 	case hc_of_nexttime:
232 		if (srv->hcs_status == ILB_HCS_DISABLED)
233 			break;
234 		if (localtime_r(&srv->hcs_nexttime, &tv) == NULL)
235 			return (B_FALSE);
236 		(void) snprintf(buf, bufsize, "%02d:%02d:%02d", tv.tm_hour,
237 		    tv.tm_min, tv.tm_sec);
238 		break;
239 	case hc_of_rtt:
240 		(void) snprintf(buf, bufsize, "%u", srv->hcs_rtt);
241 		break;
242 	}
243 	return (B_TRUE);
244 }
245 
246 /* Call back to ilbd_walk_hc_srvs(). */
247 /* ARGSUSED */
248 static ilb_status_t
ilbadm_print_hc_result(ilb_handle_t h,ilb_hc_srv_t * srv,void * arg)249 ilbadm_print_hc_result(ilb_handle_t h, ilb_hc_srv_t *srv, void *arg)
250 {
251 	ofmt_handle_t	ofmt_h = arg;
252 
253 	ofmt_print(ofmt_h, srv);
254 	return (ILB_STATUS_OK);
255 }
256 
257 /*
258  * Output hc result of a specified rule or all rules.
259  */
260 ilbadm_status_t
ilbadm_show_hc_result(int argc,char * argv[])261 ilbadm_show_hc_result(int argc, char *argv[])
262 {
263 	ilb_handle_t	h = ILB_INVALID_HANDLE;
264 	ilb_status_t 	rclib = ILB_STATUS_OK;
265 	int		i;
266 	ofmt_handle_t	ofmt_h;
267 	ofmt_status_t	ofmt_ret;
268 
269 	/* ilbadm show-hc-result [rule-name] */
270 	if (argc < 1) {
271 		ilbadm_err(gettext("usage: ilbadm show-hc-result"
272 		    " [rule-name]"));
273 		return (ILBADM_LIBERR);
274 	}
275 
276 	if ((ofmt_ret = ofmt_open("all", hc_results, 0, SHOW_HC_COLS,
277 	    &ofmt_h)) != OFMT_SUCCESS) {
278 		char err_buf[SHOW_HC_COLS];
279 
280 		ilbadm_err(gettext("ofmt_open failed: %s"),
281 		    ofmt_strerror(ofmt_h, ofmt_ret, err_buf, SHOW_HC_COLS));
282 		return (ILBADM_LIBERR);
283 	}
284 
285 	rclib = ilb_open(&h);
286 	if (rclib != ILB_STATUS_OK)
287 		goto out;
288 
289 	/* If no rule name is given, show results for all rules. */
290 	if (argc == 1) {
291 		rclib = ilb_walk_hc_srvs(h, ilbadm_print_hc_result, NULL,
292 		    ofmt_h);
293 	} else {
294 		for (i = 1; i < argc; i++) {
295 			rclib = ilb_walk_hc_srvs(h, ilbadm_print_hc_result,
296 			    argv[i], ofmt_h);
297 			if (rclib != ILB_STATUS_OK)
298 				break;
299 		}
300 	}
301 out:
302 	ofmt_close(ofmt_h);
303 
304 	if (h != ILB_INVALID_HANDLE)
305 		(void) ilb_close(h);
306 
307 	if (rclib != ILB_STATUS_OK) {
308 		ilbadm_err(ilb_errstr(rclib));
309 		return (ILBADM_LIBERR);
310 	}
311 	return (ILBADM_OK);
312 }
313 
314 #define	ILBADM_DEF_HC_COUNT	3
315 #define	ILBADM_DEF_HC_INTERVAL	30	/* in sec */
316 #define	ILBADM_DEF_HC_TIMEOUT	5	/* in sec */
317 
318 static ilbadm_key_name_t hc_parse_keys[] = {
319 	{ILB_KEY_HC_TEST, "hc-test", "hc-test"},
320 	{ILB_KEY_HC_COUNT, "hc-count", "hc-count"},
321 	{ILB_KEY_HC_TIMEOUT, "hc-timeout", "hc-tout"},
322 	{ILB_KEY_HC_INTERVAL, "hc-interval", "hc-intl"},
323 	{ILB_KEY_BAD, "", ""}
324 };
325 
326 static ilbadm_status_t
ilbadm_hc_parse_arg(char * arg,ilb_hc_info_t * hc)327 ilbadm_hc_parse_arg(char *arg, ilb_hc_info_t *hc)
328 {
329 	ilbadm_status_t ret;
330 
331 	/* set default value for count, interval, timeout */
332 	hc->hci_count = ILBADM_DEF_HC_COUNT;
333 	hc->hci_interval = ILBADM_DEF_HC_INTERVAL;
334 	hc->hci_timeout = ILBADM_DEF_HC_TIMEOUT;
335 	hc->hci_test[0] = '\0';
336 
337 	ret = i_parse_optstring(arg, hc, hc_parse_keys, 0, NULL);
338 	if (ret != ILBADM_OK && ret != ILBADM_LIBERR) {
339 		ilbadm_err(ilbadm_errstr(ret));
340 		return (ILBADM_LIBERR);
341 	}
342 	if (hc->hci_test[0] == '\0' && ret != ILBADM_LIBERR) {
343 		ilbadm_err("hc-test: missing");
344 		return (ILBADM_LIBERR);
345 	}
346 	return (ret);
347 }
348 
349 /* ARGSUSED */
350 ilbadm_status_t
ilbadm_create_hc(int argc,char * argv[])351 ilbadm_create_hc(int argc, char *argv[])
352 {
353 	ilb_handle_t	h = ILB_INVALID_HANDLE;
354 	ilb_hc_info_t	hc_info;
355 	ilbadm_status_t	ret = ILBADM_OK;
356 	ilb_status_t	rclib;
357 	int		c;
358 
359 
360 	hc_info.hci_def_ping = B_TRUE;
361 	while ((c = getopt(argc, argv, ":h:n")) != -1) {
362 		if (c == 'h') {
363 			ret = ilbadm_hc_parse_arg(optarg, &hc_info);
364 			if (ret != ILBADM_OK)
365 				return (ret);
366 		} else if (c == 'n') {
367 			hc_info.hci_def_ping = B_FALSE;
368 		} else {
369 			ilbadm_err(gettext("bad argument %c"), c);
370 			return (ILBADM_LIBERR);
371 		}
372 	}
373 
374 	if (optind >= argc) {
375 		ilbadm_err(gettext("usage: ilbadm"
376 		    " create-healthcheck [-n] -h"
377 		    " hc-test=val[,hc-timeout=val][,hc-count=va]"
378 		    "[,hc-interval=val]  hc-name"));
379 		return (ILBADM_FAIL);
380 	}
381 
382 	if (strlen(argv[optind]) > ILBD_NAMESZ - 1) {
383 		ilbadm_err(gettext("health check object name %s is too long - "
384 		    "must not exceed %d chars"), argv[optind],
385 		    ILBD_NAMESZ - 1);
386 		return (ILBADM_FAIL);
387 	}
388 
389 	if (((strcasecmp(hc_info.hci_test, ILB_HC_STR_UDP) == 0) ||
390 	    (strcasecmp(hc_info.hci_test, ILB_HC_STR_PING) == 0)) &&
391 	    !(hc_info.hci_def_ping)) {
392 		ilbadm_err(gettext("cannot disable default PING"
393 		    " for this test"));
394 		return (ILBADM_LIBERR);
395 	}
396 
397 	rclib = ilb_open(&h);
398 	if (rclib != ILB_STATUS_OK)
399 		goto out;
400 
401 	(void) strlcpy(hc_info.hci_name, argv[optind],
402 	    sizeof (hc_info.hci_name));
403 	rclib = ilb_create_hc(h, &hc_info);
404 out:
405 	if (h != ILB_INVALID_HANDLE)
406 		(void) ilb_close(h);
407 
408 	if (rclib != ILB_STATUS_OK) {
409 		ilbadm_err(ilb_errstr(rclib));
410 		ret = ILBADM_LIBERR;
411 	}
412 	return (ret);
413 }
414 
415 ilbadm_status_t
ilbadm_destroy_hc(int argc,char * argv[])416 ilbadm_destroy_hc(int argc, char *argv[])
417 {
418 	ilb_handle_t	h = ILB_INVALID_HANDLE;
419 	ilb_status_t	rclib;
420 	ilbadm_status_t ret = ILBADM_OK;
421 	int		i;
422 
423 	if (argc < 2) {
424 		ilbadm_err(gettext("usage: ilbadm"
425 		    " delete-healthcheck hc-name ..."));
426 		return (ILBADM_LIBERR);
427 	}
428 
429 	rclib = ilb_open(&h);
430 	if (rclib != ILB_STATUS_OK)
431 		goto out;
432 
433 	for (i = 1; i < argc; i++) {
434 		rclib = ilb_destroy_hc(h, argv[i]);
435 		if (rclib != ILB_STATUS_OK)
436 			break;
437 	}
438 out:
439 	if (h != ILB_INVALID_HANDLE)
440 		(void) ilb_close(h);
441 
442 	if (rclib != ILB_STATUS_OK) {
443 		ilbadm_err(ilb_errstr(rclib));
444 		ret = ILBADM_LIBERR;
445 	}
446 	return (ret);
447 }
448 
449 /*
450  * Since this function is used by libilb function, it
451  * must return libilb errors
452  */
453 /* ARGSUSED */
454 ilb_status_t
ilbadm_export_hcinfo(ilb_handle_t h,ilb_hc_info_t * hc_info,void * arg)455 ilbadm_export_hcinfo(ilb_handle_t h, ilb_hc_info_t *hc_info, void *arg)
456 {
457 	FILE 		*fp = ((hc_export_arg_t *)arg)->fp;
458 	int		count = 0;
459 	int		ret;
460 
461 	/*
462 	 * a test name "PING" implies "no default ping", so we only
463 	 * print -n if the test is NOT "PING"
464 	 */
465 	if (hc_info->hci_def_ping == B_FALSE &&
466 	    strncasecmp(hc_info->hci_test, "PING", 5) != 0)
467 		(void) fprintf(fp, "create-healthcheck -n -h ");
468 	else
469 		(void) fprintf(fp, "create-healthcheck -h ");
470 
471 	if (*hc_info->hci_test != '\0') {
472 		(void) fprintf(fp, "hc-test=%s", hc_info->hci_test);
473 		count++;
474 	}
475 	if (hc_info->hci_timeout != 0) {
476 		if (count++ > 0)
477 			(void) fprintf(fp, ",");
478 		(void) fprintf(fp, "hc-timeout=%d", hc_info->hci_timeout);
479 	}
480 	if (hc_info->hci_count != 0) {
481 		if (count++ > 0)
482 			(void) fprintf(fp, ",");
483 		(void) fprintf(fp, "hc-count=%d", hc_info->hci_count);
484 	}
485 	if (hc_info->hci_interval != 0) {
486 		if (count > 0)
487 			(void) fprintf(fp, ",");
488 		(void) fprintf(fp, "hc-interval=%d", hc_info->hci_interval);
489 	}
490 
491 	/*
492 	 * if any of the above writes fails, then, we assume, so will
493 	 * this one; so it's sufficient to test once
494 	 */
495 	ret = fprintf(fp, " %s\n", hc_info->hci_name);
496 	if (ret < 0)
497 		goto out_fail;
498 	ret = fflush(fp);
499 
500 out_fail:
501 	if (ret < 0)
502 		return (ILB_STATUS_WRITE);
503 	return (ILB_STATUS_OK);
504 }
505 
506 ilbadm_status_t
ilbadm_export_hc(ilb_handle_t h,FILE * fp)507 ilbadm_export_hc(ilb_handle_t h, FILE *fp)
508 {
509 	ilb_status_t	rclib;
510 	ilbadm_status_t	ret = ILBADM_OK;
511 	hc_export_arg_t	arg;
512 
513 	arg.fp = fp;
514 	rclib = ilb_walk_hc(h, ilbadm_export_hcinfo, (void *)&arg);
515 	if (rclib != ILB_STATUS_OK) {
516 		ilbadm_err(ilb_errstr(rclib));
517 		ret = ILBADM_LIBERR;
518 	}
519 	return (ret);
520 }
521