Home | History | Annotate | Download | only in power
      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, ':')) {
    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)))
    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, 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))
    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 }
   1251