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 <strings.h>
28 #include <limits.h>
29 #include <unistd.h>
30 #include <stdlib.h>
31 #include <alloca.h>
32 
33 #include <fmd_rpc_adm.h>
34 #include <fmd_rpc.h>
35 #include <fmd_module.h>
36 #include <fmd_ustat.h>
37 #include <fmd_error.h>
38 #include <fmd_asru.h>
39 #include <fmd_ckpt.h>
40 #include <fmd_case.h>
41 #include <fmd_fmri.h>
42 #include <fmd_idspace.h>
43 #include <fmd_xprt.h>
44 
45 #include <fmd.h>
46 
47 bool_t
48 fmd_adm_modinfo_1_svc(struct fmd_rpc_modlist *rvp, struct svc_req *req)
49 {
50 	struct fmd_rpc_modinfo *rmi;
51 	fmd_module_t *mp;
52 
53 	rvp->rml_list = NULL;
54 	rvp->rml_err = 0;
55 	rvp->rml_len = 0;
56 
57 	if (fmd_rpc_deny(req)) {
58 		rvp->rml_err = FMD_ADM_ERR_PERM;
59 		return (TRUE);
60 	}
61 
62 	(void) pthread_mutex_lock(&fmd.d_mod_lock);
63 
64 	for (mp = fmd_list_next(&fmd.d_mod_list);
65 	    mp != NULL; mp = fmd_list_next(mp)) {
66 
67 		if ((rmi = malloc(sizeof (struct fmd_rpc_modinfo))) == NULL) {
68 			rvp->rml_err = FMD_ADM_ERR_NOMEM;
69 			break;
70 		}
71 
72 		fmd_module_lock(mp);
73 
74 		/*
75 		 * If mod_info is NULL, the module is in the middle of loading:
76 		 * do not report its presence to observability tools yet.
77 		 */
78 		if (mp->mod_info == NULL) {
79 			fmd_module_unlock(mp);
80 			free(rmi);
81 			continue;
82 		}
83 
84 		rmi->rmi_name = strdup(mp->mod_name);
85 		rmi->rmi_desc = strdup(mp->mod_info->fmdi_desc);
86 		rmi->rmi_vers = strdup(mp->mod_info->fmdi_vers);
87 		rmi->rmi_faulty = mp->mod_error != 0;
88 		rmi->rmi_next = rvp->rml_list;
89 
90 		fmd_module_unlock(mp);
91 		rvp->rml_list = rmi;
92 		rvp->rml_len++;
93 
94 		if (rmi->rmi_desc == NULL || rmi->rmi_vers == NULL) {
95 			rvp->rml_err = FMD_ADM_ERR_NOMEM;
96 			break;
97 		}
98 	}
99 
100 	(void) pthread_mutex_unlock(&fmd.d_mod_lock);
101 	return (TRUE);
102 }
103 
104 bool_t
105 fmd_adm_modcstat_1_svc(char *name,
106     struct fmd_rpc_modstat *rms, struct svc_req *req)
107 {
108 	fmd_ustat_snap_t snap;
109 	fmd_module_t *mp;
110 
111 	rms->rms_buf.rms_buf_val = NULL;
112 	rms->rms_buf.rms_buf_len = 0;
113 	rms->rms_err = 0;
114 
115 	if (fmd_rpc_deny(req)) {
116 		rms->rms_err = FMD_ADM_ERR_PERM;
117 		return (TRUE);
118 	}
119 
120 	if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) == NULL) {
121 		rms->rms_err = FMD_ADM_ERR_MODSRCH;
122 		return (TRUE);
123 	}
124 
125 	if (fmd_modstat_snapshot(mp, &snap) == 0) {
126 		rms->rms_buf.rms_buf_val = snap.uss_buf;
127 		rms->rms_buf.rms_buf_len = snap.uss_len;
128 	} else if (errno == EFMD_HDL_ABORT) {
129 		rms->rms_err = FMD_ADM_ERR_MODFAIL;
130 	} else
131 		rms->rms_err = FMD_ADM_ERR_NOMEM;
132 
133 	fmd_module_rele(mp);
134 	return (TRUE);
135 }
136 
137 bool_t
138 fmd_adm_moddstat_1_svc(char *name,
139     struct fmd_rpc_modstat *rms, struct svc_req *req)
140 {
141 	fmd_module_t *mp;
142 
143 	rms->rms_buf.rms_buf_val = NULL;
144 	rms->rms_buf.rms_buf_len = 0;
145 	rms->rms_err = 0;
146 
147 	if (fmd_rpc_deny(req)) {
148 		rms->rms_err = FMD_ADM_ERR_PERM;
149 		return (TRUE);
150 	}
151 
152 	if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) == NULL) {
153 		rms->rms_err = FMD_ADM_ERR_MODSRCH;
154 		return (TRUE);
155 	}
156 
157 	rms->rms_buf.rms_buf_val = malloc(sizeof (fmd_modstat_t));
158 	rms->rms_buf.rms_buf_len = sizeof (fmd_modstat_t) / sizeof (fmd_stat_t);
159 
160 	if (rms->rms_buf.rms_buf_val == NULL) {
161 		rms->rms_err = FMD_ADM_ERR_NOMEM;
162 		rms->rms_buf.rms_buf_len = 0;
163 		fmd_module_rele(mp);
164 		return (TRUE);
165 	}
166 
167 	/*
168 	 * Note: the bcopy() here is valid only if no FMD_TYPE_STRING stats
169 	 * are present in mp->mod_stats.  We don't use any for the daemon-
170 	 * maintained stats and provide this function in order to reduce the
171 	 * overhead of the fmstat(1M) default view, where these minimal stats
172 	 * must be retrieved for all of the active modules.
173 	 */
174 	(void) pthread_mutex_lock(&mp->mod_stats_lock);
175 
176 	if (mp->mod_stats != NULL) {
177 		mp->mod_stats->ms_snaptime.fmds_value.ui64 = gethrtime();
178 		bcopy(mp->mod_stats, rms->rms_buf.rms_buf_val,
179 		    sizeof (fmd_modstat_t));
180 	} else {
181 		free(rms->rms_buf.rms_buf_val);
182 		rms->rms_buf.rms_buf_val = NULL;
183 		rms->rms_buf.rms_buf_len = 0;
184 		rms->rms_err = FMD_ADM_ERR_MODFAIL;
185 	}
186 
187 	(void) pthread_mutex_unlock(&mp->mod_stats_lock);
188 	fmd_module_rele(mp);
189 	return (TRUE);
190 }
191 
192 bool_t
193 fmd_adm_modgstat_1_svc(struct fmd_rpc_modstat *rms, struct svc_req *req)
194 {
195 	const size_t size = sizeof (fmd_statistics_t);
196 
197 	if (fmd_rpc_deny(req)) {
198 		rms->rms_buf.rms_buf_val = NULL;
199 		rms->rms_buf.rms_buf_len = 0;
200 		rms->rms_err = FMD_ADM_ERR_PERM;
201 	} else if ((rms->rms_buf.rms_buf_val = malloc(size)) != NULL) {
202 		/*
203 		 * Note: the bcopy() here is valid only if no FMD_TYPE_STRING
204 		 * stats are present in fmd.d_stats (see definition in fmd.c).
205 		 */
206 		(void) pthread_mutex_lock(&fmd.d_stats_lock);
207 		bcopy(fmd.d_stats, rms->rms_buf.rms_buf_val, size);
208 		(void) pthread_mutex_unlock(&fmd.d_stats_lock);
209 		rms->rms_buf.rms_buf_len = size / sizeof (fmd_stat_t);
210 		rms->rms_err = 0;
211 	} else {
212 		rms->rms_buf.rms_buf_len = 0;
213 		rms->rms_err = FMD_ADM_ERR_NOMEM;
214 	}
215 
216 	return (TRUE);
217 }
218 
219 bool_t
220 fmd_adm_modload_1_svc(char *path, int *rvp, struct svc_req *req)
221 {
222 	fmd_module_t *mp;
223 	const char *p;
224 	int err = 0;
225 
226 	if (fmd_rpc_deny(req)) {
227 		*rvp = FMD_ADM_ERR_PERM;
228 		return (TRUE);
229 	}
230 
231 	/*
232 	 * Before we endure the expense of constructing a module and attempting
233 	 * to load it, do a quick check to see if the pathname is valid.
234 	 */
235 	if (access(path, F_OK) != 0) {
236 		*rvp = FMD_ADM_ERR_MODNOENT;
237 		return (TRUE);
238 	}
239 
240 	if ((p = strrchr(path, '.')) != NULL && strcmp(p, ".so") == 0)
241 		mp = fmd_modhash_load(fmd.d_mod_hash, path, &fmd_rtld_ops);
242 	else
243 		mp = fmd_modhash_load(fmd.d_mod_hash, path, &fmd_proc_ops);
244 
245 	if (mp == NULL) {
246 		switch (errno) {
247 		case EFMD_MOD_LOADED:
248 			err = FMD_ADM_ERR_MODEXIST;
249 			break;
250 		case EFMD_MOD_INIT:
251 			err = FMD_ADM_ERR_MODINIT;
252 			break;
253 		default:
254 			err = FMD_ADM_ERR_MODLOAD;
255 			break;
256 		}
257 	}
258 
259 	*rvp = err;
260 	return (TRUE);
261 }
262 
263 bool_t
264 fmd_adm_modunload_1_svc(char *name, int *rvp, struct svc_req *req)
265 {
266 	fmd_module_t *mp = NULL;
267 	int err = 0;
268 
269 	if (fmd_rpc_deny(req))
270 		err = FMD_ADM_ERR_PERM;
271 	else if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) == NULL)
272 		err = FMD_ADM_ERR_MODSRCH;
273 	else if (mp == fmd.d_self)
274 		err = FMD_ADM_ERR_MODBUSY;
275 	else if (fmd_modhash_unload(fmd.d_mod_hash, name) != 0)
276 		err = FMD_ADM_ERR_MODSRCH;
277 
278 	if (mp != NULL)
279 		fmd_module_rele(mp);
280 
281 	*rvp = err;
282 	return (TRUE);
283 }
284 
285 bool_t
286 fmd_adm_modreset_1_svc(char *name, int *rvp, struct svc_req *req)
287 {
288 	fmd_module_t *mp = NULL;
289 	int err = 0;
290 
291 	if (fmd_rpc_deny(req))
292 		err = FMD_ADM_ERR_PERM;
293 	else if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) == NULL)
294 		err = FMD_ADM_ERR_MODSRCH;
295 	else if (mp == fmd.d_self)
296 		err = FMD_ADM_ERR_MODBUSY;
297 	else if (fmd_modhash_unload(fmd.d_mod_hash, name) != 0)
298 		err = FMD_ADM_ERR_MODSRCH;
299 
300 	if (err == 0)
301 		fmd_ckpt_delete(mp); /* erase any saved checkpoints */
302 
303 	if (err == 0 && fmd_modhash_load(fmd.d_mod_hash,
304 	    mp->mod_path, mp->mod_ops) == NULL) {
305 		if (errno == EFMD_MOD_INIT)
306 			err = FMD_ADM_ERR_MODINIT;
307 		else
308 			err = FMD_ADM_ERR_MODLOAD;
309 	}
310 
311 	if (mp != NULL)
312 		fmd_module_rele(mp);
313 
314 	*rvp = err;
315 	return (TRUE);
316 }
317 
318 bool_t
319 fmd_adm_modgc_1_svc(char *name, int *rvp, struct svc_req *req)
320 {
321 	fmd_module_t *mp;
322 	int err = 0;
323 
324 	if (fmd_rpc_deny(req))
325 		err = FMD_ADM_ERR_PERM;
326 	else if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) == NULL)
327 		err = FMD_ADM_ERR_MODSRCH;
328 	else {
329 		fmd_module_gc(mp);
330 		fmd_module_rele(mp);
331 	}
332 
333 	*rvp = err;
334 	return (TRUE);
335 }
336 
337 /*
338  * Unlike our other RPC callbacks, fmd_adm_rsrclist_1 can return large amounts
339  * of data that may exceed the underlying RPC transport buffer size if the
340  * resource cache is heavily populated and/or all resources are requested.
341  * To minimize the likelihood of running out of RPC buffer space and having to
342  * fail the client request, fmd_adm_rsrclist_1 returns a snapshot of the
343  * relevant FMRI strings only: the client can use fmd_adm_rsrcinfo_1 on an
344  * individual FMRI if more information is needed.  To further reduce the XDR
345  * overhead, the string list is represented as XDR-opaque data where the
346  * entire list is returned as a string table (e.g. "fmriA\0fmriB\0...").
347  */
348 static void
349 fmd_adm_rsrclist_asru(fmd_asru_t *ap, void *arg)
350 {
351 	struct fmd_rpc_rsrclist *rrl = arg;
352 	size_t name_len, buf_len;
353 	void *p;
354 
355 	/*
356 	 * Skip the ASRU if this fault is marked as invisible.
357 	 * If rrl_all is false, we take a quick look at asru_flags with no lock
358 	 * held to see if the ASRU is not faulty.  If so,
359 	 * we don't want to report it by default and can just skip this ASRU.
360 	 * This helps keep overhead low in the common case, as the call to
361 	 * fmd_asru_getstate() can be expensive depending on the scheme.
362 	 */
363 
364 	if (ap->asru_flags & FMD_ASRU_INVISIBLE)
365 		return;
366 	if (rrl->rrl_all == B_FALSE && !(ap->asru_flags & FMD_ASRU_FAULTY))
367 		return;
368 
369 	if (rrl->rrl_err != 0 || fmd_asru_getstate(ap) == 0)
370 		return; /* error has occurred or resource is in 'ok' state */
371 
372 	/*
373 	 * Lock the ASRU and reallocate rrl_buf[] to be large enough to hold
374 	 * another string, doubling it as needed.  Then copy the new string
375 	 * on to the end, and increment rrl_len to indicate the used space.
376 	 */
377 	(void) pthread_mutex_lock(&ap->asru_lock);
378 	name_len = strlen(ap->asru_name) + 1;
379 
380 	while (rrl->rrl_len + name_len > rrl->rrl_buf.rrl_buf_len) {
381 		if (rrl->rrl_buf.rrl_buf_len != 0)
382 			buf_len = rrl->rrl_buf.rrl_buf_len * 2;
383 		else
384 			buf_len = 1024; /* default buffer size */
385 
386 		if ((p = realloc(rrl->rrl_buf.rrl_buf_val, buf_len)) != NULL) {
387 			bzero((char *)p + rrl->rrl_buf.rrl_buf_len,
388 			    buf_len - rrl->rrl_buf.rrl_buf_len);
389 			rrl->rrl_buf.rrl_buf_val = p;
390 			rrl->rrl_buf.rrl_buf_len = buf_len;
391 		} else {
392 			rrl->rrl_err = FMD_ADM_ERR_NOMEM;
393 			break;
394 		}
395 	}
396 
397 	if (rrl->rrl_err == 0) {
398 		bcopy(ap->asru_name, (char *)rrl->rrl_buf.rrl_buf_val +
399 		    rrl->rrl_len, name_len);
400 		rrl->rrl_len += name_len;
401 		rrl->rrl_cnt++;
402 	}
403 
404 	(void) pthread_mutex_unlock(&ap->asru_lock);
405 }
406 
407 bool_t
408 fmd_adm_rsrclist_1_svc(bool_t all,
409     struct fmd_rpc_rsrclist *rvp, struct svc_req *req)
410 {
411 	rvp->rrl_buf.rrl_buf_len = 0;
412 	rvp->rrl_buf.rrl_buf_val = NULL;
413 	rvp->rrl_len = 0;
414 	rvp->rrl_cnt = 0;
415 	rvp->rrl_err = 0;
416 	rvp->rrl_all = all;
417 
418 	if (fmd_rpc_deny(req))
419 		rvp->rrl_err = FMD_ADM_ERR_PERM;
420 	else
421 		fmd_asru_hash_apply(fmd.d_asrus, fmd_adm_rsrclist_asru, rvp);
422 
423 	return (TRUE);
424 }
425 
426 bool_t
427 fmd_adm_rsrcinfo_1_svc(char *fmri,
428     struct fmd_rpc_rsrcinfo *rvp, struct svc_req *req)
429 {
430 	fmd_asru_t *ap;
431 	fmd_case_impl_t *cip;
432 	int state;
433 
434 	bzero(rvp, sizeof (struct fmd_rpc_rsrcinfo));
435 
436 	if (fmd_rpc_deny(req)) {
437 		rvp->rri_err = FMD_ADM_ERR_PERM;
438 		return (TRUE);
439 	}
440 
441 	if ((ap = fmd_asru_hash_lookup_name(fmd.d_asrus, fmri)) == NULL) {
442 		rvp->rri_err = FMD_ADM_ERR_RSRCSRCH;
443 		return (TRUE);
444 	}
445 
446 	state = fmd_asru_getstate(ap);
447 	(void) pthread_mutex_lock(&ap->asru_lock);
448 	cip = (fmd_case_impl_t *)ap->asru_case;
449 
450 	rvp->rri_fmri = strdup(ap->asru_name);
451 	rvp->rri_uuid = strdup(ap->asru_uuid);
452 	rvp->rri_case = cip ? strdup(cip->ci_uuid) : NULL;
453 	rvp->rri_faulty = (state & FMD_ASRU_FAULTY) != 0;
454 	rvp->rri_unusable = (state & FMD_ASRU_UNUSABLE) != 0;
455 	rvp->rri_invisible = (ap->asru_flags & FMD_ASRU_INVISIBLE) != 0;
456 
457 	(void) pthread_mutex_unlock(&ap->asru_lock);
458 	fmd_asru_hash_release(fmd.d_asrus, ap);
459 
460 	if (rvp->rri_fmri == NULL || rvp->rri_uuid == NULL)
461 		rvp->rri_err = FMD_ADM_ERR_NOMEM;
462 
463 	return (TRUE);
464 }
465 
466 static void
467 fmd_adm_do_repair(char *name, struct svc_req *req, int *errp, uint8_t reason,
468     char *uuid)
469 {
470 	if (fmd_rpc_deny(req))
471 		*errp = FMD_ADM_ERR_PERM;
472 	else {
473 		fmd_asru_rep_arg_t fara;
474 		int err = FARA_ERR_RSRCNOTF;
475 
476 		fara.fara_reason = reason;
477 		fara.fara_rval = &err;
478 		fara.fara_uuid = uuid;
479 		fara.fara_bywhat = FARA_BY_ASRU;
480 		fmd_asru_hash_apply_by_asru(fmd.d_asrus, name,
481 		    fmd_asru_repaired, &fara);
482 		fara.fara_bywhat = FARA_BY_LABEL;
483 		fmd_asru_hash_apply_by_label(fmd.d_asrus, name,
484 		    fmd_asru_repaired, &fara);
485 		fara.fara_bywhat = FARA_BY_FRU;
486 		fmd_asru_hash_apply_by_fru(fmd.d_asrus, name,
487 		    fmd_asru_repaired, &fara);
488 		fara.fara_bywhat = FARA_BY_RSRC;
489 		fmd_asru_hash_apply_by_rsrc(fmd.d_asrus, name,
490 		    fmd_asru_repaired, &fara);
491 		if (err == FARA_ERR_RSRCNOTR)
492 			*errp = FMD_ADM_ERR_RSRCNOTR;
493 		else if (err == FARA_OK)
494 			*errp = 0;
495 	}
496 }
497 
498 bool_t
499 fmd_adm_rsrcflush_1_svc(char *name, int *rvp, struct svc_req *req)
500 {
501 	int err = FMD_ADM_ERR_RSRCNOTF;
502 
503 	/*
504 	 * If anyone does an fmadm flush command, discard any resolved
505 	 * cases that were being retained for historic diagnosis.
506 	 */
507 	if (fmd_rpc_deny(req))
508 		err = FMD_ADM_ERR_PERM;
509 	else {
510 		fmd_asru_hash_apply_by_asru(fmd.d_asrus, name,
511 		    fmd_asru_flush, &err);
512 		fmd_asru_hash_apply_by_label(fmd.d_asrus, name,
513 		    fmd_asru_flush, &err);
514 		fmd_asru_hash_apply_by_fru(fmd.d_asrus, name,
515 		    fmd_asru_flush, &err);
516 		fmd_asru_hash_apply_by_rsrc(fmd.d_asrus, name,
517 		    fmd_asru_flush, &err);
518 	}
519 	*rvp = err;
520 	return (TRUE);
521 }
522 
523 bool_t
524 fmd_adm_rsrcrepaired_1_svc(char *name, int *rvp, struct svc_req *req)
525 {
526 	int err = FMD_ADM_ERR_RSRCNOTF;
527 
528 	fmd_adm_do_repair(name, req, &err, FMD_ASRU_REPAIRED, NULL);
529 	*rvp = err;
530 	return (TRUE);
531 }
532 
533 bool_t
534 fmd_adm_rsrcreplaced_1_svc(char *name, int *rvp, struct svc_req *req)
535 {
536 	int err = FMD_ADM_ERR_RSRCNOTF;
537 
538 	fmd_adm_do_repair(name, req, &err, FMD_ASRU_REPLACED, NULL);
539 	*rvp = err;
540 	return (TRUE);
541 }
542 
543 bool_t
544 fmd_adm_rsrcacquit_1_svc(char *name, char *uuid, int *rvp, struct svc_req *req)
545 {
546 	int err = FMD_ADM_ERR_RSRCNOTF;
547 
548 	fmd_adm_do_repair(name, req, &err, FMD_ASRU_ACQUITTED, uuid);
549 	*rvp = err;
550 	return (TRUE);
551 }
552 
553 static void
554 fmd_adm_serdlist_measure(fmd_serd_eng_t *sgp, void *arg)
555 {
556 	struct fmd_rpc_serdlist *rsl = arg;
557 
558 	rsl->rsl_len += strlen(sgp->sg_name) + 1;
559 	rsl->rsl_cnt++;
560 }
561 
562 static void
563 fmd_adm_serdlist_record(fmd_serd_eng_t *sgp, void *arg)
564 {
565 	struct fmd_rpc_serdlist *rsl = arg;
566 
567 	bcopy(sgp->sg_name, rsl->rsl_buf.rsl_buf_val + rsl->rsl_len,
568 	    strlen(sgp->sg_name));
569 	rsl->rsl_len += strlen(sgp->sg_name) + 1;
570 }
571 
572 bool_t
573 fmd_adm_serdlist_1_svc(char *name, struct fmd_rpc_serdlist *rvp,
574     struct svc_req *req)
575 {
576 	fmd_module_t *mp;
577 	void *p;
578 
579 	rvp->rsl_buf.rsl_buf_len = 0;
580 	rvp->rsl_buf.rsl_buf_val = NULL;
581 	rvp->rsl_len = 0;
582 	rvp->rsl_cnt = 0;
583 	rvp->rsl_err = 0;
584 
585 	if (fmd_rpc_deny(req)) {
586 		rvp->rsl_err = FMD_ADM_ERR_PERM;
587 		return (TRUE);
588 	}
589 
590 	if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) == NULL) {
591 		rvp->rsl_err = FMD_ADM_ERR_MODSRCH;
592 		return (TRUE);
593 	}
594 
595 	fmd_module_lock(mp);
596 	/* In the first pass, collect the overall length of the buffer. */
597 	fmd_serd_hash_apply(&mp->mod_serds, fmd_adm_serdlist_measure, rvp);
598 	if (rvp->rsl_len == 0) {
599 		fmd_module_unlock(mp);
600 		fmd_module_rele(mp);
601 		return (TRUE);
602 	}
603 	p = malloc(rvp->rsl_len);
604 	if (p) {
605 		rvp->rsl_buf.rsl_buf_val = p;
606 		rvp->rsl_buf.rsl_buf_len = rvp->rsl_len;
607 		bzero(rvp->rsl_buf.rsl_buf_val, rvp->rsl_buf.rsl_buf_len);
608 		rvp->rsl_len = 0;
609 		/* In the second pass, populate the buffer with data. */
610 		fmd_serd_hash_apply(&mp->mod_serds, fmd_adm_serdlist_record,
611 		    rvp);
612 	} else {
613 		rvp->rsl_err = FMD_ADM_ERR_NOMEM;
614 	}
615 	fmd_module_unlock(mp);
616 
617 	fmd_module_rele(mp);
618 	return (TRUE);
619 }
620 
621 static void
622 fmd_adm_serdinfo_record(fmd_serd_eng_t *sgp, struct fmd_rpc_serdinfo *rsi)
623 {
624 	uint64_t old, now = fmd_time_gethrtime();
625 	const fmd_serd_elem_t *oep;
626 
627 	if ((rsi->rsi_name = strdup(sgp->sg_name)) == NULL) {
628 		rsi->rsi_err = FMD_ADM_ERR_NOMEM;
629 		return;
630 	}
631 
632 	if ((oep = fmd_list_next(&sgp->sg_list)) != NULL)
633 		old = fmd_event_hrtime(oep->se_event);
634 	else
635 		old = now;
636 
637 	rsi->rsi_delta = now >= old ? now - old : (UINT64_MAX - old) + now + 1;
638 	rsi->rsi_count = sgp->sg_count;
639 	rsi->rsi_fired = fmd_serd_eng_fired(sgp) != 0;
640 	rsi->rsi_n = sgp->sg_n;
641 	rsi->rsi_t = sgp->sg_t;
642 }
643 
644 bool_t
645 fmd_adm_serdinfo_1_svc(char *mname, char *sname, struct fmd_rpc_serdinfo *rvp,
646     struct svc_req *req)
647 {
648 	fmd_module_t *mp;
649 	fmd_serd_eng_t *sgp;
650 
651 	bzero(rvp, sizeof (struct fmd_rpc_serdinfo));
652 
653 	if (fmd_rpc_deny(req)) {
654 		rvp->rsi_err = FMD_ADM_ERR_PERM;
655 		return (TRUE);
656 	}
657 
658 	if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, mname)) == NULL) {
659 		rvp->rsi_err = FMD_ADM_ERR_MODSRCH;
660 		return (TRUE);
661 	}
662 
663 	fmd_module_lock(mp);
664 
665 	if ((sgp = fmd_serd_eng_lookup(&mp->mod_serds, sname)) != NULL) {
666 		fmd_adm_serdinfo_record(sgp, rvp);
667 	} else
668 		rvp->rsi_err = FMD_ADM_ERR_SERDSRCH;
669 
670 	fmd_module_unlock(mp);
671 	fmd_module_rele(mp);
672 
673 	return (TRUE);
674 }
675 
676 /*ARGSUSED*/
677 bool_t
678 fmd_adm_serdinfo_old_1_svc(char *name, struct fmd_rpc_serdlist *rvp,
679     struct svc_req *req)
680 {
681 	return (FALSE);
682 }
683 
684 bool_t
685 fmd_adm_serdreset_1_svc(char *mname, char *sname, int *rvp, struct svc_req *req)
686 {
687 	fmd_module_t *mp;
688 	fmd_serd_eng_t *sgp;
689 	int err = 0;
690 
691 	if (fmd_rpc_deny(req)) {
692 		*rvp = FMD_ADM_ERR_PERM;
693 		return (TRUE);
694 	}
695 
696 	if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, mname)) == NULL) {
697 		*rvp = FMD_ADM_ERR_MODSRCH;
698 		return (TRUE);
699 	}
700 
701 	fmd_module_lock(mp);
702 
703 	if ((sgp = fmd_serd_eng_lookup(&mp->mod_serds, sname)) != NULL) {
704 		if (fmd_serd_eng_fired(sgp)) {
705 			err = FMD_ADM_ERR_SERDFIRED;
706 		} else {
707 			fmd_serd_eng_reset(sgp);
708 			fmd_module_setdirty(mp);
709 		}
710 	} else
711 		err = FMD_ADM_ERR_SERDSRCH;
712 
713 	fmd_module_unlock(mp);
714 	fmd_module_rele(mp);
715 
716 	*rvp = err;
717 	return (TRUE);
718 }
719 
720 bool_t
721 fmd_adm_logrotate_1_svc(char *name, int *rvp, struct svc_req *req)
722 {
723 	fmd_log_t **lpp, *old, *new;
724 	int try = 1, trylimit = 1;
725 
726 	hrtime_t nsec = 0;
727 	timespec_t tv;
728 
729 	if (fmd_rpc_deny(req)) {
730 		*rvp = FMD_ADM_ERR_PERM;
731 		return (TRUE);
732 	}
733 
734 	if (strcmp(name, "errlog") == 0)
735 		lpp = &fmd.d_errlog;
736 	else if (strcmp(name, "fltlog") == 0)
737 		lpp = &fmd.d_fltlog;
738 	else {
739 		*rvp = FMD_ADM_ERR_ROTSRCH;
740 		return (TRUE);
741 	}
742 
743 	(void) fmd_conf_getprop(fmd.d_conf, "log.tryrotate", &trylimit);
744 	(void) fmd_conf_getprop(fmd.d_conf, "log.waitrotate", &nsec);
745 
746 	tv.tv_sec = nsec / NANOSEC;
747 	tv.tv_nsec = nsec % NANOSEC;
748 
749 	/*
750 	 * To rotate a log file, grab d_log_lock as writer to make sure no
751 	 * one else can discover the current log pointer.  Then try to rotate
752 	 * the log.  If we're successful, release the old log pointer.
753 	 */
754 	do {
755 		if (try > 1)
756 			(void) nanosleep(&tv, NULL); /* wait for checkpoints */
757 
758 		(void) pthread_rwlock_wrlock(&fmd.d_log_lock);
759 		old = *lpp;
760 
761 		if ((new = fmd_log_rotate(old)) != NULL) {
762 			fmd_log_rele(old);
763 			*lpp = new;
764 		}
765 
766 		(void) pthread_rwlock_unlock(&fmd.d_log_lock);
767 
768 	} while (new == NULL && errno == EFMD_LOG_ROTBUSY && try++ < trylimit);
769 
770 	if (new != NULL)
771 		*rvp = 0;
772 	else if (errno == EFMD_LOG_ROTBUSY)
773 		*rvp = FMD_ADM_ERR_ROTBUSY;
774 	else
775 		*rvp = FMD_ADM_ERR_ROTFAIL;
776 
777 	return (TRUE);
778 }
779 
780 bool_t
781 fmd_adm_caserepair_1_svc(char *uuid, int *rvp, struct svc_req *req)
782 {
783 	fmd_case_t *cp = NULL;
784 	int err = 0;
785 
786 	if (fmd_rpc_deny(req))
787 		err = FMD_ADM_ERR_PERM;
788 	else if ((cp = fmd_case_hash_lookup(fmd.d_cases, uuid)) == NULL)
789 		err = FMD_ADM_ERR_CASESRCH;
790 	else if (fmd_case_repair(cp) != 0) {
791 		err = errno == EFMD_CASE_OWNER ?
792 		    FMD_ADM_ERR_CASEXPRT : FMD_ADM_ERR_CASEOPEN;
793 	}
794 
795 	if (cp != NULL)
796 		fmd_case_rele(cp);
797 
798 	*rvp = err;
799 	return (TRUE);
800 }
801 
802 bool_t
803 fmd_adm_caseacquit_1_svc(char *uuid, int *rvp, struct svc_req *req)
804 {
805 	fmd_case_t *cp = NULL;
806 	int err = 0;
807 
808 	if (fmd_rpc_deny(req))
809 		err = FMD_ADM_ERR_PERM;
810 	else if ((cp = fmd_case_hash_lookup(fmd.d_cases, uuid)) == NULL)
811 		err = FMD_ADM_ERR_CASESRCH;
812 	else if (fmd_case_acquit(cp) != 0) {
813 		err = errno == EFMD_CASE_OWNER ?
814 		    FMD_ADM_ERR_CASEXPRT : FMD_ADM_ERR_CASEOPEN;
815 	}
816 
817 	if (cp != NULL)
818 		fmd_case_rele(cp);
819 
820 	*rvp = err;
821 	return (TRUE);
822 }
823 
824 void
825 fmd_adm_caselist_case(fmd_case_t *cp, void *arg)
826 {
827 	fmd_case_impl_t *cip = (fmd_case_impl_t *)cp;
828 	struct fmd_rpc_caselist *rcl = arg;
829 	size_t uuid_len, buf_len;
830 	void *p;
831 
832 	if (rcl->rcl_err != 0)
833 		return;
834 
835 	/*
836 	 * skip invisible cases
837 	 */
838 	if (cip->ci_flags & FMD_CF_INVISIBLE)
839 		return;
840 
841 	/*
842 	 * Lock the case and reallocate rcl_buf[] to be large enough to hold
843 	 * another string, doubling it as needed.  Then copy the new string
844 	 * on to the end, and increment rcl_len to indicate the used space.
845 	 */
846 	if (!(cip->ci_flags & FMD_CF_SOLVED))
847 		return;
848 
849 	(void) pthread_mutex_lock(&cip->ci_lock);
850 
851 	uuid_len = cip->ci_uuidlen + 1;
852 
853 	while (rcl->rcl_len + uuid_len > rcl->rcl_buf.rcl_buf_len) {
854 		if (rcl->rcl_buf.rcl_buf_len != 0)
855 			buf_len = rcl->rcl_buf.rcl_buf_len * 2;
856 		else
857 			buf_len = 1024; /* default buffer size */
858 
859 		if ((p = realloc(rcl->rcl_buf.rcl_buf_val, buf_len)) != NULL) {
860 			bzero((char *)p + rcl->rcl_buf.rcl_buf_len,
861 			    buf_len - rcl->rcl_buf.rcl_buf_len);
862 			rcl->rcl_buf.rcl_buf_val = p;
863 			rcl->rcl_buf.rcl_buf_len = buf_len;
864 		} else {
865 			rcl->rcl_err = FMD_ADM_ERR_NOMEM;
866 			break;
867 		}
868 	}
869 
870 	if (rcl->rcl_err == 0) {
871 		bcopy(cip->ci_uuid, (char *)rcl->rcl_buf.rcl_buf_val +
872 		    rcl->rcl_len, uuid_len);
873 		rcl->rcl_len += uuid_len;
874 		rcl->rcl_cnt++;
875 	}
876 
877 	(void) pthread_mutex_unlock(&cip->ci_lock);
878 }
879 
880 bool_t
881 fmd_adm_caselist_1_svc(struct fmd_rpc_caselist *rvp, struct svc_req *req)
882 {
883 	rvp->rcl_buf.rcl_buf_len = 0;
884 	rvp->rcl_buf.rcl_buf_val = NULL;
885 	rvp->rcl_len = 0;
886 	rvp->rcl_cnt = 0;
887 	rvp->rcl_err = 0;
888 
889 	if (fmd_rpc_deny(req))
890 		rvp->rcl_err = FMD_ADM_ERR_PERM;
891 	else
892 		fmd_case_hash_apply(fmd.d_cases, fmd_adm_caselist_case, rvp);
893 
894 	return (TRUE);
895 }
896 
897 bool_t
898 fmd_adm_caseinfo_1_svc(char *uuid, struct fmd_rpc_caseinfo *rvp,
899     struct svc_req *req)
900 {
901 	fmd_case_t *cp;
902 	nvlist_t *nvl;
903 	int err = 0;
904 
905 	bzero(rvp, sizeof (struct fmd_rpc_caseinfo));
906 
907 	if (fmd_rpc_deny(req)) {
908 		rvp->rci_err = FMD_ADM_ERR_PERM;
909 		return (TRUE);
910 	}
911 
912 	if ((cp = fmd_case_hash_lookup(fmd.d_cases, uuid)) == NULL) {
913 		rvp->rci_err = FMD_ADM_ERR_CASESRCH;
914 		return (TRUE);
915 	}
916 
917 	if (!(((fmd_case_impl_t *)cp)->ci_flags & FMD_CF_SOLVED)) {
918 		fmd_case_rele(cp);
919 		rvp->rci_err = FMD_ADM_ERR_CASESRCH;
920 		return (TRUE);
921 	}
922 
923 	nvl = fmd_case_mkevent(cp, FM_LIST_SUSPECT_CLASS);
924 
925 	err = nvlist_pack(nvl, &rvp->rci_evbuf.rci_evbuf_val,
926 	    &rvp->rci_evbuf.rci_evbuf_len, NV_ENCODE_XDR, 0);
927 
928 	nvlist_free(nvl);
929 
930 	if (err != 0)
931 		rvp->rci_err = FMD_ADM_ERR_NOMEM;
932 
933 	fmd_case_rele(cp);
934 
935 	return (TRUE);
936 }
937 
938 /*ARGSUSED*/
939 static void
940 fmd_adm_xprtlist_one(fmd_idspace_t *ids, id_t id, void *arg)
941 {
942 	struct fmd_rpc_xprtlist *rvp = arg;
943 
944 	if (rvp->rxl_len < rvp->rxl_buf.rxl_buf_len)
945 		rvp->rxl_buf.rxl_buf_val[rvp->rxl_len++] = id;
946 }
947 
948 bool_t
949 fmd_adm_xprtlist_1_svc(struct fmd_rpc_xprtlist *rvp, struct svc_req *req)
950 {
951 	if (fmd_rpc_deny(req)) {
952 		rvp->rxl_buf.rxl_buf_len = 0;
953 		rvp->rxl_buf.rxl_buf_val = NULL;
954 		rvp->rxl_len = 0;
955 		rvp->rxl_err = FMD_ADM_ERR_PERM;
956 		return (TRUE);
957 	}
958 
959 	/*
960 	 * Since we're taking a snapshot of the transports, and these could
961 	 * change after we return our result, there's no need to hold any kind
962 	 * of lock between retrieving ids_count and taking the snapshot.  We'll
963 	 * just capture up to a maximum of whatever ids_count value we sampled.
964 	 */
965 	rvp->rxl_buf.rxl_buf_len = fmd.d_xprt_ids->ids_count;
966 	rvp->rxl_buf.rxl_buf_val = malloc(sizeof (int32_t) *
967 	    rvp->rxl_buf.rxl_buf_len);
968 	rvp->rxl_len = 0;
969 	rvp->rxl_err = 0;
970 
971 	if (rvp->rxl_buf.rxl_buf_val == NULL) {
972 		rvp->rxl_err = FMD_ADM_ERR_NOMEM;
973 		return (TRUE);
974 	}
975 
976 	fmd_idspace_apply(fmd.d_xprt_ids, fmd_adm_xprtlist_one, rvp);
977 	return (TRUE);
978 }
979 
980 bool_t
981 fmd_adm_xprtstat_1_svc(int32_t id,
982     struct fmd_rpc_modstat *rms, struct svc_req *req)
983 {
984 	fmd_xprt_impl_t *xip;
985 	fmd_stat_t *sp, *ep, *cp;
986 
987 	if (fmd_rpc_deny(req)) {
988 		rms->rms_buf.rms_buf_val = NULL;
989 		rms->rms_buf.rms_buf_len = 0;
990 		rms->rms_err = FMD_ADM_ERR_PERM;
991 		return (TRUE);
992 	}
993 
994 	rms->rms_buf.rms_buf_val = malloc(sizeof (fmd_xprt_stat_t));
995 	rms->rms_buf.rms_buf_len = sizeof (fmd_xprt_stat_t) /
996 	    sizeof (fmd_stat_t);
997 	rms->rms_err = 0;
998 
999 	if (rms->rms_buf.rms_buf_val == NULL) {
1000 		rms->rms_err = FMD_ADM_ERR_NOMEM;
1001 		rms->rms_buf.rms_buf_len = 0;
1002 		return (TRUE);
1003 	}
1004 
1005 	if ((xip = fmd_idspace_hold(fmd.d_xprt_ids, id)) == NULL) {
1006 		rms->rms_err = FMD_ADM_ERR_XPRTSRCH;
1007 		return (TRUE);
1008 	}
1009 
1010 	/*
1011 	 * Grab the stats lock and bcopy the entire transport stats array in
1012 	 * one shot. Then go back through and duplicate any string values.
1013 	 */
1014 	(void) pthread_mutex_lock(&xip->xi_stats_lock);
1015 
1016 	sp = (fmd_stat_t *)xip->xi_stats;
1017 	ep = sp + rms->rms_buf.rms_buf_len;
1018 	cp = rms->rms_buf.rms_buf_val;
1019 
1020 	bcopy(sp, cp, sizeof (fmd_xprt_stat_t));
1021 
1022 	for (; sp < ep; sp++, cp++) {
1023 		if (sp->fmds_type == FMD_TYPE_STRING &&
1024 		    sp->fmds_value.str != NULL)
1025 			cp->fmds_value.str = strdup(sp->fmds_value.str);
1026 	}
1027 
1028 	(void) pthread_mutex_unlock(&xip->xi_stats_lock);
1029 	fmd_idspace_rele(fmd.d_xprt_ids, id);
1030 
1031 	return (TRUE);
1032 }
1033 
1034 int
1035 fmd_adm_1_freeresult(SVCXPRT *xprt, xdrproc_t proc, caddr_t data)
1036 {
1037 	xdr_free(proc, data);
1038 	svc_done(xprt);
1039 	return (TRUE);
1040 }
1041 
1042 /*
1043  * Custom XDR routine for our API structure fmd_stat_t.  This function must
1044  * match the definition of fmd_stat_t in <fmd_api.h> and must also match
1045  * the corresponding routine in usr/src/lib/fm/libfmd_adm/common/fmd_adm.c.
1046  */
1047 bool_t
1048 xdr_fmd_stat(XDR *xp, fmd_stat_t *sp)
1049 {
1050 	bool_t rv = TRUE;
1051 
1052 	rv &= xdr_opaque(xp, sp->fmds_name, sizeof (sp->fmds_name));
1053 	rv &= xdr_u_int(xp, &sp->fmds_type);
1054 	rv &= xdr_opaque(xp, sp->fmds_desc, sizeof (sp->fmds_desc));
1055 
1056 	switch (sp->fmds_type) {
1057 	case FMD_TYPE_BOOL:
1058 		rv &= xdr_int(xp, &sp->fmds_value.bool);
1059 		break;
1060 	case FMD_TYPE_INT32:
1061 		rv &= xdr_int32_t(xp, &sp->fmds_value.i32);
1062 		break;
1063 	case FMD_TYPE_UINT32:
1064 		rv &= xdr_uint32_t(xp, &sp->fmds_value.ui32);
1065 		break;
1066 	case FMD_TYPE_INT64:
1067 		rv &= xdr_int64_t(xp, &sp->fmds_value.i64);
1068 		break;
1069 	case FMD_TYPE_UINT64:
1070 	case FMD_TYPE_TIME:
1071 	case FMD_TYPE_SIZE:
1072 		rv &= xdr_uint64_t(xp, &sp->fmds_value.ui64);
1073 		break;
1074 	case FMD_TYPE_STRING:
1075 		rv &= xdr_string(xp, &sp->fmds_value.str, ~0);
1076 		break;
1077 	}
1078 
1079 	return (rv);
1080 }
1081