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  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #include "pmconfig.h"
  27 #include <sys/mkdev.h>
  28 #include <sys/syslog.h>
  29 #include <sys/openpromio.h>
  30 #include <sys/mnttab.h>
  31 #include <sys/vtoc.h>
  32 #include <sys/efi_partition.h>
  33 #include <syslog.h>
  34 #include <stdlib.h>
  35 #include <sys/pm.h>
  36 #include <kstat.h>
  37 #include <sys/smbios.h>
  38 #include <libzfs.h>
  39 
  40 
  41 #define STRCPYLIM(dst, src, str) strcpy_limit(dst, src, sizeof (dst), str)
  42 #define LASTBYTE(str) (str + strlen(str) - 1)
  43 
  44 static char nerr_fmt[] = "number is out of range (%s)\n";
  45 static char alloc_fmt[] = "cannot allocate space for \"%s\", %s\n";
  46 static char set_thresh_fmt[] = "error setting threshold(s) for \"%s\", %s\n";
  47 static char bad_thresh_fmt[] = "bad threshold(s)\n";
  48 static char stat_fmt[] = "cannot stat \"%s\", %s\n";
  49 static char always_on[] = "always-on";
  50 
  51 #define PM_DEFAULT_ALGORITHM -1
  52 /*
  53  * When lines in a config file (usually "/etc/power.conf") start with
  54  * a recognized keyword, a "handler" routine is called for specific
  55  * CPR or PM -related action(s).  Each routine returns a status code
  56  * indicating whether all tasks were successful; if any errors occured,
  57  * future CPR or PM updates are skipped.  Following are the handler
  58  * routines for all keywords:
  59  */
  60 
  61 
  62 static char pm_cmd_string[32];
  63 
  64 static char *
  65 pm_map(int cmd)
  66 {
  67         pm_req_t req;
  68 
  69         req.value = cmd;
  70         req.data = (void *)pm_cmd_string;
  71         req.datasize = sizeof (pm_cmd_string);
  72 
  73         if (ioctl(pm_fd, PM_GET_CMD_NAME, &req) < 0) {
  74                 perror(gettext("PM_GET_CMD_NAME failed:"));
  75                 return ("??");
  76         }
  77         return (pm_cmd_string);
  78 }
  79 
  80 static int
  81 isonlist(char *listname, const char *man, const char *prod)
  82 {
  83         pm_searchargs_t sl;
  84         int ret;
  85 
  86         sl.pms_listname = listname;
  87         sl.pms_manufacturer = (char *)man;
  88         sl.pms_product = (char *)prod;
  89         ret = ioctl(pm_fd, PM_SEARCH_LIST, &sl);
  90         mesg(MDEBUG, "PM_SEARCH_LIST %s for %s,%s returns %d\n",
  91             listname, man, prod, ret);
  92         return (ret == 0);
  93 }
  94 
  95 static int
  96 do_ioctl(int ioctl_cmd, char *keyword, char *behavior, int suppress)
  97 {
  98         mesg(MDEBUG, "doing ioctl %s for %s ", pm_map(ioctl_cmd), keyword);
  99         if (ioctl(pm_fd, ioctl_cmd, NULL) == -1) {
 100                 int suppressed = suppress == -1 || suppress == errno;
 101                 if (!suppressed) {
 102                         mesg(MERR, "%s %s failed, %s\n", keyword, behavior,
 103                             strerror(errno));
 104                         return (NOUP);
 105                 } else {
 106                         mesg(MDEBUG, "%s %s failed, %s\n", keyword, behavior,
 107                             strerror(errno));
 108                         return (OKUP);
 109                 }
 110         }
 111         mesg(MDEBUG, "succeeded\n");
 112         return (OKUP);
 113 }
 114 
 115 /*
 116  * Check for valid cpupm behavior and communicate it to the kernel.
 117  */
 118 int
 119 cpupm(void)
 120 {
 121         struct bmtoc {
 122                 char *behavior;
 123                 char *mode;
 124                 int cmd;
 125                 int Errno;
 126         };
 127 
 128         static struct bmtoc bmlist[] = {
 129                 "disable",      "\0",           PM_STOP_CPUPM,          EINVAL,
 130                 "enable",       "poll-mode",    PM_START_CPUPM_POLL,    EBUSY,
 131                 "enable",       "event-mode",   PM_START_CPUPM_EV,      EBUSY,
 132                 "enable",       "\0",           PM_START_CPUPM,         EBUSY,
 133                 NULL,           0,              0,                      0
 134         };
 135         struct bmtoc *bp;
 136         char *behavior;
 137         char *mode;
 138 
 139         behavior = LINEARG(1);
 140         if ((mode = LINEARG(2)) == NULL)
 141                 mode = "\0";
 142 
 143         for (bp = bmlist; bp->cmd; bp++) {
 144                 if (strcmp(behavior, bp->behavior) == 0 &&
 145                     strcmp(mode, bp->mode) == 0) {
 146                         break;
 147                 }
 148         }
 149         if (bp->cmd == 0) {
 150                 if (LINEARG(2) == NULL) {
 151                         mesg(MERR, "invalid cpupm behavior \"%s\"\n", behavior);
 152                 } else {
 153                         mesg(MERR, "invalid cpupm behavior \"%s %s\"\n",
 154                             behavior, mode);
 155                 }
 156                 return (NOUP);
 157         }
 158         if (ioctl(pm_fd, bp->cmd, NULL) == -1 && errno != bp->Errno) {
 159                 mesg(MERR, "cpupm %s failed, %s\n",
 160                     behavior, strerror(errno));
 161                 return (NOUP);
 162         }
 163         return (OKUP);
 164 }
 165 
 166 /*
 167  * Check for valid cpu_deep_idle option and communicate it to the kernel.
 168  */
 169 int
 170 cpuidle(void)
 171 {
 172         struct btoc {
 173                 char *behavior;
 174                 int cmd;
 175                 int Errno;
 176         };
 177         static struct btoc blist[] = {
 178                 "disable",      PM_DISABLE_CPU_DEEP_IDLE, EINVAL,
 179                 "enable",       PM_ENABLE_CPU_DEEP_IDLE, EBUSY,
 180                 "default",      PM_DEFAULT_CPU_DEEP_IDLE, EBUSY,
 181                 NULL,           0, 0
 182         };
 183         struct btoc *bp;
 184         char *behavior;
 185 
 186         for (behavior = LINEARG(1), bp = blist; bp->cmd; bp++) {
 187                 if (strcmp(behavior, bp->behavior) == 0)
 188                         break;
 189         }
 190         if (bp->cmd == 0) {
 191                 mesg(MERR, "invalid cpu_deep_idle behavior \"%s\"\n", behavior);
 192                 return (NOUP);
 193         }
 194         if (ioctl(pm_fd, bp->cmd, NULL) == -1 && errno != bp->Errno) {
 195                 mesg(MERR, "cpu_deep_idle %s failed, %s\n",
 196                     behavior, strerror(errno));
 197                 return (NOUP);
 198         }
 199         return (OKUP);
 200 }
 201 
 202 /*
 203  * Two decisions are identical except for the list names and ioctl commands
 204  * inputs: whitelist, blacklist, yes, no
 205  * if (! ("S3" kstat exists))
 206  *      return (no)
 207  * if (SystemInformation.Manufacturer == "Sun Microsystems" &&
 208  *    (Pref_PM_Profile == Workstation || Pref_PM_Profile == Desktop)) {
 209  *      if (platform on blacklist)
 210  *              return (no)
 211  *      return (yes)
 212  * } else {
 213  *      if (platform on whitelist)
 214  *              return (yes)
 215  *      return (no)
 216  * }
 217  */
 218 
 219 int
 220 S3_helper(char *whitelist, char *blacklist, int yes, int no, char *keyword,
 221         char *behavior, int *didyes, int suppress)
 222 {
 223         int oflags = SMB_O_NOCKSUM | SMB_O_NOVERS;
 224         smbios_hdl_t *shp;
 225         smbios_system_t sys;
 226         id_t id;
 227         int ret;
 228         kstat_ctl_t *kc;
 229         kstat_t *ksp;
 230         kstat_named_t *dp;
 231         smbios_info_t info;
 232         int preferred_pm_profile = 0;
 233         char yesstr[32], nostr[32];     /* DEBUG */
 234 
 235         *didyes = 0;
 236 
 237         (void) strncpy(yesstr, pm_map(yes), sizeof (yesstr));
 238         (void) strncpy(nostr, pm_map(no), sizeof (nostr));
 239         mesg(MDEBUG, "S3_helper(%s, %s, %s, %s, %s, %s)\n", whitelist,
 240             blacklist, yesstr, nostr, keyword, behavior);
 241         if ((kc = kstat_open()) == NULL) {
 242                 mesg(MDEBUG, "kstat_open failed\n");
 243                 return (OKUP);
 244         }
 245         ksp = kstat_lookup(kc, "acpi", -1, "acpi");
 246         if (ksp == NULL) {
 247                 mesg(MDEBUG, "kstat_lookup 'acpi', -1, 'acpi' failed\n");
 248                 (void) kstat_close(kc);
 249                 return (OKUP);
 250         }
 251         (void) kstat_read(kc, ksp,  NULL);
 252         dp = kstat_data_lookup(ksp, "S3");
 253         if (dp == NULL || dp->value.l == 0) {
 254                 mesg(MDEBUG, "kstat_data_lookup 'S3' fails\n");
 255                 if (dp != NULL)
 256                         mesg(MDEBUG, "value.l %lx\n", dp->value.l);
 257                 (void) kstat_close(kc);
 258                 return (do_ioctl(no, keyword, behavior, suppress));
 259         }
 260         mesg(MDEBUG, "kstat indicates S3 support (%lx)\n", dp->value.l);
 261 
 262         if (!whitelist_only) {
 263                 /*
 264                  * We still have an ACPI ksp, search it again for
 265                  * 'preferred_pm_profile' (needs to be valid if we don't
 266                  * aren't only using a whitelist).
 267                  */
 268                 dp = kstat_data_lookup(ksp, "preferred_pm_profile");
 269                 if (dp == NULL) {
 270                         mesg(MDEBUG, "kstat_data_lookup 'ppmp fails\n");
 271                         (void) kstat_close(kc);
 272                         return (do_ioctl(no, keyword, behavior, suppress));
 273                 }
 274                 mesg(MDEBUG, "kstat indicates preferred_pm_profile is %lx\n",
 275                     dp->value.l);
 276                 preferred_pm_profile = dp->value.l;
 277         }
 278         (void) kstat_close(kc);
 279 
 280         if ((shp = smbios_open(NULL,
 281             SMB_VERSION, oflags, &ret)) == NULL) {
 282                 /* we promised not to complain */
 283                 /* we bail leaving it to the kernel default */
 284                 mesg(MDEBUG, "smbios_open failed %d\n", errno);
 285                 return (OKUP);
 286         }
 287         if ((id = smbios_info_system(shp, &sys)) == SMB_ERR) {
 288                 mesg(MDEBUG, "smbios_info_system failed %d\n", errno);
 289                 smbios_close(shp);
 290                 return (OKUP);
 291         }
 292         if (smbios_info_common(shp, id, &info) == SMB_ERR) {
 293                 mesg(MDEBUG, "smbios_info_common failed %d\n", errno);
 294                 smbios_close(shp);
 295                 return (OKUP);
 296         }
 297         mesg(MDEBUG, "Manufacturer: %s\n", info.smbi_manufacturer);
 298         mesg(MDEBUG, "Product: %s\n", info.smbi_product);
 299         smbios_close(shp);
 300 
 301         if (!whitelist_only) {
 302 #define PPP_DESKTOP 1
 303 #define PPP_WORKSTATION 3
 304                 if (strcmp(info.smbi_manufacturer, "Sun Microsystems") == 0 &&
 305                     (preferred_pm_profile == PPP_DESKTOP ||
 306                     preferred_pm_profile == PPP_WORKSTATION)) {
 307                         if (isonlist(blacklist,
 308                             info.smbi_manufacturer, info.smbi_product)) {
 309                                 return (do_ioctl(no, keyword, behavior,
 310                                     suppress));
 311                         } else {
 312                                 ret = do_ioctl(yes, keyword, behavior,
 313                                     suppress);
 314                                 *didyes = (ret == OKUP);
 315                                 return (ret);
 316                         }
 317                 }
 318         }
 319         if (isonlist(whitelist,
 320             info.smbi_manufacturer, info.smbi_product)) {
 321                 ret = do_ioctl(yes, keyword, behavior, suppress);
 322                 *didyes = (ret == OKUP);
 323                 return (ret);
 324         } else {
 325                 return (do_ioctl(no, keyword, behavior, suppress));
 326         }
 327 }
 328 
 329 int
 330 S3sup(void)     /* S3-support keyword handler */
 331 {
 332         struct btoc {
 333                 char *behavior;
 334                 int cmd;
 335         };
 336         static struct btoc blist[] = {
 337                 "default",      PM_DEFAULT_ALGORITHM,
 338                 "enable",       PM_ENABLE_S3,
 339                 "disable",      PM_DISABLE_S3,
 340                 NULL,           0
 341         };
 342         struct btoc *bp;
 343         char *behavior;
 344         int dontcare;
 345 
 346         for (behavior = LINEARG(1), bp = blist; bp->cmd; bp++) {
 347                 if (strcmp(behavior, bp->behavior) == 0)
 348                         break;
 349         }
 350         if (bp->cmd == 0) {
 351                 mesg(MERR, "invalid S3-support behavior \"%s\"\n", behavior);
 352                 return (NOUP);
 353         }
 354 
 355 
 356         switch (bp->cmd) {
 357 
 358         case PM_ENABLE_S3:
 359         case PM_DISABLE_S3:
 360                 return (do_ioctl(bp->cmd, "S3-support", behavior, EBUSY));
 361 
 362         case PM_DEFAULT_ALGORITHM:
 363                 /*
 364                  * we suppress errors in the "default" case because we
 365                  * already did an invisible default call, so we know we'll
 366                  * get EBUSY
 367                  */
 368                 return (S3_helper("S3-support-enable", "S3-support-disable",
 369                     PM_ENABLE_S3, PM_DISABLE_S3, "S3-support", behavior,
 370                     &dontcare, EBUSY));
 371 
 372         default:
 373                 mesg(MERR, "S3-support %s failed, %s\n", behavior,
 374                     strerror(errno));
 375                 return (NOUP);
 376         }
 377 }
 378 
 379 /*
 380  * Check for valid autoS3 behavior and save after ioctl success.
 381  */
 382 int
 383 autoS3(void)
 384 {
 385         struct btoc {
 386                 char *behavior;
 387                 int cmd;
 388         };
 389         static struct btoc blist[] = {
 390                 "default",      PM_DEFAULT_ALGORITHM,
 391                 "disable",      PM_STOP_AUTOS3,
 392                 "enable",       PM_START_AUTOS3,
 393                 NULL,           0
 394         };
 395         struct btoc *bp;
 396         char *behavior;
 397         int dontcare;
 398 
 399         for (behavior = LINEARG(1), bp = blist; bp->cmd; bp++) {
 400                 if (strcmp(behavior, bp->behavior) == 0)
 401                         break;
 402         }
 403         if (bp->cmd == 0) {
 404                 mesg(MERR, "invalid autoS3 behavior \"%s\"\n", behavior);
 405                 return (NOUP);
 406         }
 407 
 408         switch (bp->cmd) {
 409         default:
 410                 mesg(MERR, "autoS3 %s failed, %s\n",
 411                     behavior, strerror(errno));
 412                 mesg(MDEBUG, "unknown command\n", bp->cmd);
 413                 return (OKUP);
 414 
 415         case PM_STOP_AUTOS3:
 416         case PM_START_AUTOS3:
 417                 return (do_ioctl(bp->cmd, "autoS3", behavior, EBUSY));
 418 
 419         case PM_DEFAULT_ALGORITHM:
 420                 return (S3_helper("S3-autoenable", "S3-autodisable",
 421                     PM_START_AUTOS3, PM_STOP_AUTOS3, "autoS3", behavior,
 422                     &dontcare, EBUSY));
 423         }
 424 }
 425 
 426 
 427 /*
 428  * Check for valid autopm behavior and save after ioctl success.
 429  */
 430 int
 431 autopm(void)
 432 {
 433         struct btoc {
 434                 char *behavior;
 435                 int cmd, Errno, isdef;
 436         };
 437         static struct btoc blist[] = {
 438                 "default",      PM_START_PM,    -1,     1,
 439                 "disable",      PM_STOP_PM,     EINVAL, 0,
 440                 "enable",       PM_START_PM,    EBUSY,  0,
 441                 NULL,           0,              0,      0,
 442         };
 443         struct btoc *bp;
 444         char *behavior;
 445 
 446         for (behavior = LINEARG(1), bp = blist; bp->cmd; bp++) {
 447                 if (strcmp(behavior, bp->behavior) == 0)
 448                         break;
 449         }
 450         if (bp->cmd == 0) {
 451                 mesg(MERR, "invalid autopm behavior \"%s\"\n", behavior);
 452                 return (NOUP);
 453         }
 454 
 455         /*
 456          * for "default" behavior, do not enable autopm if not ESTAR_V3
 457          */
 458 #if defined(__sparc)
 459         if (!bp->isdef || (estar_vers == ESTAR_V3)) {
 460                 if (ioctl(pm_fd, bp->cmd, NULL) == -1 && errno != bp->Errno) {
 461                         mesg(MERR, "autopm %s failed, %s\n",
 462                             behavior, strerror(errno));
 463                         return (NOUP);
 464                 }
 465         }
 466         (void) strcpy(new_cc.apm_behavior, behavior);
 467         return (OKUP);
 468 #endif
 469 #if defined(__x86)
 470         if (!bp->isdef) {
 471                 if (ioctl(pm_fd, bp->cmd, NULL) == -1 && errno != bp->Errno) {
 472                         mesg(MERR, "autopm %s failed, %s\n",
 473                             behavior, strerror(errno));
 474                         return (NOUP);
 475                 }
 476                 mesg(MDEBUG, "autopm %s succeeded\n", behavior);
 477 
 478                 return (OKUP);
 479         } else {
 480                 int didenable;
 481                 int ret = S3_helper("autopm-enable", "autopm-disable",
 482                     PM_START_PM, PM_STOP_PM, "autopm", behavior, &didenable,
 483                     bp->Errno);
 484                 if (didenable) {
 485                         /* tell powerd to attach all devices */
 486                         new_cc.is_autopm_default = 1;
 487                         (void) strcpy(new_cc.apm_behavior, behavior);
 488                 }
 489                 return (ret);
 490         }
 491 #endif
 492 }
 493 
 494 
 495 static int
 496 gethm(char *src, int *hour, int *min)
 497 {
 498         if (sscanf(src, "%d:%d", hour, min) != 2) {
 499                 mesg(MERR, "bad time format (%s)\n", src);
 500                 return (-1);
 501         }
 502         return (0);
 503 }
 504 
 505 
 506 static void
 507 strcpy_limit(char *dst, char *src, size_t limit, char *info)
 508 {
 509         if (strlcpy(dst, src, limit) >= limit)
 510                 mesg(MEXIT, "%s is too long (%s)\n", info, src);
 511 }
 512 
 513 
 514 /*
 515  * Convert autoshutdown idle and start/finish times;
 516  * check and record autoshutdown behavior.
 517  */
 518 int
 519 autosd(void)
 520 {
 521         char **bp, *behavior;
 522         char *unrec = gettext("unrecognized autoshutdown behavior");
 523         static char *blist[] = {
 524                 "autowakeup", "default", "noshutdown",
 525                 "shutdown", "unconfigured", NULL
 526         };
 527 
 528         new_cc.as_idle = atoi(LINEARG(1));
 529         if (gethm(LINEARG(2), &new_cc.as_sh, &new_cc.as_sm) ||
 530             gethm(LINEARG(3), &new_cc.as_fh, &new_cc.as_fm))
 531                 return (NOUP);
 532         mesg(MDEBUG, "idle %d, start %d:%02d, finish %d:%02d\n",
 533             new_cc.as_idle, new_cc.as_sh, new_cc.as_sm,
 534             new_cc.as_fh, new_cc.as_fm);
 535 
 536         for (behavior = LINEARG(4), bp = blist; *bp; bp++) {
 537                 if (strcmp(behavior, *bp) == 0)
 538                         break;
 539         }
 540         if (*bp == NULL) {
 541                 mesg(MERR, "%s: \"%s\"\n", unrec, behavior);
 542                 return (NOUP);
 543         }
 544         STRCPYLIM(new_cc.as_behavior, *bp, unrec);
 545         return (OKUP);
 546 }
 547 
 548 
 549 /*
 550  * Check for a real device and try to resolve to a full path.
 551  * The orig/resolved path may be modified into a prom pathname,
 552  * and an allocated copy of the result is stored at *destp;
 553  * the caller will need to free that space.  Returns 1 for any
 554  * error, otherwise 0; also sets *errp after an alloc error.
 555  */
 556 static int
 557 devpath(char **destp, char *src, int *errp)
 558 {
 559         struct stat stbuf;
 560         char buf[PATH_MAX];
 561         char *cp, *dstr;
 562         int devok, dcs = 0;
 563         size_t len;
 564 
 565         /*
 566          * When there's a real device, try to resolve the path
 567          * and trim the leading "/devices" component.
 568          */
 569         if ((devok = (stat(src, &stbuf) == 0 && stbuf.st_rdev)) != 0) {
 570                 if (realpath(src, buf) == NULL) {
 571                         mesg(MERR, "realpath cannot resolve \"%s\"\n",
 572                             src, strerror(errno));
 573                         return (1);
 574                 }
 575                 src = buf;
 576                 dstr = "/devices";
 577                 len = strlen(dstr);
 578                 dcs = (strncmp(src, dstr, len) == 0);
 579                 if (dcs)
 580                         src += len;
 581         } else
 582                 mesg(MDEBUG, stat_fmt, src, strerror(errno));
 583 
 584         /*
 585          * When the path has ":anything", display an error for
 586          * a non-device or truncate a resolved+modifed path.
 587          */
 588         if ((cp = strchr(src, ':')) != NULL) {
 589                 if (devok == 0) {
 590                         mesg(MERR, "physical path may not contain "
 591                             "a minor string (%s)\n", src);
 592                         return (1);
 593                 } else if (dcs)
 594                         *cp = '\0';
 595         }
 596 
 597         if ((*destp = strdup(src)) == NULL) {
 598                 *errp = NOUP;
 599                 mesg(MERR, alloc_fmt, src, strerror(errno));
 600         }
 601         return (*destp == NULL);
 602 }
 603 
 604 
 605 /*
 606  * Call pm ioctl request(s) to set property/device dependencies.
 607  */
 608 static int
 609 dev_dep_common(int isprop)
 610 {
 611         int cmd, argn, upval = OKUP;
 612         char *src, *first, **destp;
 613         pm_req_t pmreq;
 614 
 615         bzero(&pmreq, sizeof (pmreq));
 616         src = LINEARG(1);
 617         if (isprop) {
 618                 cmd = PM_ADD_DEPENDENT_PROPERTY;
 619                 first = NULL;
 620                 pmreq.pmreq_kept = src;
 621         } else {
 622                 cmd = PM_ADD_DEPENDENT;
 623                 if (devpath(&first, src, &upval))
 624                         return (upval);
 625                 pmreq.pmreq_kept = first;
 626         }
 627         destp = &pmreq.pmreq_keeper;
 628 
 629         /*
 630          * Now loop through any dependents.
 631          */
 632         for (argn = 2; (src = LINEARG(argn)) != NULL; argn++) {
 633                 if (devpath(destp, src, &upval)) {
 634                         if (upval != OKUP)
 635                                 return (upval);
 636                         break;
 637                 }
 638                 if ((upval = ioctl(pm_fd, cmd, &pmreq)) == -1) {
 639                         mesg(MDEBUG, "pm ioctl, cmd %d, errno %d\n"
 640                             "kept \"%s\", keeper \"%s\"\n",
 641                             cmd, errno, pmreq.pmreq_kept, pmreq.pmreq_keeper);
 642                         mesg(MERR, "cannot set \"%s\" dependency "
 643                             "for \"%s\", %s\n", pmreq.pmreq_keeper,
 644                             pmreq.pmreq_kept, strerror(errno));
 645                 }
 646                 free(*destp);
 647                 *destp = NULL;
 648                 if (upval != OKUP)
 649                         break;
 650         }
 651 
 652         free(first);
 653         return (upval);
 654 }
 655 
 656 
 657 int
 658 ddprop(void)
 659 {
 660         return (dev_dep_common(1));
 661 }
 662 
 663 
 664 int
 665 devdep(void)
 666 {
 667         return (dev_dep_common(0));
 668 }
 669 
 670 
 671 /*
 672  * Convert a numeric string (with a possible trailing scaling byte)
 673  * into an integer.  Returns a converted value and *nerrp unchanged,
 674  * or 0 with *nerrp set to 1 for a conversion error.
 675  */
 676 static int
 677 get_scaled_value(char *str, int *nerrp)
 678 {
 679         longlong_t svalue = 0, factor = 1;
 680         char *sp;
 681 
 682         errno = 0;
 683         svalue = strtol(str, &sp, 0);
 684         if (errno || (*str != '-' && (*str < '0' || *str > '9')))
 685                 *nerrp = 1;
 686         else if (sp && *sp != '\0') {
 687                 if (*sp == 'h')
 688                         factor = 3600;
 689                 else if (*sp == 'm')
 690                         factor = 60;
 691                 else if (*sp != 's')
 692                         *nerrp = 1;
 693         }
 694         /* any bytes following sp are ignored */
 695 
 696         if (*nerrp == 0) {
 697                 svalue *= factor;
 698                 if (svalue < INT_MIN || svalue > INT_MAX)
 699                         *nerrp = 1;
 700         }
 701         if (*nerrp)
 702                 mesg(MERR, nerr_fmt, str);
 703         mesg(MDEBUG, "got scaled value %d\n", (int)svalue);
 704         return ((int)svalue);
 705 }
 706 
 707 
 708 /*
 709  * Increment the count of threshold values,
 710  * reallocate *vlistp and append another element.
 711  * Returns 1 on error, otherwise 0.
 712  */
 713 static int
 714 vlist_append(int **vlistp, int *vcntp, int value)
 715 {
 716         (*vcntp)++;
 717         if ((*vlistp = realloc(*vlistp, *vcntp * sizeof (**vlistp))) != NULL)
 718                 *(*vlistp + *vcntp - 1) = value;
 719         else
 720                 mesg(MERR, alloc_fmt, "threshold list", strerror(errno));
 721         return (*vlistp == NULL);
 722 }
 723 
 724 
 725 /*
 726  * Convert a single threshold string or paren groups of thresh's as
 727  * described below.  All thresh's are saved to an allocated list at
 728  * *vlistp; the caller will need to free that space.  On return:
 729  * *vcntp is the count of the vlist array, and vlist is either
 730  * a single thresh or N groups of thresh's with a trailing zero:
 731  * (cnt_1 thr_1a thr_1b [...]) ... (cnt_N thr_Na thr_Nb [...]) 0.
 732  * Returns 0 when all conversions were OK, and 1 for any syntax,
 733  * conversion, or alloc error.
 734  */
 735 static int
 736 get_thresh(int **vlistp, int *vcntp)
 737 {
 738         int argn, value, gci = 0, grp_cnt = 0, paren = 0, nerr = 0;
 739         char *rp, *src;
 740 
 741         for (argn = 2; (src = LINEARG(argn)) != NULL; argn++) {
 742                 if (*src == LPAREN) {
 743                         gci = *vcntp;
 744                         if ((nerr = vlist_append(vlistp, vcntp, 0)) != 0)
 745                                 break;
 746                         paren = 1;
 747                         src++;
 748                 }
 749                 if (*(rp = LASTBYTE(src)) == RPAREN) {
 750                         if (paren) {
 751                                 grp_cnt = *vcntp - gci;
 752                                 *(*vlistp + gci) = grp_cnt;
 753                                 paren = 0;
 754                                 *rp = '\0';
 755                         } else {
 756                                 nerr = 1;
 757                                 break;
 758                         }
 759                 }
 760 
 761                 value = get_scaled_value(src, &nerr);
 762                 if (nerr || (nerr = vlist_append(vlistp, vcntp, value)))
 763                         break;
 764         }
 765 
 766         if (nerr == 0 && grp_cnt)
 767                 nerr = vlist_append(vlistp, vcntp, 0);
 768         return (nerr);
 769 }
 770 
 771 
 772 /*
 773  * Set device thresholds from (3) formats:
 774  *      path    "always-on"
 775  *      path    time-spec: [0-9]+[{h,m,s}]
 776  *      path    (ts1 ts2 ...)+
 777  */
 778 int
 779 devthr(void)
 780 {
 781         int cmd, upval = OKUP, nthresh = 0, *vlist = NULL;
 782         pm_req_t pmreq;
 783 
 784         bzero(&pmreq, sizeof (pmreq));
 785         if (devpath(&pmreq.physpath, LINEARG(1), &upval))
 786                 return (upval);
 787 
 788         if (strcmp(LINEARG(2), always_on) == 0) {
 789                 cmd = PM_SET_DEVICE_THRESHOLD;
 790                 pmreq.value = INT_MAX;
 791         } else if (get_thresh(&vlist, &nthresh)) {
 792                 mesg(MERR, bad_thresh_fmt);
 793                 upval = NOUP;
 794         } else if (nthresh == 1) {
 795                 pmreq.value = *vlist;
 796                 cmd = PM_SET_DEVICE_THRESHOLD;
 797         } else {
 798                 pmreq.data = vlist;
 799                 pmreq.datasize = (nthresh * sizeof (*vlist));
 800                 cmd = PM_SET_COMPONENT_THRESHOLDS;
 801         }
 802 
 803         if (upval != NOUP && (upval = ioctl(pm_fd, cmd, &pmreq)) == -1)
 804                 mesg(MERR, set_thresh_fmt, pmreq.physpath, strerror(errno));
 805 
 806         free(vlist);
 807         free(pmreq.physpath);
 808         return (upval);
 809 }
 810 
 811 
 812 static int
 813 scan_int(char *src, int *dst)
 814 {
 815         long lval;
 816 
 817         errno = 0;
 818 
 819         lval = strtol(LINEARG(1), NULL, 0);
 820         if (errno || lval > INT_MAX || lval < 0) {
 821                 mesg(MERR, nerr_fmt, src);
 822                 return (NOUP);
 823         }
 824 
 825         *dst = (int)lval;
 826         return (OKUP);
 827 }
 828 
 829 static int
 830 scan_float(char *src, float *dst)
 831 {
 832         float fval;
 833 
 834         errno = 0;
 835 
 836         fval = strtof(src, NULL);
 837         if (errno || fval < 0.0) {
 838                 mesg(MERR, nerr_fmt, src);
 839                 return (NOUP);
 840         }
 841 
 842         *dst = fval;
 843         return (OKUP);
 844 }
 845 
 846 
 847 int
 848 dreads(void)
 849 {
 850         return (scan_int(LINEARG(1), &new_cc.diskreads_thold));
 851 }
 852 
 853 
 854 /*
 855  * Set pathname for idlecheck;
 856  * an overflowed pathname is treated as a fatal error.
 857  */
 858 int
 859 idlechk(void)
 860 {
 861         STRCPYLIM(new_cc.idlecheck_path, LINEARG(1), "idle path");
 862         return (OKUP);
 863 }
 864 
 865 
 866 int
 867 loadavg(void)
 868 {
 869         return (scan_float(LINEARG(1), &new_cc.loadaverage_thold));
 870 }
 871 
 872 
 873 int
 874 nfsreq(void)
 875 {
 876         return (scan_int(LINEARG(1), &new_cc.nfsreqs_thold));
 877 }
 878 
 879 #ifdef sparc
 880 static char open_fmt[] = "cannot open \"%s\", %s\n";
 881 
 882 /*
 883  * Verify the filesystem type for a regular statefile is "ufs"
 884  * or verify a block device is not in use as a mounted filesytem.
 885  * Returns 1 if any error, otherwise 0.
 886  */
 887 static int
 888 check_mount(char *sfile, dev_t sfdev, int ufs)
 889 {
 890         char *src, *err_fmt = NULL, *mnttab = MNTTAB;
 891         int rgent, match = 0;
 892         struct mnttab zroot = { 0 };
 893         struct mnttab entry;
 894         struct extmnttab ent;
 895         FILE *fp;
 896 
 897         if ((fp = fopen(mnttab, "r")) == NULL) {
 898                 mesg(MERR, open_fmt, mnttab, strerror(errno));
 899                 return (1);
 900         }
 901 
 902         if (ufs) {
 903                 zroot.mnt_mountp = "/";
 904                 zroot.mnt_fstype = "zfs";
 905                 if (getmntany(fp, &entry, &zroot) == 0) {
 906                         err_fmt = "ufs statefile with zfs root is not"
 907                             " supported\n";
 908                         mesg(MERR, err_fmt, sfile);
 909                         (void) fclose(fp);
 910                         return (1);
 911                 }
 912                 resetmnttab(fp);
 913         }
 914         /*
 915          * Search for a matching dev_t;
 916          * ignore non-ufs filesystems for a regular statefile.
 917          */
 918         while ((rgent = getextmntent(fp, &ent, sizeof (ent))) != -1) {
 919                 if (rgent > 0) {
 920                         mesg(MERR, "error reading \"%s\"\n", mnttab);
 921                         (void) fclose(fp);
 922                         return (1);
 923                 } else if (ufs && strcmp(ent.mnt_fstype, "ufs"))
 924                         continue;
 925                 else if (makedev(ent.mnt_major, ent.mnt_minor) == sfdev) {
 926                         match = 1;
 927                         break;
 928                 }
 929         }
 930 
 931         /*
 932          * No match is needed for a block device statefile,
 933          * a match is needed for a regular statefile.
 934          */
 935         if (match == 0) {
 936                 if (new_cc.cf_type != CFT_UFS)
 937                         STRCPYLIM(new_cc.cf_devfs, sfile, "block statefile");
 938                 else
 939                         err_fmt = "cannot find ufs mount point for \"%s\"\n";
 940         } else if (new_cc.cf_type == CFT_UFS) {
 941                 STRCPYLIM(new_cc.cf_fs, ent.mnt_mountp, "mnt entry");
 942                 STRCPYLIM(new_cc.cf_devfs, ent.mnt_special, "mnt special");
 943                 while (*(sfile + 1) == '/') sfile++;
 944                 src = sfile + strlen(ent.mnt_mountp);
 945                 while (*src == '/') src++;
 946                 STRCPYLIM(new_cc.cf_path, src, "statefile path");
 947         } else
 948                 err_fmt = "statefile device \"%s\" is a mounted filesystem\n";
 949         (void) fclose(fp);
 950         if (err_fmt)
 951                 mesg(MERR, err_fmt, sfile);
 952         return (err_fmt != NULL);
 953 }
 954 
 955 
 956 /*
 957  * Convert a Unix device to a prom device and save on success,
 958  * log any ioctl/conversion error.
 959  */
 960 static int
 961 utop(char *fs_name, char *prom_name)
 962 {
 963         union obpbuf {
 964                 char    buf[OBP_MAXPATHLEN + sizeof (uint_t)];
 965                 struct  openpromio oppio;
 966         };
 967         union obpbuf oppbuf;
 968         struct openpromio *opp;
 969         char *promdev = "/dev/openprom";
 970         int fd, upval;
 971 
 972         if ((fd = open(promdev, O_RDONLY)) == -1) {
 973                 mesg(MERR, open_fmt, promdev, strerror(errno));
 974                 return (NOUP);
 975         }
 976 
 977         opp = &oppbuf.oppio;
 978         opp->oprom_size = OBP_MAXPATHLEN;
 979         strcpy_limit(opp->oprom_array, fs_name,
 980             OBP_MAXPATHLEN, "statefile device");
 981         upval = ioctl(fd, OPROMDEV2PROMNAME, opp);
 982         (void) close(fd);
 983         if (upval == OKUP) {
 984                 strcpy_limit(prom_name, opp->oprom_array, OBP_MAXPATHLEN,
 985                     "prom device");
 986         } else {
 987                 openlog("pmconfig", 0, LOG_DAEMON);
 988                 syslog(LOG_NOTICE,
 989                     gettext("cannot convert \"%s\" to prom device"),
 990                     fs_name);
 991                 closelog();
 992         }
 993 
 994         return (upval);
 995 }
 996 
 997 /*
 998  * given the path to a zvol, return the cXtYdZ name
 999  * returns < 0 on error, 0 if it isn't a zvol, > 1 on success
1000  */
1001 static int
1002 ztop(char *arg, char *diskname)
1003 {
1004         zpool_handle_t *zpool_handle;
1005         nvlist_t *config, *nvroot;
1006         nvlist_t **child;
1007         uint_t children;
1008         libzfs_handle_t *lzfs;
1009         char *vname;
1010         char *p;
1011         char pool_name[MAXPATHLEN];
1012 
1013         if (strncmp(arg, "/dev/zvol/dsk/", 14)) {
1014                 return (0);
1015         }
1016         arg += 14;
1017         (void) strncpy(pool_name, arg, MAXPATHLEN);
1018         if (p = strchr(pool_name, '/'))
1019                 *p = '\0';
1020         STRCPYLIM(new_cc.cf_fs, p + 1, "statefile path");
1021 
1022         if ((lzfs = libzfs_init()) == NULL) {
1023                 mesg(MERR, "failed to initialize ZFS library\n");
1024                 return (-1);
1025         }
1026         if ((zpool_handle = zpool_open(lzfs, pool_name)) == NULL) {
1027                 mesg(MERR, "couldn't open pool '%s'\n", pool_name);
1028                 libzfs_fini(lzfs);
1029                 return (-1);
1030         }
1031         config = zpool_get_config(zpool_handle, NULL);
1032         if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
1033             &nvroot) != 0) {
1034                 zpool_close(zpool_handle);
1035                 libzfs_fini(lzfs);
1036                 return (-1);
1037         }
1038         verify(nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
1039             &child, &children) == 0);
1040         if (children != 1) {
1041                 mesg(MERR, "expected one vdev, got %d\n", children);
1042                 zpool_close(zpool_handle);
1043                 libzfs_fini(lzfs);
1044                 return (-1);
1045         }
1046         vname = zpool_vdev_name(lzfs, zpool_handle, child[0], B_FALSE);
1047         if (vname == NULL) {
1048                 mesg(MERR, "couldn't determine vdev name\n");
1049                 zpool_close(zpool_handle);
1050                 libzfs_fini(lzfs);
1051                 return (-1);
1052         }
1053         (void) strcpy(diskname, "/dev/dsk/");
1054         (void) strcat(diskname, vname);
1055         free(vname);
1056         zpool_close(zpool_handle);
1057         libzfs_fini(lzfs);
1058         return (1);
1059 }
1060 
1061 /*
1062  * returns NULL if the slice is good (e.g. does not start at block
1063  * zero, or a string describing the error if it doesn't
1064  */
1065 static boolean_t
1066 is_good_slice(char *sfile, char **err)
1067 {
1068         int fd, rc;
1069         struct vtoc vtoc;
1070         dk_gpt_t *gpt;
1071         char rdskname[MAXPATHLEN];
1072         char *x, *y;
1073 
1074         *err = NULL;
1075         /* convert from dsk to rdsk */
1076         STRCPYLIM(rdskname, sfile, "disk name");
1077         x = strstr(rdskname, "dsk/");
1078         y = strstr(sfile, "dsk/");
1079         if (x != NULL) {
1080                 *x++ = 'r';
1081                 (void) strcpy(x, y);
1082         }
1083 
1084         if ((fd = open(rdskname, O_RDONLY)) == -1) {
1085                 *err = "could not open '%s'\n";
1086         } else if ((rc = read_vtoc(fd, &vtoc)) >= 0) {
1087                 /*
1088                  * we got a slice number; now check the block
1089                  * number where the slice starts
1090                  */
1091                 if (vtoc.v_part[rc].p_start < 2)
1092                         *err = "using '%s' would clobber the disk label\n";
1093                 (void) close(fd);
1094                 return (*err ? B_FALSE : B_TRUE);
1095         } else if ((rc == VT_ENOTSUP) &&
1096             (efi_alloc_and_read(fd, &gpt)) >= 0) {
1097                 /* EFI slices don't clobber the disk label */
1098                 free(gpt);
1099                 (void) close(fd);
1100                 return (B_TRUE);
1101         } else
1102                 *err = "could not read partition table from '%s'\n";
1103         return (B_FALSE);
1104 }
1105 
1106 /*
1107  * Check for a valid statefile pathname, inode and mount status.
1108  */
1109 int
1110 sfpath(void)
1111 {
1112         static int statefile;
1113         char *err_fmt = NULL;
1114         char *sfile, *sp, ch;
1115         char diskname[256];
1116         struct stat stbuf;
1117         int dir = 0;
1118         dev_t dev;
1119 
1120         if (statefile) {
1121                 mesg(MERR, "ignored redundant statefile entry\n");
1122                 return (OKUP);
1123         } else if (ua_err) {
1124                 if (ua_err != ENOTSUP)
1125                         mesg(MERR, "uadmin(A_FREEZE, A_CHECK, 0): %s\n",
1126                             strerror(ua_err));
1127                 return (NOUP);
1128         }
1129 
1130         /*
1131          * Check for an absolute path and trim any trailing '/'.
1132          */
1133         sfile = LINEARG(1);
1134         if (*sfile != '/') {
1135                 mesg(MERR, "statefile requires an absolute path\n");
1136                 return (NOUP);
1137         }
1138         for (sp = sfile + strlen(sfile) - 1; sp > sfile && *sp == '/'; sp--)
1139                 *sp = '\0';
1140 
1141         /*
1142          * If the statefile doesn't exist, the leading path must be a dir.
1143          */
1144         if (stat(sfile, &stbuf) == -1) {
1145                 if (errno == ENOENT) {
1146                         dir = 1;
1147                         if ((sp = strrchr(sfile, '/')) == sfile)
1148                                 sp++;
1149                         ch = *sp;
1150                         *sp = '\0';
1151                         if (stat(sfile, &stbuf) == -1)
1152                                 err_fmt = stat_fmt;
1153                         *sp = ch;
1154                 } else
1155                         err_fmt = stat_fmt;
1156                 if (err_fmt) {
1157                         mesg(MERR, err_fmt, sfile, strerror(errno));
1158                         return (NOUP);
1159                 }
1160         }
1161 
1162         /*
1163          * Check for regular/dir/block types, set cf_type and dev.
1164          */
1165         if (S_ISREG(stbuf.st_mode) || (dir && S_ISDIR(stbuf.st_mode))) {
1166                 new_cc.cf_type = CFT_UFS;
1167                 dev = stbuf.st_dev;
1168         } else if (S_ISBLK(stbuf.st_mode)) {
1169                 if (is_good_slice(sfile, &err_fmt)) {
1170                         switch (ztop(sfile, diskname)) {
1171                                 case 1:
1172                                         new_cc.cf_type = CFT_ZVOL;
1173                                         break;
1174                                 case 0:
1175                                         new_cc.cf_type = CFT_SPEC;
1176                                         break;
1177                                 case -1:
1178                                 default:
1179                                         return (NOUP);
1180                         }
1181                         dev = stbuf.st_rdev;
1182                 }
1183         } else
1184                 err_fmt = "bad file type for \"%s\"\n"
1185                     "statefile must be a regular file or block device\n";
1186         if (err_fmt) {
1187                 mesg(MERR, err_fmt, sfile);
1188                 return (NOUP);
1189         }
1190         if (check_mount(sfile, dev, (new_cc.cf_type == CFT_UFS)))
1191                 return (NOUP);
1192         if (new_cc.cf_type == CFT_ZVOL) {
1193                 if (utop(diskname, new_cc.cf_dev_prom))
1194                         return (NOUP);
1195         } else if (utop(new_cc.cf_devfs, new_cc.cf_dev_prom)) {
1196                 return (NOUP);
1197         }
1198         new_cc.cf_magic = CPR_CONFIG_MAGIC;
1199         statefile = 1;
1200         return (OKUP);
1201 }
1202 #endif /* sparc */
1203 
1204 
1205 /*
1206  * Common function to set a system or cpu threshold.
1207  */
1208 static int
1209 cmnthr(int req)
1210 {
1211         int value, nerr = 0, upval = OKUP;
1212         char *thresh = LINEARG(1);
1213 
1214         if (strcmp(thresh, always_on) == 0)
1215                 value = INT_MAX;
1216         else if ((value = get_scaled_value(thresh, &nerr)) < 0 || nerr) {
1217                 mesg(MERR, "%s must be a positive value\n", LINEARG(0));
1218                 upval = NOUP;
1219         }
1220         if (upval == OKUP)
1221                 (void) ioctl(pm_fd, req, value);
1222         return (upval);
1223 }
1224 
1225 
1226 /*
1227  * Try setting system threshold.
1228  */
1229 int
1230 systhr(void)
1231 {
1232         return (cmnthr(PM_SET_SYSTEM_THRESHOLD));
1233 }
1234 
1235 
1236 /*
1237  * Try setting cpu threshold.
1238  */
1239 int
1240 cputhr(void)
1241 {
1242         return (cmnthr(PM_SET_CPU_THRESHOLD));
1243 }
1244 
1245 
1246 int
1247 tchars(void)
1248 {
1249         return (scan_int(LINEARG(1), &new_cc.ttychars_thold));
1250 }