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