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 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <strings.h>
30 #include <stdlib.h>
31 #include <netdir.h>
32 #include <errno.h>
33 #include <alloca.h>
34 #include <locale.h>
35 #include <uuid/uuid.h>
36 
37 #include <sys/fm/protocol.h>
38 #include <fmd_adm_impl.h>
39 #include <fmd_rpc_adm.h>
40 
41 static const uint_t _fmd_adm_bufsize = 128 * 1024;
42 static const char _url_fallback[] = "http://sun.com/msg/";
43 
44 fmd_adm_t *
45 fmd_adm_open(const char *host, uint32_t prog, int version)
46 {
47 	fmd_adm_t *ap;
48 	CLIENT *c;
49 	rpcvers_t v;
50 
51 	if (version != FMD_ADM_VERSION) {
52 		errno = ENOTSUP;
53 		return (NULL);
54 	}
55 
56 	if (host == NULL)
57 		host = HOST_SELF;
58 
59 	if (prog == FMD_ADM_PROGRAM)
60 		prog = FMD_ADM;
61 
62 	if ((ap = malloc(sizeof (fmd_adm_t))) == NULL)
63 		return (NULL);
64 
65 	if (strcmp(host, HOST_SELF) == 0) {
66 		c = clnt_door_create(prog, FMD_ADM_VERSION_1, _fmd_adm_bufsize);
67 		ap->adm_maxretries = 1;
68 	} else {
69 		c = clnt_create_vers(host, prog, &v,
70 		    FMD_ADM_VERSION_1, FMD_ADM_VERSION_1, NULL);
71 		ap->adm_maxretries = 0;
72 	}
73 
74 	if (c == NULL) {
75 		errno = EPROTO;
76 		free(ap);
77 		return (NULL);
78 	}
79 
80 	ap->adm_prog = prog;
81 	ap->adm_clnt = c;
82 	ap->adm_version = version;
83 	ap->adm_svcerr = 0;
84 	ap->adm_errno = 0;
85 
86 	return (ap);
87 }
88 
89 void
90 fmd_adm_close(fmd_adm_t *ap)
91 {
92 	if (ap == NULL)
93 		return; /* permit NULL to simply caller code */
94 
95 	clnt_destroy(ap->adm_clnt);
96 	free(ap);
97 }
98 
99 static const char *
100 fmd_adm_svc_errmsg(enum fmd_adm_error err)
101 {
102 	switch (err) {
103 	case FMD_ADM_ERR_NOMEM:
104 		return ("unable to perform request due to allocation failure");
105 	case FMD_ADM_ERR_PERM:
106 		return ("operation requires additional privilege");
107 	case FMD_ADM_ERR_MODSRCH:
108 		return ("specified module is not loaded in fault manager");
109 	case FMD_ADM_ERR_MODBUSY:
110 		return ("module is in use and cannot be unloaded");
111 	case FMD_ADM_ERR_MODFAIL:
112 		return ("module failed and can no longer export statistics");
113 	case FMD_ADM_ERR_MODNOENT:
114 		return ("file missing or cannot be accessed by fault manager");
115 	case FMD_ADM_ERR_MODEXIST:
116 		return ("module using same name is already loaded");
117 	case FMD_ADM_ERR_MODINIT:
118 		return ("module failed to initialize (consult fmd(1M) log)");
119 	case FMD_ADM_ERR_MODLOAD:
120 		return ("module failed to load (consult fmd(1M) log)");
121 	case FMD_ADM_ERR_RSRCSRCH:
122 		return ("specified resource is not cached by fault manager");
123 	case FMD_ADM_ERR_RSRCNOTF:
124 		return ("specified resource is not known to be faulty");
125 	case FMD_ADM_ERR_SERDSRCH:
126 		return ("specified serd engine not present in module");
127 	case FMD_ADM_ERR_SERDFIRED:
128 		return ("specified serd engine has already fired");
129 	case FMD_ADM_ERR_ROTSRCH:
130 		return ("invalid log file name");
131 	case FMD_ADM_ERR_ROTFAIL:
132 		return ("failed to rotate log file (consult fmd(1M) log)");
133 	case FMD_ADM_ERR_ROTBUSY:
134 		return ("log file is too busy to rotate (try again later)");
135 	case FMD_ADM_ERR_CASESRCH:
136 		return ("specified UUID is invalid or has been repaired");
137 	case FMD_ADM_ERR_CASEOPEN:
138 		return ("specified UUID is still being diagnosed");
139 	case FMD_ADM_ERR_XPRTSRCH:
140 		return ("specified transport ID is invalid or has been closed");
141 	case FMD_ADM_ERR_CASEXPRT:
142 		return ("specified UUID is owned by a different fault manager");
143 	default:
144 		return ("unknown fault manager error");
145 	}
146 }
147 
148 const char *
149 fmd_adm_errmsg(fmd_adm_t *ap)
150 {
151 	if (ap == NULL) {
152 		switch (errno) {
153 		case ENOTSUP:
154 			return ("client requires newer libfmd_adm version");
155 		case EPROTO:
156 			return (clnt_spcreateerror("failed to connect to fmd"));
157 		}
158 	}
159 
160 	switch (ap ? ap->adm_errno : errno) {
161 	case EPROTO:
162 		return (clnt_sperror(ap->adm_clnt, "rpc call failed"));
163 	case EREMOTE:
164 		return (fmd_adm_svc_errmsg(ap->adm_svcerr));
165 	default:
166 		return (strerror(ap->adm_errno));
167 	}
168 }
169 
170 static int
171 fmd_adm_set_svcerr(fmd_adm_t *ap, enum fmd_adm_error err)
172 {
173 	if (err != 0) {
174 		ap->adm_svcerr = err;
175 		ap->adm_errno = EREMOTE;
176 		return (-1);
177 	} else {
178 		ap->adm_svcerr = err;
179 		ap->adm_errno = 0;
180 		return (0);
181 	}
182 }
183 
184 static int
185 fmd_adm_set_errno(fmd_adm_t *ap, int err)
186 {
187 	ap->adm_errno = err;
188 	errno = err;
189 	return (-1);
190 }
191 
192 static int
193 fmd_adm_stats_cmp(const void *lp, const void *rp)
194 {
195 	return (strcmp(((fmd_stat_t *)lp)->fmds_name,
196 	    ((fmd_stat_t *)rp)->fmds_name));
197 }
198 
199 /*
200  * If the server (fmd) is restarted, this will cause all future door calls to
201  * fail.  Unfortunately, once the server comes back up, we have no way of
202  * reestablishing the connection.  To get around this, if the error indicates
203  * that the RPC call failed, we reopen the client handle and try again.  For
204  * simplicity we only deal with the door case, as it's unclear whether the
205  * remote case suffers from the same pathology.
206  */
207 boolean_t
208 fmd_adm_retry(fmd_adm_t *ap, enum clnt_stat cs, uint_t *retries)
209 {
210 	CLIENT *c;
211 	struct rpc_err err;
212 
213 	if (cs == RPC_SUCCESS || *retries == ap->adm_maxretries)
214 		return (B_FALSE);
215 
216 	clnt_geterr(ap->adm_clnt, &err);
217 	if (err.re_status != RPC_CANTSEND)
218 		return (B_FALSE);
219 
220 	if ((c = clnt_door_create(ap->adm_prog, FMD_ADM_VERSION_1,
221 	    _fmd_adm_bufsize)) == NULL)
222 		return (B_FALSE);
223 
224 	(*retries)++;
225 
226 	clnt_destroy(ap->adm_clnt);
227 	ap->adm_clnt = c;
228 
229 	return (B_TRUE);
230 }
231 
232 int
233 fmd_adm_stats_read(fmd_adm_t *ap, const char *name, fmd_adm_stats_t *sp)
234 {
235 	struct fmd_rpc_modstat rms;
236 	enum clnt_stat cs;
237 	uint_t retries = 0;
238 
239 	if (sp == NULL)
240 		return (fmd_adm_set_errno(ap, EINVAL));
241 
242 	bzero(&rms, sizeof (rms)); /* tell xdr to allocate memory for us */
243 
244 	do {
245 		if (name != NULL)
246 			cs = fmd_adm_modcstat_1((char *)name, &rms,
247 			    ap->adm_clnt);
248 		else
249 			cs = fmd_adm_modgstat_1(&rms, ap->adm_clnt);
250 	} while (fmd_adm_retry(ap, cs, &retries));
251 
252 	if (cs != RPC_SUCCESS)
253 		return (fmd_adm_set_errno(ap, EPROTO));
254 
255 	if (rms.rms_err != 0) {
256 		xdr_free(xdr_fmd_rpc_modstat, (char *)&rms);
257 		return (fmd_adm_set_svcerr(ap, rms.rms_err));
258 	}
259 
260 	sp->ams_buf = rms.rms_buf.rms_buf_val;
261 	sp->ams_len = rms.rms_buf.rms_buf_len;
262 
263 	if (sp->ams_len != 0) {
264 		qsort(sp->ams_buf, sp->ams_len,
265 		    sizeof (fmd_stat_t), fmd_adm_stats_cmp);
266 	}
267 
268 	return (0);
269 }
270 
271 int
272 fmd_adm_stats_free(fmd_adm_t *ap, fmd_adm_stats_t *sp)
273 {
274 	struct fmd_rpc_modstat rms;
275 
276 	if (sp == NULL)
277 		return (fmd_adm_set_errno(ap, EINVAL));
278 
279 	rms.rms_buf.rms_buf_val = sp->ams_buf;
280 	rms.rms_buf.rms_buf_len = sp->ams_len;
281 	rms.rms_err = 0;
282 
283 	xdr_free(xdr_fmd_rpc_modstat, (char *)&rms);
284 	bzero(sp, sizeof (fmd_adm_stats_t));
285 
286 	return (0);
287 }
288 
289 static int
290 fmd_adm_module_cmp(const void *lp, const void *rp)
291 {
292 	return (strcmp((*(struct fmd_rpc_modinfo **)lp)->rmi_name,
293 	    (*(struct fmd_rpc_modinfo **)rp)->rmi_name));
294 }
295 
296 int
297 fmd_adm_module_iter(fmd_adm_t *ap, fmd_adm_module_f *func, void *arg)
298 {
299 	struct fmd_rpc_modinfo *rmi, **rms, **rmp;
300 	struct fmd_rpc_modlist rml;
301 	fmd_adm_modinfo_t ami;
302 	enum clnt_stat cs;
303 	uint_t retries = 0;
304 
305 	bzero(&rml, sizeof (rml)); /* tell xdr to allocate memory for us */
306 
307 	do {
308 		cs = fmd_adm_modinfo_1(&rml, ap->adm_clnt);
309 	} while (fmd_adm_retry(ap, cs, &retries));
310 
311 	if (cs != RPC_SUCCESS)
312 		return (fmd_adm_set_errno(ap, EPROTO));
313 
314 	if (rml.rml_err != 0 || rml.rml_len == 0) {
315 		xdr_free(xdr_fmd_rpc_modlist, (char *)&rml);
316 		return (fmd_adm_set_svcerr(ap, rml.rml_err));
317 	}
318 
319 	if ((rms = rmp = malloc(sizeof (void *) * rml.rml_len)) == NULL) {
320 		xdr_free(xdr_fmd_rpc_modlist, (char *)&rml);
321 		return (fmd_adm_set_errno(ap, EAGAIN));
322 	}
323 
324 	for (rmi = rml.rml_list; rmi != NULL; rmi = rmi->rmi_next)
325 		*rmp++ = rmi; /* store copy of pointer in array for sorting */
326 
327 	qsort(rms, rml.rml_len, sizeof (void *), fmd_adm_module_cmp);
328 
329 	for (rmp = rms; rmp < rms + rml.rml_len; rmp++) {
330 		rmi = *rmp;
331 
332 		ami.ami_name = rmi->rmi_name;
333 		ami.ami_desc = rmi->rmi_desc;
334 		ami.ami_vers = rmi->rmi_vers;
335 		ami.ami_flags = 0;
336 
337 		if (rmi->rmi_faulty)
338 			ami.ami_flags |= FMD_ADM_MOD_FAILED;
339 
340 		if (func(&ami, arg) != 0)
341 			break;
342 	}
343 
344 	free(rms);
345 	xdr_free(xdr_fmd_rpc_modlist, (char *)&rml);
346 	return (0);
347 }
348 
349 int
350 fmd_adm_module_load(fmd_adm_t *ap, const char *path)
351 {
352 	char *str = (char *)path;
353 	int err;
354 	enum clnt_stat cs;
355 	uint_t retries = 0;
356 
357 	if (path == NULL || path[0] != '/')
358 		return (fmd_adm_set_errno(ap, EINVAL));
359 
360 	do {
361 		cs = fmd_adm_modload_1(str, &err, ap->adm_clnt);
362 	} while (fmd_adm_retry(ap, cs, &retries));
363 
364 	if (cs != RPC_SUCCESS)
365 		return (fmd_adm_set_errno(ap, EPROTO));
366 
367 	return (fmd_adm_set_svcerr(ap, err));
368 }
369 
370 int
371 fmd_adm_module_unload(fmd_adm_t *ap, const char *name)
372 {
373 	char *str = (char *)name;
374 	int err;
375 	enum clnt_stat cs;
376 	uint_t retries = 0;
377 
378 	if (name == NULL || strchr(name, '/') != NULL)
379 		return (fmd_adm_set_errno(ap, EINVAL));
380 
381 	do {
382 		cs = fmd_adm_modunload_1(str, &err, ap->adm_clnt);
383 	} while (fmd_adm_retry(ap, cs, &retries));
384 
385 	if (cs != RPC_SUCCESS)
386 		return (fmd_adm_set_errno(ap, EPROTO));
387 
388 	return (fmd_adm_set_svcerr(ap, err));
389 }
390 
391 int
392 fmd_adm_module_reset(fmd_adm_t *ap, const char *name)
393 {
394 	char *str = (char *)name;
395 	int err;
396 	enum clnt_stat cs;
397 	uint_t retries = 0;
398 
399 	if (name == NULL || strchr(name, '/') != NULL)
400 		return (fmd_adm_set_errno(ap, EINVAL));
401 
402 	do {
403 		cs = fmd_adm_modreset_1(str, &err, ap->adm_clnt);
404 	} while (fmd_adm_retry(ap, cs, &retries));
405 
406 	if (cs != RPC_SUCCESS)
407 		return (fmd_adm_set_errno(ap, EPROTO));
408 
409 	return (fmd_adm_set_svcerr(ap, err));
410 }
411 
412 int
413 fmd_adm_module_gc(fmd_adm_t *ap, const char *name)
414 {
415 	char *str = (char *)name;
416 	int err;
417 	enum clnt_stat cs;
418 	uint_t retries = 0;
419 
420 	if (name == NULL || strchr(name, '/') != NULL)
421 		return (fmd_adm_set_errno(ap, EINVAL));
422 
423 	do {
424 		cs = fmd_adm_modgc_1(str, &err, ap->adm_clnt);
425 	} while (fmd_adm_retry(ap, cs, &retries));
426 
427 	if (cs != RPC_SUCCESS)
428 		return (fmd_adm_set_errno(ap, EPROTO));
429 
430 	return (fmd_adm_set_svcerr(ap, err));
431 }
432 
433 int
434 fmd_adm_module_stats(fmd_adm_t *ap, const char *name, fmd_adm_stats_t *sp)
435 {
436 	struct fmd_rpc_modstat rms;
437 	enum clnt_stat cs;
438 	uint_t retries = 0;
439 
440 	if (name == NULL || sp == NULL)
441 		return (fmd_adm_set_errno(ap, EINVAL));
442 
443 	bzero(&rms, sizeof (rms)); /* tell xdr to allocate memory for us */
444 
445 	do {
446 		cs = fmd_adm_moddstat_1((char *)name, &rms, ap->adm_clnt);
447 	} while (fmd_adm_retry(ap, cs, &retries));
448 
449 	if (cs != RPC_SUCCESS)
450 		return (fmd_adm_set_errno(ap, EPROTO));
451 
452 	if (rms.rms_err != 0) {
453 		xdr_free(xdr_fmd_rpc_modstat, (char *)&rms);
454 		return (fmd_adm_set_svcerr(ap, rms.rms_err));
455 	}
456 
457 	sp->ams_buf = rms.rms_buf.rms_buf_val;
458 	sp->ams_len = rms.rms_buf.rms_buf_len;
459 
460 	return (0);
461 }
462 
463 int
464 fmd_adm_rsrc_count(fmd_adm_t *ap, int all, uint32_t *rcp)
465 {
466 	struct fmd_rpc_rsrclist rrl;
467 	enum clnt_stat cs;
468 	uint_t retries = 0;
469 
470 	if (rcp == NULL)
471 		return (fmd_adm_set_errno(ap, EINVAL));
472 
473 	bzero(&rrl, sizeof (rrl)); /* tell xdr to allocate memory for us */
474 
475 	do {
476 		cs = fmd_adm_rsrclist_1(all, &rrl, ap->adm_clnt);
477 	} while (fmd_adm_retry(ap, cs, &retries));
478 
479 	if (cs != RPC_SUCCESS)
480 		return (fmd_adm_set_errno(ap, EPROTO));
481 
482 	if (rrl.rrl_err != 0) {
483 		xdr_free(xdr_fmd_rpc_rsrclist, (char *)&rrl);
484 		return (fmd_adm_set_svcerr(ap, rrl.rrl_err));
485 	}
486 
487 	*rcp = rrl.rrl_cnt;
488 	xdr_free(xdr_fmd_rpc_rsrclist, (char *)&rrl);
489 	return (0);
490 }
491 
492 static int
493 fmd_adm_rsrc_cmp(const void *lp, const void *rp)
494 {
495 	return (strcmp(*(char **)lp, *(char **)rp));
496 }
497 
498 int
499 fmd_adm_rsrc_iter(fmd_adm_t *ap, int all, fmd_adm_rsrc_f *func, void *arg)
500 {
501 	struct fmd_rpc_rsrclist rrl;
502 	struct fmd_rpc_rsrcinfo rri;
503 	fmd_adm_rsrcinfo_t ari;
504 	char **fmris, *p;
505 	int i, rv;
506 	enum clnt_stat cs;
507 	uint_t retries = 0;
508 
509 	bzero(&rrl, sizeof (rrl)); /* tell xdr to allocate memory for us */
510 
511 	do {
512 		cs = fmd_adm_rsrclist_1(all, &rrl, ap->adm_clnt);
513 	} while (fmd_adm_retry(ap, cs, &retries));
514 
515 	if (cs != RPC_SUCCESS)
516 		return (fmd_adm_set_errno(ap, EPROTO));
517 
518 	if (rrl.rrl_err != 0) {
519 		xdr_free(xdr_fmd_rpc_rsrclist, (char *)&rrl);
520 		return (fmd_adm_set_svcerr(ap, rrl.rrl_err));
521 	}
522 
523 	if ((fmris = malloc(sizeof (char *) * rrl.rrl_cnt)) == NULL) {
524 		xdr_free(xdr_fmd_rpc_rsrclist, (char *)&rrl);
525 		return (fmd_adm_set_errno(ap, EAGAIN));
526 	}
527 
528 	/*
529 	 * The fmd_adm_rsrclist_1 request returns an opaque XDR buffer that is
530 	 * a string table of FMRIs (e.g. "fmriA\0fmriB\0...") where rrl_cnt is
531 	 * the number of strings in the table and rrl_buf_val is its address.
532 	 * We construct an array of pointers into the string table and sort it.
533 	 */
534 	p = rrl.rrl_buf.rrl_buf_val;
535 
536 	for (i = 0; i < rrl.rrl_cnt; i++, p += strlen(p) + 1)
537 		fmris[i] = p; /* store fmri pointer in array for sorting */
538 
539 	qsort(fmris, rrl.rrl_cnt, sizeof (char *), fmd_adm_rsrc_cmp);
540 
541 	/*
542 	 * For each FMRI in the resource cache snapshot, use fmd_adm_rsrcinfo_1
543 	 * to get more information and the invoke the callback function.  If
544 	 * FMD_ADM_ERR_RSRCSRCH is returned, the FMRI has been purged from the
545 	 * cache since our snapshot: this error is therefore silently ignored.
546 	 */
547 	for (i = 0; i < rrl.rrl_cnt; i++) {
548 		bzero(&rri, sizeof (rri));
549 
550 		retries = 0;
551 		do {
552 			cs = fmd_adm_rsrcinfo_1(fmris[i], &rri, ap->adm_clnt);
553 		} while (fmd_adm_retry(ap, cs, &retries));
554 
555 		if (cs != RPC_SUCCESS) {
556 			free(fmris);
557 			xdr_free(xdr_fmd_rpc_rsrclist, (char *)&rrl);
558 			return (fmd_adm_set_errno(ap, EPROTO));
559 		}
560 
561 		if (rri.rri_err != 0 && rri.rri_err != FMD_ADM_ERR_RSRCSRCH) {
562 			xdr_free(xdr_fmd_rpc_rsrcinfo, (char *)&rri);
563 			free(fmris);
564 			xdr_free(xdr_fmd_rpc_rsrclist, (char *)&rrl);
565 			return (fmd_adm_set_svcerr(ap, rri.rri_err));
566 		}
567 
568 		if (rri.rri_err == FMD_ADM_ERR_RSRCSRCH) {
569 			xdr_free(xdr_fmd_rpc_rsrcinfo, (char *)&rri);
570 			continue;
571 		}
572 
573 		ari.ari_fmri = rri.rri_fmri;
574 		ari.ari_uuid = rri.rri_uuid;
575 		ari.ari_case = rri.rri_case;
576 		ari.ari_flags = 0;
577 
578 		if (rri.rri_faulty)
579 			ari.ari_flags |= FMD_ADM_RSRC_FAULTY;
580 		if (rri.rri_unusable)
581 			ari.ari_flags |= FMD_ADM_RSRC_UNUSABLE;
582 		if (rri.rri_invisible)
583 			ari.ari_flags |= FMD_ADM_RSRC_INVISIBLE;
584 
585 		rv = func(&ari, arg);
586 		xdr_free(xdr_fmd_rpc_rsrcinfo, (char *)&rri);
587 
588 		if (rv != 0)
589 			break;
590 	}
591 
592 	free(fmris);
593 	xdr_free(xdr_fmd_rpc_rsrclist, (char *)&rrl);
594 	return (0);
595 }
596 
597 int
598 fmd_adm_rsrc_flush(fmd_adm_t *ap, const char *fmri)
599 {
600 	char *str = (char *)fmri;
601 	int err;
602 	enum clnt_stat cs;
603 	uint_t retries = 0;
604 
605 	if (fmri == NULL)
606 		return (fmd_adm_set_errno(ap, EINVAL));
607 
608 	do {
609 		cs = fmd_adm_rsrcflush_1(str, &err, ap->adm_clnt);
610 	} while (fmd_adm_retry(ap, cs, &retries));
611 
612 	if (cs != RPC_SUCCESS)
613 		return (fmd_adm_set_errno(ap, EPROTO));
614 
615 	return (fmd_adm_set_svcerr(ap, err));
616 }
617 
618 int
619 fmd_adm_rsrc_repair(fmd_adm_t *ap, const char *fmri)
620 {
621 	char *str = (char *)fmri;
622 	int err;
623 	enum clnt_stat cs;
624 	uint_t retries = 0;
625 
626 	if (fmri == NULL)
627 		return (fmd_adm_set_errno(ap, EINVAL));
628 
629 	do {
630 		cs = fmd_adm_rsrcrepair_1(str, &err, ap->adm_clnt);
631 	} while (fmd_adm_retry(ap, cs, &retries));
632 
633 	if (cs != RPC_SUCCESS)
634 		return (fmd_adm_set_errno(ap, EPROTO));
635 
636 	return (fmd_adm_set_svcerr(ap, err));
637 }
638 
639 int
640 fmd_adm_case_repair(fmd_adm_t *ap, const char *uuid)
641 {
642 	char *str = (char *)uuid;
643 	int err;
644 	enum clnt_stat cs;
645 	uint_t retries = 0;
646 
647 	if (uuid == NULL)
648 		return (fmd_adm_set_errno(ap, EINVAL));
649 
650 	do {
651 		cs = fmd_adm_caserepair_1(str, &err, ap->adm_clnt);
652 	} while (fmd_adm_retry(ap, cs, &retries));
653 
654 	if (cs != RPC_SUCCESS)
655 		return (fmd_adm_set_errno(ap, EPROTO));
656 
657 	return (fmd_adm_set_svcerr(ap, err));
658 }
659 
660 static int
661 fmd_adm_case_cmp(const void *lp, const void *rp)
662 {
663 	return (strcmp(*(char **)lp, *(char **)rp));
664 }
665 
666 static int
667 fmd_adm_case_one(fmd_adm_caseinfo_t *acp, const char *url_token,
668     fmd_adm_case_f *func, void *arg)
669 {
670 	char *p, *urlcode, *dict, *olang;
671 	const char *url;
672 	size_t	len;
673 
674 	if ((p = strchr(acp->aci_code, '-')) == NULL ||
675 	    p == acp->aci_code) {
676 		acp->aci_url = NULL;
677 	} else {
678 		dict = alloca((size_t)(p - acp->aci_code) + 1);
679 		(void) strncpy(dict, acp->aci_code,
680 		    (size_t)(p - acp->aci_code));
681 		dict[(size_t)(p - acp->aci_code)] = '\0';
682 
683 		/*
684 		 * If we're given a token to use in looking up the URL, try
685 		 * to use it.  Otherwise, or if we don't find it that way,
686 		 * use the fallback.
687 		 */
688 		if (url_token == NULL) {
689 			url = _url_fallback;
690 		} else if ((url = dgettext(dict, url_token)) == url_token) {
691 			/*
692 			 * We didn't find a translation in the
693 			 * dictionary for the current language.  Fall
694 			 * back to C and try again.
695 			 */
696 			olang = setlocale(LC_MESSAGES, NULL);
697 			(void) setlocale(LC_MESSAGES, "C");
698 			if ((url = dgettext(dict, url_token)) == url_token)
699 				url = _url_fallback;
700 			(void) setlocale(LC_MESSAGES, olang);
701 		}
702 		len = strlen(url);
703 		if (url[len - 1] == '/') {
704 			len += strlen(acp->aci_code) + 1;
705 			urlcode = alloca(len);
706 			(void) snprintf(urlcode, len, "%s%s", url,
707 			    acp->aci_code);
708 		} else {
709 			urlcode = (char *)url;
710 		}
711 		acp->aci_url = urlcode;
712 	}
713 
714 	return (func(acp, arg));
715 }
716 
717 /*
718  * Our approach to cases is the same as for resources: we first obtain a
719  * list of UUIDs, sort them, then obtain the case information for each.
720  */
721 int
722 fmd_adm_case_iter(fmd_adm_t *ap, const char *url_token, fmd_adm_case_f *func,
723     void *arg)
724 {
725 	struct fmd_rpc_caselist rcl;
726 	struct fmd_rpc_caseinfo rci;
727 	fmd_adm_caseinfo_t aci;
728 	char **uuids, *p;
729 	int i, rv;
730 	enum clnt_stat cs;
731 	uint_t retries = 0;
732 
733 	bzero(&rcl, sizeof (rcl)); /* tell xdr to allocate memory for us */
734 
735 	do {
736 		cs = fmd_adm_caselist_1(&rcl, ap->adm_clnt);
737 	} while (fmd_adm_retry(ap, cs, &retries));
738 
739 	if (cs != RPC_SUCCESS)
740 		return (fmd_adm_set_errno(ap, EPROTO));
741 
742 	if (rcl.rcl_err != 0) {
743 		xdr_free(xdr_fmd_rpc_caselist, (char *)&rcl);
744 		return (fmd_adm_set_svcerr(ap, rcl.rcl_err));
745 	}
746 
747 	if ((uuids = malloc(sizeof (char *) * rcl.rcl_cnt)) == NULL) {
748 		xdr_free(xdr_fmd_rpc_caselist, (char *)&rcl);
749 		return (fmd_adm_set_errno(ap, EAGAIN));
750 	}
751 
752 	p = rcl.rcl_buf.rcl_buf_val;
753 
754 	for (i = 0; i < rcl.rcl_cnt; i++, p += strlen(p) + 1)
755 		uuids[i] = p;
756 
757 	qsort(uuids, rcl.rcl_cnt, sizeof (char *), fmd_adm_case_cmp);
758 
759 	for (i = 0; i < rcl.rcl_cnt; i++) {
760 		bzero(&rci, sizeof (rci));
761 
762 		retries = 0;
763 		do {
764 			cs = fmd_adm_caseinfo_1(uuids[i], &rci, ap->adm_clnt);
765 		} while (fmd_adm_retry(ap, cs, &retries));
766 
767 		if (cs != RPC_SUCCESS) {
768 			free(uuids);
769 			xdr_free(xdr_fmd_rpc_caselist, (char *)&rcl);
770 			return (fmd_adm_set_errno(ap, EPROTO));
771 		}
772 
773 		if (rci.rci_err != 0 && rci.rci_err != FMD_ADM_ERR_CASESRCH) {
774 			xdr_free(xdr_fmd_rpc_caseinfo, (char *)&rci);
775 			free(uuids);
776 			xdr_free(xdr_fmd_rpc_caselist, (char *)&rcl);
777 			return (fmd_adm_set_svcerr(ap, rci.rci_err));
778 		}
779 
780 		if (rci.rci_err == FMD_ADM_ERR_CASESRCH) {
781 			xdr_free(xdr_fmd_rpc_caseinfo, (char *)&rci);
782 			continue;
783 		}
784 
785 		bzero(&aci, sizeof (aci));
786 
787 		if ((rv = nvlist_unpack(rci.rci_evbuf.rci_evbuf_val,
788 		    rci.rci_evbuf.rci_evbuf_len, &aci.aci_event, 0)) != 0) {
789 			xdr_free(xdr_fmd_rpc_caseinfo, (char *)&rci);
790 			free(uuids);
791 			xdr_free(xdr_fmd_rpc_caselist, (char *)&rcl);
792 			return (fmd_adm_set_errno(ap, rv));
793 		}
794 
795 		if ((rv = nvlist_lookup_string(aci.aci_event, FM_SUSPECT_UUID,
796 		    (char **)&aci.aci_uuid)) != 0) {
797 			xdr_free(xdr_fmd_rpc_caseinfo, (char *)&rci);
798 			free(uuids);
799 			xdr_free(xdr_fmd_rpc_caselist, (char *)&rcl);
800 			nvlist_free(aci.aci_event);
801 			return (fmd_adm_set_errno(ap, rv));
802 		}
803 		if ((rv = nvlist_lookup_string(aci.aci_event,
804 		    FM_SUSPECT_DIAG_CODE, (char **)&aci.aci_code)) != 0) {
805 			xdr_free(xdr_fmd_rpc_caseinfo, (char *)&rci);
806 			free(uuids);
807 			xdr_free(xdr_fmd_rpc_caselist, (char *)&rcl);
808 			nvlist_free(aci.aci_event);
809 			return (fmd_adm_set_errno(ap, rv));
810 		}
811 
812 		rv = fmd_adm_case_one(&aci, url_token, func, arg);
813 
814 		xdr_free(xdr_fmd_rpc_caseinfo, (char *)&rci);
815 		nvlist_free(aci.aci_event);
816 
817 		if (rv != 0)
818 			break;
819 	}
820 
821 	free(uuids);
822 	xdr_free(xdr_fmd_rpc_caselist, (char *)&rcl);
823 	return (0);
824 }
825 
826 static int
827 fmd_adm_serd_cmp(const void *lp, const void *rp)
828 {
829 	return (strcmp((*(struct fmd_rpc_serdinfo **)lp)->rsi_name,
830 	    (*(struct fmd_rpc_serdinfo **)rp)->rsi_name));
831 }
832 
833 int
834 fmd_adm_serd_iter(fmd_adm_t *ap, const char *name,
835     fmd_adm_serd_f *func, void *arg)
836 {
837 	struct fmd_rpc_serdinfo *rsi, **ris, **rip;
838 	struct fmd_rpc_serdlist rsl;
839 	fmd_adm_serdinfo_t asi;
840 	enum clnt_stat cs;
841 	uint_t retries = 0;
842 
843 	bzero(&rsl, sizeof (rsl)); /* tell xdr to allocate memory for us */
844 
845 	do {
846 		cs = fmd_adm_serdinfo_1((char *)name, &rsl, ap->adm_clnt);
847 	} while (fmd_adm_retry(ap, cs, &retries));
848 
849 	if (cs != RPC_SUCCESS)
850 		return (fmd_adm_set_errno(ap, EPROTO));
851 
852 	if (rsl.rsl_err != 0 || rsl.rsl_len == 0) {
853 		xdr_free(xdr_fmd_rpc_serdlist, (char *)&rsl);
854 		return (fmd_adm_set_svcerr(ap, rsl.rsl_err));
855 	}
856 
857 	if ((ris = rip = malloc(sizeof (void *) * rsl.rsl_len)) == NULL) {
858 		xdr_free(xdr_fmd_rpc_serdlist, (char *)&rsl);
859 		return (fmd_adm_set_errno(ap, EAGAIN));
860 	}
861 
862 	for (rsi = rsl.rsl_list; rsi != NULL; rsi = rsi->rsi_next)
863 		*rip++ = rsi; /* store copy of pointer in array for sorting */
864 
865 	qsort(ris, rsl.rsl_len, sizeof (void *), fmd_adm_serd_cmp);
866 
867 	for (rip = ris; rip < ris + rsl.rsl_len; rip++) {
868 		rsi = *rip;
869 
870 		asi.asi_name = rsi->rsi_name;
871 		asi.asi_delta = rsi->rsi_delta;
872 		asi.asi_n = rsi->rsi_n;
873 		asi.asi_t = rsi->rsi_t;
874 		asi.asi_count = rsi->rsi_count;
875 		asi.asi_flags = 0;
876 
877 		if (rsi->rsi_fired)
878 			asi.asi_flags |= FMD_ADM_SERD_FIRED;
879 
880 		if (func(&asi, arg) != 0)
881 			break;
882 	}
883 
884 	free(ris);
885 	xdr_free(xdr_fmd_rpc_serdlist, (char *)&rsl);
886 	return (0);
887 }
888 
889 int
890 fmd_adm_serd_reset(fmd_adm_t *ap, const char *mod, const char *name)
891 {
892 	char *s1 = (char *)mod, *s2 = (char *)name;
893 	int err;
894 	enum clnt_stat cs;
895 	uint_t retries = 0;
896 
897 	if (mod == NULL || name == NULL || strchr(mod, '/') != NULL)
898 		return (fmd_adm_set_errno(ap, EINVAL));
899 
900 	do {
901 		cs = fmd_adm_serdreset_1(s1, s2, &err, ap->adm_clnt);
902 	} while (fmd_adm_retry(ap, cs, &retries));
903 
904 	if (cs != RPC_SUCCESS)
905 		return (fmd_adm_set_errno(ap, EPROTO));
906 
907 	return (fmd_adm_set_svcerr(ap, err));
908 }
909 
910 int
911 fmd_adm_xprt_iter(fmd_adm_t *ap, fmd_adm_xprt_f *func, void *arg)
912 {
913 	struct fmd_rpc_xprtlist rxl;
914 	uint_t i;
915 	enum clnt_stat cs;
916 	uint_t retries = 0;
917 
918 	bzero(&rxl, sizeof (rxl)); /* tell xdr to allocate memory for us */
919 
920 	do {
921 		cs = fmd_adm_xprtlist_1(&rxl, ap->adm_clnt);
922 	} while (fmd_adm_retry(ap, cs, &retries));
923 
924 	if (cs != RPC_SUCCESS)
925 		return (fmd_adm_set_errno(ap, EPROTO));
926 
927 	if (rxl.rxl_err != 0) {
928 		xdr_free(xdr_fmd_rpc_xprtlist, (char *)&rxl);
929 		return (fmd_adm_set_svcerr(ap, rxl.rxl_err));
930 	}
931 
932 	for (i = 0; i < rxl.rxl_len; i++)
933 		func(rxl.rxl_buf.rxl_buf_val[i], arg);
934 
935 	xdr_free(xdr_fmd_rpc_xprtlist, (char *)&rxl);
936 	return (0);
937 }
938 
939 int
940 fmd_adm_xprt_stats(fmd_adm_t *ap, id_t id, fmd_adm_stats_t *sp)
941 {
942 	struct fmd_rpc_modstat rms;
943 	enum clnt_stat cs;
944 	uint_t retries = 0;
945 
946 	if (sp == NULL)
947 		return (fmd_adm_set_errno(ap, EINVAL));
948 
949 	bzero(&rms, sizeof (rms)); /* tell xdr to allocate memory for us */
950 
951 	do {
952 		cs = fmd_adm_xprtstat_1(id, &rms, ap->adm_clnt);
953 	} while (fmd_adm_retry(ap, cs, &retries));
954 
955 	if (cs != RPC_SUCCESS)
956 		return (fmd_adm_set_errno(ap, EPROTO));
957 
958 	if (rms.rms_err != 0) {
959 		xdr_free(xdr_fmd_rpc_modstat, (char *)&rms);
960 		return (fmd_adm_set_svcerr(ap, rms.rms_err));
961 	}
962 
963 	sp->ams_buf = rms.rms_buf.rms_buf_val;
964 	sp->ams_len = rms.rms_buf.rms_buf_len;
965 
966 	return (0);
967 }
968 
969 int
970 fmd_adm_log_rotate(fmd_adm_t *ap, const char *log)
971 {
972 	int err;
973 	enum clnt_stat cs;
974 	uint_t retries = 0;
975 
976 	if (log == NULL)
977 		return (fmd_adm_set_errno(ap, EINVAL));
978 
979 	do {
980 		cs = fmd_adm_logrotate_1((char *)log, &err, ap->adm_clnt);
981 	} while (fmd_adm_retry(ap, cs, &retries));
982 
983 	if (cs != RPC_SUCCESS)
984 		return (fmd_adm_set_errno(ap, EPROTO));
985 
986 	return (fmd_adm_set_svcerr(ap, err));
987 }
988 
989 /*
990  * Custom XDR routine for our API structure fmd_stat_t.  This function must
991  * match the definition of fmd_stat_t in <fm/fmd_api.h> and must also match
992  * the corresponding routine in usr/src/cmd/fm/fmd/common/fmd_rpc_adm.c.
993  */
994 bool_t
995 xdr_fmd_stat(XDR *xp, fmd_stat_t *sp)
996 {
997 	bool_t rv = TRUE;
998 
999 	rv &= xdr_opaque(xp, sp->fmds_name, sizeof (sp->fmds_name));
1000 	rv &= xdr_u_int(xp, &sp->fmds_type);
1001 	rv &= xdr_opaque(xp, sp->fmds_desc, sizeof (sp->fmds_desc));
1002 
1003 	switch (sp->fmds_type) {
1004 	case FMD_TYPE_BOOL:
1005 		rv &= xdr_int(xp, &sp->fmds_value.bool);
1006 		break;
1007 	case FMD_TYPE_INT32:
1008 		rv &= xdr_int32_t(xp, &sp->fmds_value.i32);
1009 		break;
1010 	case FMD_TYPE_UINT32:
1011 		rv &= xdr_uint32_t(xp, &sp->fmds_value.ui32);
1012 		break;
1013 	case FMD_TYPE_INT64:
1014 		rv &= xdr_int64_t(xp, &sp->fmds_value.i64);
1015 		break;
1016 	case FMD_TYPE_UINT64:
1017 	case FMD_TYPE_TIME:
1018 	case FMD_TYPE_SIZE:
1019 		rv &= xdr_uint64_t(xp, &sp->fmds_value.ui64);
1020 		break;
1021 	case FMD_TYPE_STRING:
1022 		rv &= xdr_string(xp, &sp->fmds_value.str, ~0);
1023 		break;
1024 	}
1025 
1026 	return (rv);
1027 }
1028