xref: /illumos-gate/usr/src/cmd/fm/fmd/common/fmd_rpc_adm.c (revision 4de2612967d06c4fdbf524a62556a1e8118a006f)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 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 <limits.h>
31 #include <unistd.h>
32 #include <stdlib.h>
33 #include <alloca.h>
34 
35 #include <fmd_rpc_adm.h>
36 #include <fmd_rpc.h>
37 #include <fmd_module.h>
38 #include <fmd_ustat.h>
39 #include <fmd_error.h>
40 #include <fmd_asru.h>
41 #include <fmd_ckpt.h>
42 #include <fmd_case.h>
43 #include <fmd_fmri.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 	 * If rrl_all is false, we take a quick look at asru_flags with no lock
357 	 * held to see if the ASRU is either not faulty or invisible.  If so,
358 	 * we don't want to report it by default and can just skip this ASRU.
359 	 * This helps keep overhead low in the common case, as the call to
360 	 * fmd_asru_getstate() can be expensive depending on the scheme.
361 	 */
362 	if (rrl->rrl_all == B_FALSE && (ap->asru_flags & (FMD_ASRU_FAULTY |
363 	    FMD_ASRU_INVISIBLE)) != FMD_ASRU_FAULTY)
364 		return;
365 
366 	if (rrl->rrl_err != 0 || fmd_asru_getstate(ap) == 0)
367 		return; /* error has occurred or resource is in 'ok' state */
368 
369 	/*
370 	 * Lock the ASRU and reallocate rrl_buf[] to be large enough to hold
371 	 * another string, doubling it as needed.  Then copy the new string
372 	 * on to the end, and increment rrl_len to indicate the used space.
373 	 */
374 	(void) pthread_mutex_lock(&ap->asru_lock);
375 	name_len = strlen(ap->asru_name) + 1;
376 
377 	while (rrl->rrl_len + name_len > rrl->rrl_buf.rrl_buf_len) {
378 		if (rrl->rrl_buf.rrl_buf_len != 0)
379 			buf_len = rrl->rrl_buf.rrl_buf_len * 2;
380 		else
381 			buf_len = 1024; /* default buffer size */
382 
383 		if ((p = realloc(rrl->rrl_buf.rrl_buf_val, buf_len)) != NULL) {
384 			bzero((char *)p + rrl->rrl_buf.rrl_buf_len,
385 			    buf_len - rrl->rrl_buf.rrl_buf_len);
386 			rrl->rrl_buf.rrl_buf_val = p;
387 			rrl->rrl_buf.rrl_buf_len = buf_len;
388 		} else {
389 			rrl->rrl_err = FMD_ADM_ERR_NOMEM;
390 			break;
391 		}
392 	}
393 
394 	if (rrl->rrl_err == 0) {
395 		bcopy(ap->asru_name, (char *)rrl->rrl_buf.rrl_buf_val +
396 		    rrl->rrl_len, name_len);
397 		rrl->rrl_len += name_len;
398 		rrl->rrl_cnt++;
399 	}
400 
401 	(void) pthread_mutex_unlock(&ap->asru_lock);
402 }
403 
404 bool_t
405 fmd_adm_rsrclist_1_svc(bool_t all,
406     struct fmd_rpc_rsrclist *rvp, struct svc_req *req)
407 {
408 	rvp->rrl_buf.rrl_buf_len = 0;
409 	rvp->rrl_buf.rrl_buf_val = NULL;
410 	rvp->rrl_len = 0;
411 	rvp->rrl_cnt = 0;
412 	rvp->rrl_err = 0;
413 	rvp->rrl_all = all;
414 
415 	if (fmd_rpc_deny(req))
416 		rvp->rrl_err = FMD_ADM_ERR_PERM;
417 	else
418 		fmd_asru_hash_apply(fmd.d_asrus, fmd_adm_rsrclist_asru, rvp);
419 
420 	return (TRUE);
421 }
422 
423 bool_t
424 fmd_adm_rsrcinfo_1_svc(char *fmri,
425     struct fmd_rpc_rsrcinfo *rvp, struct svc_req *req)
426 {
427 	fmd_asru_t *ap;
428 	int state;
429 
430 	bzero(rvp, sizeof (struct fmd_rpc_rsrcinfo));
431 
432 	if (fmd_rpc_deny(req)) {
433 		rvp->rri_err = FMD_ADM_ERR_PERM;
434 		return (TRUE);
435 	}
436 
437 	if ((ap = fmd_asru_hash_lookup_name(fmd.d_asrus, fmri)) == NULL) {
438 		rvp->rri_err = FMD_ADM_ERR_RSRCSRCH;
439 		return (TRUE);
440 	}
441 
442 	state = fmd_asru_getstate(ap);
443 	(void) pthread_mutex_lock(&ap->asru_lock);
444 
445 	rvp->rri_fmri = strdup(ap->asru_name);
446 	rvp->rri_uuid = strdup(ap->asru_uuid);
447 	rvp->rri_case = ap->asru_case ? strdup(ap->asru_case) : NULL;
448 	rvp->rri_faulty = (state & FMD_ASRU_FAULTY) != 0;
449 	rvp->rri_unusable = (state & FMD_ASRU_UNUSABLE) != 0;
450 	rvp->rri_invisible = (ap->asru_flags & FMD_ASRU_INVISIBLE) != 0;
451 
452 	(void) pthread_mutex_unlock(&ap->asru_lock);
453 	fmd_asru_hash_release(fmd.d_asrus, ap);
454 
455 	if (rvp->rri_fmri == NULL || rvp->rri_uuid == NULL)
456 		rvp->rri_err = FMD_ADM_ERR_NOMEM;
457 
458 	return (TRUE);
459 }
460 
461 bool_t
462 fmd_adm_rsrcflush_1_svc(char *name, int *rvp, struct svc_req *req)
463 {
464 	if (fmd_rpc_deny(req))
465 		*rvp = FMD_ADM_ERR_PERM;
466 	else if (fmd_asru_hash_delete_name(fmd.d_asrus, name) != 0)
467 		*rvp = FMD_ADM_ERR_RSRCSRCH;
468 	else
469 		*rvp = 0;
470 
471 	return (TRUE);
472 }
473 
474 static int
475 fmd_adm_repair(fmd_asru_t *ap)
476 {
477 	fmd_case_t *cp;
478 	uint_t cidlen;
479 	char *cid;
480 
481 	if (!fmd_asru_clrflags(ap, FMD_ASRU_FAULTY, NULL, NULL))
482 		return (FMD_ADM_ERR_RSRCNOTF);
483 
484 	(void) fmd_conf_getprop(fmd.d_conf, "uuidlen", &cidlen);
485 	cid = alloca(cidlen + 1);
486 	(void) fmd_asru_getcase(ap, cid, cidlen + 1);
487 
488 	if ((cp = fmd_case_hash_lookup(fmd.d_cases, cid)) != NULL) {
489 		fmd_case_update(cp);
490 		fmd_case_rele(cp);
491 	}
492 
493 	return (0);
494 }
495 
496 static void
497 fmd_adm_repair_containee(fmd_asru_t *ee, void *er)
498 {
499 	if ((ee->asru_flags & FMD_ASRU_FAULTY) &&
500 	    fmd_fmri_contains(er, ee->asru_fmri) > 0)
501 		(void) fmd_adm_repair(ee);
502 }
503 
504 bool_t
505 fmd_adm_rsrcrepair_1_svc(char *name, int *rvp, struct svc_req *req)
506 {
507 	fmd_asru_t *ap = NULL;
508 	int err = 0;
509 
510 	if (fmd_rpc_deny(req))
511 		err = FMD_ADM_ERR_PERM;
512 	else if ((ap = fmd_asru_hash_lookup_name(fmd.d_asrus, name)) == NULL)
513 		err = FMD_ADM_ERR_RSRCSRCH;
514 	else if ((err = fmd_adm_repair(ap)) == 0) {
515 		/*
516 		 * We've located the requested ASRU, and have repaired it.  Now
517 		 * traverse the ASRU cache, looking for any faulty entries that
518 		 * are contained by this one.  If we find any, repair them too.
519 		 */
520 		fmd_asru_hash_apply(fmd.d_asrus,
521 		    fmd_adm_repair_containee, ap->asru_fmri);
522 	}
523 
524 	if (ap != NULL)
525 		fmd_asru_hash_release(fmd.d_asrus, ap);
526 
527 	*rvp = err;
528 	return (TRUE);
529 }
530 
531 static void
532 fmd_adm_serdinfo_eng(fmd_serd_eng_t *sgp, void *arg)
533 {
534 	struct fmd_rpc_serdlist *rsl = arg;
535 	struct fmd_rpc_serdinfo *rsi = malloc(sizeof (struct fmd_rpc_serdinfo));
536 
537 	uint64_t old, now = fmd_time_gethrtime();
538 	const fmd_serd_elem_t *oep;
539 
540 	if (rsi == NULL || (rsi->rsi_name = strdup(sgp->sg_name)) == NULL) {
541 		rsl->rsl_err = FMD_ADM_ERR_NOMEM;
542 		free(rsi);
543 		return;
544 	}
545 
546 	if ((oep = fmd_list_next(&sgp->sg_list)) != NULL)
547 		old = fmd_event_hrtime(oep->se_event);
548 	else
549 		old = now;
550 
551 	rsi->rsi_delta = now >= old ? now - old : (UINT64_MAX - old) + now + 1;
552 	rsi->rsi_count = sgp->sg_count;
553 	rsi->rsi_fired = fmd_serd_eng_fired(sgp) != 0;
554 	rsi->rsi_n = sgp->sg_n;
555 	rsi->rsi_t = sgp->sg_t;
556 	rsi->rsi_next = rsl->rsl_list;
557 
558 	rsl->rsl_list = rsi;
559 	rsl->rsl_len++;
560 }
561 
562 bool_t
563 fmd_adm_serdinfo_1_svc(char *name,
564     struct fmd_rpc_serdlist *rvp, struct svc_req *req)
565 {
566 	fmd_module_t *mp;
567 
568 	rvp->rsl_list = NULL;
569 	rvp->rsl_err = 0;
570 	rvp->rsl_len = 0;
571 
572 	if (fmd_rpc_deny(req)) {
573 		rvp->rsl_err = FMD_ADM_ERR_PERM;
574 		return (TRUE);
575 	}
576 
577 	if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) == NULL) {
578 		rvp->rsl_err = FMD_ADM_ERR_MODSRCH;
579 		return (TRUE);
580 	}
581 
582 	fmd_module_lock(mp);
583 	fmd_serd_hash_apply(&mp->mod_serds, fmd_adm_serdinfo_eng, rvp);
584 	fmd_module_unlock(mp);
585 
586 	fmd_module_rele(mp);
587 	return (TRUE);
588 }
589 
590 bool_t
591 fmd_adm_serdreset_1_svc(char *mname, char *sname, int *rvp, struct svc_req *req)
592 {
593 	fmd_module_t *mp;
594 	fmd_serd_eng_t *sgp;
595 	int err = 0;
596 
597 	if (fmd_rpc_deny(req)) {
598 		*rvp = FMD_ADM_ERR_PERM;
599 		return (TRUE);
600 	}
601 
602 	if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, mname)) == NULL) {
603 		*rvp = FMD_ADM_ERR_MODSRCH;
604 		return (TRUE);
605 	}
606 
607 	fmd_module_lock(mp);
608 
609 	if ((sgp = fmd_serd_eng_lookup(&mp->mod_serds, sname)) != NULL) {
610 		if (fmd_serd_eng_fired(sgp)) {
611 			err = FMD_ADM_ERR_SERDFIRED;
612 		} else {
613 			fmd_serd_eng_reset(sgp);
614 			fmd_module_setdirty(mp);
615 		}
616 	} else
617 		err = FMD_ADM_ERR_SERDSRCH;
618 
619 	fmd_module_unlock(mp);
620 	fmd_module_rele(mp);
621 
622 	*rvp = err;
623 	return (TRUE);
624 }
625 
626 bool_t
627 fmd_adm_logrotate_1_svc(char *name, int *rvp, struct svc_req *req)
628 {
629 	fmd_log_t **lpp, *old, *new;
630 	int try = 1, trylimit = 1;
631 
632 	hrtime_t nsec = 0;
633 	timespec_t tv;
634 
635 	if (fmd_rpc_deny(req)) {
636 		*rvp = FMD_ADM_ERR_PERM;
637 		return (TRUE);
638 	}
639 
640 	if (strcmp(name, "errlog") == 0)
641 		lpp = &fmd.d_errlog;
642 	else if (strcmp(name, "fltlog") == 0)
643 		lpp = &fmd.d_fltlog;
644 	else {
645 		*rvp = FMD_ADM_ERR_ROTSRCH;
646 		return (TRUE);
647 	}
648 
649 	(void) fmd_conf_getprop(fmd.d_conf, "log.tryrotate", &trylimit);
650 	(void) fmd_conf_getprop(fmd.d_conf, "log.waitrotate", &nsec);
651 
652 	tv.tv_sec = nsec / NANOSEC;
653 	tv.tv_nsec = nsec % NANOSEC;
654 
655 	/*
656 	 * To rotate a log file, grab d_log_lock as writer to make sure no
657 	 * one else can discover the current log pointer.  Then try to rotate
658 	 * the log.  If we're successful, release the old log pointer.
659 	 */
660 	do {
661 		if (try > 1)
662 			(void) nanosleep(&tv, NULL); /* wait for checkpoints */
663 
664 		(void) pthread_rwlock_wrlock(&fmd.d_log_lock);
665 		old = *lpp;
666 
667 		if ((new = fmd_log_rotate(old)) != NULL) {
668 			fmd_log_rele(old);
669 			*lpp = new;
670 		}
671 
672 		(void) pthread_rwlock_unlock(&fmd.d_log_lock);
673 
674 	} while (new == NULL && errno == EFMD_LOG_ROTBUSY && try++ < trylimit);
675 
676 	if (new != NULL)
677 		*rvp = 0;
678 	else if (errno == EFMD_LOG_ROTBUSY)
679 		*rvp = FMD_ADM_ERR_ROTBUSY;
680 	else
681 		*rvp = FMD_ADM_ERR_ROTFAIL;
682 
683 	return (TRUE);
684 }
685 
686 struct fmd_adm_repair {
687 	const char *rep_uuid;	/* case uuid that we are using to match */
688 	char *rep_buf;		/* buffer in which to store case ids */
689 	uint_t rep_buflen;	/* length of rep_buf buffer minus one */
690 	uint_t rep_cnt;		/* number of matching asrus found */
691 };
692 
693 static void
694 fmd_adm_caserepair_asru(fmd_asru_t *ap, void *arg)
695 {
696 	struct fmd_adm_repair *rp = arg;
697 
698 	if (fmd_asru_getcase(ap, rp->rep_buf, rp->rep_buflen + 1) == NULL ||
699 	    strcmp(rp->rep_buf, rp->rep_uuid) != 0)
700 		return;
701 
702 	/*
703 	 * If the UUID of the case associated with this ASRU matches, close all
704 	 * ASRUs contained by 'ap' and trigger appropriate case close events.
705 	 */
706 	fmd_asru_hash_apply(fmd.d_asrus,
707 	    fmd_adm_repair_containee, ap->asru_fmri);
708 
709 	/*
710 	 * We can call fmd_asru_clrflags() here rather than fmd_adm_repair()
711 	 * because if the case was still open, fmd_case_repair() closed it.
712 	 */
713 	(void) fmd_asru_clrflags(ap, FMD_ASRU_FAULTY, NULL, NULL);
714 	rp->rep_cnt++;
715 }
716 
717 bool_t
718 fmd_adm_caserepair_1_svc(char *uuid, int *rvp, struct svc_req *req)
719 {
720 	fmd_case_t *cp = NULL;
721 	int err = 0;
722 
723 	if (fmd_rpc_deny(req))
724 		err = FMD_ADM_ERR_PERM;
725 	else if ((cp = fmd_case_hash_lookup(fmd.d_cases, uuid)) == NULL)
726 		err = FMD_ADM_ERR_CASESRCH;
727 	else if (fmd_case_repair(cp) != 0)
728 		err = FMD_ADM_ERR_CASEOPEN;
729 
730 	/*
731 	 * If the case is not found, it could be already closed (i.e. the ASRU
732 	 * was successfully offlined).  Check all ASRUs for a matching UUID and
733 	 * try to repair them.  If any are found, set err to indicate success.
734 	 * If the case is found, we also check all ASRUs so that any ASRUs
735 	 * contained by those that have been repaired are repaired recursively.
736 	 */
737 	if (err == FMD_ADM_ERR_CASESRCH || err == 0) {
738 		struct fmd_adm_repair r;
739 
740 		r.rep_uuid = uuid;
741 		(void) fmd_conf_getprop(fmd.d_conf, "uuidlen", &r.rep_buflen);
742 		r.rep_buf = alloca(r.rep_buflen + 1);
743 		r.rep_cnt = 0;
744 
745 		fmd_asru_hash_apply(fmd.d_asrus, fmd_adm_caserepair_asru, &r);
746 
747 		if (err == FMD_ADM_ERR_CASESRCH && r.rep_cnt != 0)
748 			err = 0; /* one or more ASRUs had matching case IDs */
749 	}
750 
751 	if (cp != NULL)
752 		fmd_case_rele(cp);
753 
754 	*rvp = err;
755 	return (TRUE);
756 }
757 
758 int
759 fmd_adm_1_freeresult(SVCXPRT *xprt, xdrproc_t proc, caddr_t data)
760 {
761 	xdr_free(proc, data);
762 	svc_done(xprt);
763 	return (TRUE);
764 }
765 
766 /*
767  * Custom XDR routine for our API structure fmd_stat_t.  This function must
768  * match the definition of fmd_stat_t in <fmd_api.h> and must also match
769  * the corresponding routine in usr/src/lib/fm/libfmd_adm/common/fmd_adm.c.
770  */
771 bool_t
772 xdr_fmd_stat(XDR *xp, fmd_stat_t *sp)
773 {
774 	bool_t rv = TRUE;
775 
776 	rv &= xdr_opaque(xp, sp->fmds_name, sizeof (sp->fmds_name));
777 	rv &= xdr_u_int(xp, &sp->fmds_type);
778 	rv &= xdr_opaque(xp, sp->fmds_desc, sizeof (sp->fmds_desc));
779 
780 	switch (sp->fmds_type) {
781 	case FMD_TYPE_BOOL:
782 		rv &= xdr_int(xp, &sp->fmds_value.bool);
783 		break;
784 	case FMD_TYPE_INT32:
785 		rv &= xdr_int32_t(xp, &sp->fmds_value.i32);
786 		break;
787 	case FMD_TYPE_UINT32:
788 		rv &= xdr_uint32_t(xp, &sp->fmds_value.ui32);
789 		break;
790 	case FMD_TYPE_INT64:
791 		rv &= xdr_int64_t(xp, &sp->fmds_value.i64);
792 		break;
793 	case FMD_TYPE_UINT64:
794 	case FMD_TYPE_TIME:
795 	case FMD_TYPE_SIZE:
796 		rv &= xdr_uint64_t(xp, &sp->fmds_value.ui64);
797 		break;
798 	case FMD_TYPE_STRING:
799 		rv &= xdr_string(xp, &sp->fmds_value.str, ~0);
800 		break;
801 	}
802 
803 	return (rv);
804 }
805