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