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_repaired(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_rsrcrepaired_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_rsrc_replaced(fmd_adm_t *ap, const char *fmri)
641 {
642 	char *str = (char *)fmri;
643 	int err;
644 	enum clnt_stat cs;
645 	uint_t retries = 0;
646 
647 	if (fmri == NULL)
648 		return (fmd_adm_set_errno(ap, EINVAL));
649 
650 	do {
651 		cs = fmd_adm_rsrcreplaced_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 int
661 fmd_adm_rsrc_acquit(fmd_adm_t *ap, const char *fmri, const char *uuid)
662 {
663 	char *str = (char *)fmri;
664 	char *str2 = (char *)uuid;
665 	int err;
666 	enum clnt_stat cs;
667 	uint_t retries = 0;
668 
669 	if (fmri == NULL)
670 		return (fmd_adm_set_errno(ap, EINVAL));
671 
672 	do {
673 		cs = fmd_adm_rsrcacquit_1(str, str2, &err, ap->adm_clnt);
674 	} while (fmd_adm_retry(ap, cs, &retries));
675 
676 	if (cs != RPC_SUCCESS)
677 		return (fmd_adm_set_errno(ap, EPROTO));
678 
679 	return (fmd_adm_set_svcerr(ap, err));
680 }
681 
682 int
683 fmd_adm_case_repair(fmd_adm_t *ap, const char *uuid)
684 {
685 	char *str = (char *)uuid;
686 	int err;
687 	enum clnt_stat cs;
688 	uint_t retries = 0;
689 
690 	if (uuid == NULL)
691 		return (fmd_adm_set_errno(ap, EINVAL));
692 
693 	do {
694 		cs = fmd_adm_caserepair_1(str, &err, ap->adm_clnt);
695 	} while (fmd_adm_retry(ap, cs, &retries));
696 
697 	if (cs != RPC_SUCCESS)
698 		return (fmd_adm_set_errno(ap, EPROTO));
699 
700 	return (fmd_adm_set_svcerr(ap, err));
701 }
702 
703 int
704 fmd_adm_case_acquit(fmd_adm_t *ap, const char *uuid)
705 {
706 	char *str = (char *)uuid;
707 	int err;
708 	enum clnt_stat cs;
709 	uint_t retries = 0;
710 
711 	if (uuid == NULL)
712 		return (fmd_adm_set_errno(ap, EINVAL));
713 
714 	do {
715 		cs = fmd_adm_caseacquit_1(str, &err, ap->adm_clnt);
716 	} while (fmd_adm_retry(ap, cs, &retries));
717 
718 	if (cs != RPC_SUCCESS)
719 		return (fmd_adm_set_errno(ap, EPROTO));
720 
721 	return (fmd_adm_set_svcerr(ap, err));
722 }
723 
724 static int
725 fmd_adm_case_cmp(const void *lp, const void *rp)
726 {
727 	return (strcmp(*(char **)lp, *(char **)rp));
728 }
729 
730 static int
731 fmd_adm_case_one(fmd_adm_caseinfo_t *acp, const char *url_token,
732     fmd_adm_case_f *func, void *arg)
733 {
734 	char *p, *urlcode, *dict, *olang;
735 	const char *url;
736 	size_t	len;
737 
738 	if ((p = strchr(acp->aci_code, '-')) == NULL ||
739 	    p == acp->aci_code) {
740 		acp->aci_url = NULL;
741 	} else {
742 		dict = alloca((size_t)(p - acp->aci_code) + 1);
743 		(void) strncpy(dict, acp->aci_code,
744 		    (size_t)(p - acp->aci_code));
745 		dict[(size_t)(p - acp->aci_code)] = '\0';
746 
747 		/*
748 		 * If we're given a token to use in looking up the URL, try
749 		 * to use it.  Otherwise, or if we don't find it that way,
750 		 * use the fallback.
751 		 */
752 		if (url_token == NULL) {
753 			url = _url_fallback;
754 		} else if ((url = dgettext(dict, url_token)) == url_token) {
755 			/*
756 			 * We didn't find a translation in the
757 			 * dictionary for the current language.  Fall
758 			 * back to C and try again.
759 			 */
760 			olang = setlocale(LC_MESSAGES, NULL);
761 			(void) setlocale(LC_MESSAGES, "C");
762 			if ((url = dgettext(dict, url_token)) == url_token)
763 				url = _url_fallback;
764 			(void) setlocale(LC_MESSAGES, olang);
765 		}
766 		len = strlen(url);
767 		if (url[len - 1] == '/') {
768 			len += strlen(acp->aci_code) + 1;
769 			urlcode = alloca(len);
770 			(void) snprintf(urlcode, len, "%s%s", url,
771 			    acp->aci_code);
772 		} else {
773 			urlcode = (char *)url;
774 		}
775 		acp->aci_url = urlcode;
776 	}
777 
778 	return (func(acp, arg));
779 }
780 
781 /*
782  * Our approach to cases is the same as for resources: we first obtain a
783  * list of UUIDs, sort them, then obtain the case information for each.
784  */
785 int
786 fmd_adm_case_iter(fmd_adm_t *ap, const char *url_token, fmd_adm_case_f *func,
787     void *arg)
788 {
789 	struct fmd_rpc_caselist rcl;
790 	struct fmd_rpc_caseinfo rci;
791 	fmd_adm_caseinfo_t aci;
792 	char **uuids, *p;
793 	int i, rv;
794 	enum clnt_stat cs;
795 	uint_t retries = 0;
796 
797 	bzero(&rcl, sizeof (rcl)); /* tell xdr to allocate memory for us */
798 
799 	do {
800 		cs = fmd_adm_caselist_1(&rcl, ap->adm_clnt);
801 	} while (fmd_adm_retry(ap, cs, &retries));
802 
803 	if (cs != RPC_SUCCESS)
804 		return (fmd_adm_set_errno(ap, EPROTO));
805 
806 	if (rcl.rcl_err != 0) {
807 		xdr_free(xdr_fmd_rpc_caselist, (char *)&rcl);
808 		return (fmd_adm_set_svcerr(ap, rcl.rcl_err));
809 	}
810 
811 	if ((uuids = malloc(sizeof (char *) * rcl.rcl_cnt)) == NULL) {
812 		xdr_free(xdr_fmd_rpc_caselist, (char *)&rcl);
813 		return (fmd_adm_set_errno(ap, EAGAIN));
814 	}
815 
816 	p = rcl.rcl_buf.rcl_buf_val;
817 
818 	for (i = 0; i < rcl.rcl_cnt; i++, p += strlen(p) + 1)
819 		uuids[i] = p;
820 
821 	qsort(uuids, rcl.rcl_cnt, sizeof (char *), fmd_adm_case_cmp);
822 
823 	for (i = 0; i < rcl.rcl_cnt; i++) {
824 		bzero(&rci, sizeof (rci));
825 
826 		retries = 0;
827 		do {
828 			cs = fmd_adm_caseinfo_1(uuids[i], &rci, ap->adm_clnt);
829 		} while (fmd_adm_retry(ap, cs, &retries));
830 
831 		if (cs != RPC_SUCCESS) {
832 			free(uuids);
833 			xdr_free(xdr_fmd_rpc_caselist, (char *)&rcl);
834 			return (fmd_adm_set_errno(ap, EPROTO));
835 		}
836 
837 		if (rci.rci_err != 0 && rci.rci_err != FMD_ADM_ERR_CASESRCH) {
838 			xdr_free(xdr_fmd_rpc_caseinfo, (char *)&rci);
839 			free(uuids);
840 			xdr_free(xdr_fmd_rpc_caselist, (char *)&rcl);
841 			return (fmd_adm_set_svcerr(ap, rci.rci_err));
842 		}
843 
844 		if (rci.rci_err == FMD_ADM_ERR_CASESRCH) {
845 			xdr_free(xdr_fmd_rpc_caseinfo, (char *)&rci);
846 			continue;
847 		}
848 
849 		bzero(&aci, sizeof (aci));
850 
851 		if ((rv = nvlist_unpack(rci.rci_evbuf.rci_evbuf_val,
852 		    rci.rci_evbuf.rci_evbuf_len, &aci.aci_event, 0)) != 0) {
853 			xdr_free(xdr_fmd_rpc_caseinfo, (char *)&rci);
854 			free(uuids);
855 			xdr_free(xdr_fmd_rpc_caselist, (char *)&rcl);
856 			return (fmd_adm_set_errno(ap, rv));
857 		}
858 
859 		if ((rv = nvlist_lookup_string(aci.aci_event, FM_SUSPECT_UUID,
860 		    (char **)&aci.aci_uuid)) != 0) {
861 			xdr_free(xdr_fmd_rpc_caseinfo, (char *)&rci);
862 			free(uuids);
863 			xdr_free(xdr_fmd_rpc_caselist, (char *)&rcl);
864 			nvlist_free(aci.aci_event);
865 			return (fmd_adm_set_errno(ap, rv));
866 		}
867 		if ((rv = nvlist_lookup_string(aci.aci_event,
868 		    FM_SUSPECT_DIAG_CODE, (char **)&aci.aci_code)) != 0) {
869 			xdr_free(xdr_fmd_rpc_caseinfo, (char *)&rci);
870 			free(uuids);
871 			xdr_free(xdr_fmd_rpc_caselist, (char *)&rcl);
872 			nvlist_free(aci.aci_event);
873 			return (fmd_adm_set_errno(ap, rv));
874 		}
875 
876 		rv = fmd_adm_case_one(&aci, url_token, func, arg);
877 
878 		xdr_free(xdr_fmd_rpc_caseinfo, (char *)&rci);
879 		nvlist_free(aci.aci_event);
880 
881 		if (rv != 0)
882 			break;
883 	}
884 
885 	free(uuids);
886 	xdr_free(xdr_fmd_rpc_caselist, (char *)&rcl);
887 	return (0);
888 }
889 
890 static int
891 fmd_adm_serd_cmp(const void *lp, const void *rp)
892 {
893 	return (strcmp((*(struct fmd_rpc_serdinfo **)lp)->rsi_name,
894 	    (*(struct fmd_rpc_serdinfo **)rp)->rsi_name));
895 }
896 
897 int
898 fmd_adm_serd_iter(fmd_adm_t *ap, const char *name,
899     fmd_adm_serd_f *func, void *arg)
900 {
901 	struct fmd_rpc_serdinfo *rsi, **ris, **rip;
902 	struct fmd_rpc_serdlist rsl;
903 	fmd_adm_serdinfo_t asi;
904 	enum clnt_stat cs;
905 	uint_t retries = 0;
906 
907 	bzero(&rsl, sizeof (rsl)); /* tell xdr to allocate memory for us */
908 
909 	do {
910 		cs = fmd_adm_serdinfo_1((char *)name, &rsl, ap->adm_clnt);
911 	} while (fmd_adm_retry(ap, cs, &retries));
912 
913 	if (cs != RPC_SUCCESS)
914 		return (fmd_adm_set_errno(ap, EPROTO));
915 
916 	if (rsl.rsl_err != 0 || rsl.rsl_len == 0) {
917 		xdr_free(xdr_fmd_rpc_serdlist, (char *)&rsl);
918 		return (fmd_adm_set_svcerr(ap, rsl.rsl_err));
919 	}
920 
921 	if ((ris = rip = malloc(sizeof (void *) * rsl.rsl_len)) == NULL) {
922 		xdr_free(xdr_fmd_rpc_serdlist, (char *)&rsl);
923 		return (fmd_adm_set_errno(ap, EAGAIN));
924 	}
925 
926 	for (rsi = rsl.rsl_list; rsi != NULL; rsi = rsi->rsi_next)
927 		*rip++ = rsi; /* store copy of pointer in array for sorting */
928 
929 	qsort(ris, rsl.rsl_len, sizeof (void *), fmd_adm_serd_cmp);
930 
931 	for (rip = ris; rip < ris + rsl.rsl_len; rip++) {
932 		rsi = *rip;
933 
934 		asi.asi_name = rsi->rsi_name;
935 		asi.asi_delta = rsi->rsi_delta;
936 		asi.asi_n = rsi->rsi_n;
937 		asi.asi_t = rsi->rsi_t;
938 		asi.asi_count = rsi->rsi_count;
939 		asi.asi_flags = 0;
940 
941 		if (rsi->rsi_fired)
942 			asi.asi_flags |= FMD_ADM_SERD_FIRED;
943 
944 		if (func(&asi, arg) != 0)
945 			break;
946 	}
947 
948 	free(ris);
949 	xdr_free(xdr_fmd_rpc_serdlist, (char *)&rsl);
950 	return (0);
951 }
952 
953 int
954 fmd_adm_serd_reset(fmd_adm_t *ap, const char *mod, const char *name)
955 {
956 	char *s1 = (char *)mod, *s2 = (char *)name;
957 	int err;
958 	enum clnt_stat cs;
959 	uint_t retries = 0;
960 
961 	if (mod == NULL || name == NULL || strchr(mod, '/') != NULL)
962 		return (fmd_adm_set_errno(ap, EINVAL));
963 
964 	do {
965 		cs = fmd_adm_serdreset_1(s1, s2, &err, ap->adm_clnt);
966 	} while (fmd_adm_retry(ap, cs, &retries));
967 
968 	if (cs != RPC_SUCCESS)
969 		return (fmd_adm_set_errno(ap, EPROTO));
970 
971 	return (fmd_adm_set_svcerr(ap, err));
972 }
973 
974 int
975 fmd_adm_xprt_iter(fmd_adm_t *ap, fmd_adm_xprt_f *func, void *arg)
976 {
977 	struct fmd_rpc_xprtlist rxl;
978 	uint_t i;
979 	enum clnt_stat cs;
980 	uint_t retries = 0;
981 
982 	bzero(&rxl, sizeof (rxl)); /* tell xdr to allocate memory for us */
983 
984 	do {
985 		cs = fmd_adm_xprtlist_1(&rxl, ap->adm_clnt);
986 	} while (fmd_adm_retry(ap, cs, &retries));
987 
988 	if (cs != RPC_SUCCESS)
989 		return (fmd_adm_set_errno(ap, EPROTO));
990 
991 	if (rxl.rxl_err != 0) {
992 		xdr_free(xdr_fmd_rpc_xprtlist, (char *)&rxl);
993 		return (fmd_adm_set_svcerr(ap, rxl.rxl_err));
994 	}
995 
996 	for (i = 0; i < rxl.rxl_len; i++)
997 		func(rxl.rxl_buf.rxl_buf_val[i], arg);
998 
999 	xdr_free(xdr_fmd_rpc_xprtlist, (char *)&rxl);
1000 	return (0);
1001 }
1002 
1003 int
1004 fmd_adm_xprt_stats(fmd_adm_t *ap, id_t id, fmd_adm_stats_t *sp)
1005 {
1006 	struct fmd_rpc_modstat rms;
1007 	enum clnt_stat cs;
1008 	uint_t retries = 0;
1009 
1010 	if (sp == NULL)
1011 		return (fmd_adm_set_errno(ap, EINVAL));
1012 
1013 	bzero(&rms, sizeof (rms)); /* tell xdr to allocate memory for us */
1014 
1015 	do {
1016 		cs = fmd_adm_xprtstat_1(id, &rms, ap->adm_clnt);
1017 	} while (fmd_adm_retry(ap, cs, &retries));
1018 
1019 	if (cs != RPC_SUCCESS)
1020 		return (fmd_adm_set_errno(ap, EPROTO));
1021 
1022 	if (rms.rms_err != 0) {
1023 		xdr_free(xdr_fmd_rpc_modstat, (char *)&rms);
1024 		return (fmd_adm_set_svcerr(ap, rms.rms_err));
1025 	}
1026 
1027 	sp->ams_buf = rms.rms_buf.rms_buf_val;
1028 	sp->ams_len = rms.rms_buf.rms_buf_len;
1029 
1030 	return (0);
1031 }
1032 
1033 int
1034 fmd_adm_log_rotate(fmd_adm_t *ap, const char *log)
1035 {
1036 	int err;
1037 	enum clnt_stat cs;
1038 	uint_t retries = 0;
1039 
1040 	if (log == NULL)
1041 		return (fmd_adm_set_errno(ap, EINVAL));
1042 
1043 	do {
1044 		cs = fmd_adm_logrotate_1((char *)log, &err, ap->adm_clnt);
1045 	} while (fmd_adm_retry(ap, cs, &retries));
1046 
1047 	if (cs != RPC_SUCCESS)
1048 		return (fmd_adm_set_errno(ap, EPROTO));
1049 
1050 	return (fmd_adm_set_svcerr(ap, err));
1051 }
1052 
1053 /*
1054  * Custom XDR routine for our API structure fmd_stat_t.  This function must
1055  * match the definition of fmd_stat_t in <fm/fmd_api.h> and must also match
1056  * the corresponding routine in usr/src/cmd/fm/fmd/common/fmd_rpc_adm.c.
1057  */
1058 bool_t
1059 xdr_fmd_stat(XDR *xp, fmd_stat_t *sp)
1060 {
1061 	bool_t rv = TRUE;
1062 
1063 	rv &= xdr_opaque(xp, sp->fmds_name, sizeof (sp->fmds_name));
1064 	rv &= xdr_u_int(xp, &sp->fmds_type);
1065 	rv &= xdr_opaque(xp, sp->fmds_desc, sizeof (sp->fmds_desc));
1066 
1067 	switch (sp->fmds_type) {
1068 	case FMD_TYPE_BOOL:
1069 		rv &= xdr_int(xp, &sp->fmds_value.bool);
1070 		break;
1071 	case FMD_TYPE_INT32:
1072 		rv &= xdr_int32_t(xp, &sp->fmds_value.i32);
1073 		break;
1074 	case FMD_TYPE_UINT32:
1075 		rv &= xdr_uint32_t(xp, &sp->fmds_value.ui32);
1076 		break;
1077 	case FMD_TYPE_INT64:
1078 		rv &= xdr_int64_t(xp, &sp->fmds_value.i64);
1079 		break;
1080 	case FMD_TYPE_UINT64:
1081 	case FMD_TYPE_TIME:
1082 	case FMD_TYPE_SIZE:
1083 		rv &= xdr_uint64_t(xp, &sp->fmds_value.ui64);
1084 		break;
1085 	case FMD_TYPE_STRING:
1086 		rv &= xdr_string(xp, &sp->fmds_value.str, ~0);
1087 		break;
1088 	}
1089 
1090 	return (rv);
1091 }
1092