10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 53028Smh27603 * Common Development and Distribution License (the "License"). 63028Smh27603 * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 228906SEric.Saxe@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #include "pmconfig.h" 270Sstevel@tonic-gate #include <sys/mkdev.h> 280Sstevel@tonic-gate #include <sys/syslog.h> 290Sstevel@tonic-gate #include <sys/openpromio.h> 300Sstevel@tonic-gate #include <sys/mnttab.h> 316423Sgw25295 #include <sys/vtoc.h> 326423Sgw25295 #include <sys/efi_partition.h> 330Sstevel@tonic-gate #include <syslog.h> 340Sstevel@tonic-gate #include <stdlib.h> 355295Srandyf #include <sys/pm.h> 365295Srandyf #include <kstat.h> 375295Srandyf #include <sys/smbios.h> 386423Sgw25295 #include <libzfs.h> 390Sstevel@tonic-gate 400Sstevel@tonic-gate 410Sstevel@tonic-gate #define STRCPYLIM(dst, src, str) strcpy_limit(dst, src, sizeof (dst), str) 420Sstevel@tonic-gate #define LASTBYTE(str) (str + strlen(str) - 1) 430Sstevel@tonic-gate 440Sstevel@tonic-gate static char nerr_fmt[] = "number is out of range (%s)\n"; 450Sstevel@tonic-gate static char alloc_fmt[] = "cannot allocate space for \"%s\", %s\n"; 460Sstevel@tonic-gate static char set_thresh_fmt[] = "error setting threshold(s) for \"%s\", %s\n"; 470Sstevel@tonic-gate static char bad_thresh_fmt[] = "bad threshold(s)\n"; 480Sstevel@tonic-gate static char stat_fmt[] = "cannot stat \"%s\", %s\n"; 490Sstevel@tonic-gate static char always_on[] = "always-on"; 500Sstevel@tonic-gate 515295Srandyf #define PM_DEFAULT_ALGORITHM -1 520Sstevel@tonic-gate /* 530Sstevel@tonic-gate * When lines in a config file (usually "/etc/power.conf") start with 540Sstevel@tonic-gate * a recognized keyword, a "handler" routine is called for specific 550Sstevel@tonic-gate * CPR or PM -related action(s). Each routine returns a status code 560Sstevel@tonic-gate * indicating whether all tasks were successful; if any errors occured, 570Sstevel@tonic-gate * future CPR or PM updates are skipped. Following are the handler 580Sstevel@tonic-gate * routines for all keywords: 590Sstevel@tonic-gate */ 600Sstevel@tonic-gate 610Sstevel@tonic-gate 625295Srandyf static char pm_cmd_string[32]; 635295Srandyf 645295Srandyf static char * 655295Srandyf pm_map(int cmd) 665295Srandyf { 675295Srandyf pm_req_t req; 685295Srandyf 695295Srandyf req.value = cmd; 705295Srandyf req.data = (void *)pm_cmd_string; 715295Srandyf req.datasize = sizeof (pm_cmd_string); 725295Srandyf 735295Srandyf if (ioctl(pm_fd, PM_GET_CMD_NAME, &req) < 0) { 746171Smh27603 perror(gettext("PM_GET_CMD_NAME failed:")); 755295Srandyf return ("??"); 765295Srandyf } 775295Srandyf return (pm_cmd_string); 785295Srandyf } 795295Srandyf 805295Srandyf static int 815295Srandyf isonlist(char *listname, const char *man, const char *prod) 825295Srandyf { 835295Srandyf pm_searchargs_t sl; 845295Srandyf int ret; 855295Srandyf 865295Srandyf sl.pms_listname = listname; 875295Srandyf sl.pms_manufacturer = (char *)man; 885295Srandyf sl.pms_product = (char *)prod; 895295Srandyf ret = ioctl(pm_fd, PM_SEARCH_LIST, &sl); 905295Srandyf mesg(MDEBUG, "PM_SEARCH_LIST %s for %s,%s returns %d\n", 915295Srandyf listname, man, prod, ret); 925295Srandyf return (ret == 0); 935295Srandyf } 945295Srandyf 955295Srandyf static int 965295Srandyf do_ioctl(int ioctl_cmd, char *keyword, char *behavior, int suppress) 975295Srandyf { 985295Srandyf mesg(MDEBUG, "doing ioctl %s for %s ", pm_map(ioctl_cmd), keyword); 995295Srandyf if (ioctl(pm_fd, ioctl_cmd, NULL) == -1) { 1005295Srandyf int suppressed = suppress == -1 || suppress == errno; 1015295Srandyf if (!suppressed) { 1025295Srandyf mesg(MERR, "%s %s failed, %s\n", keyword, behavior, 1035295Srandyf strerror(errno)); 1045295Srandyf return (NOUP); 1055295Srandyf } else { 1066171Smh27603 mesg(MDEBUG, "%s %s failed, %s\n", keyword, behavior, 1076171Smh27603 strerror(errno)); 1085295Srandyf return (OKUP); 1095295Srandyf } 1105295Srandyf } 1115295Srandyf mesg(MDEBUG, "succeeded\n"); 1125295Srandyf return (OKUP); 1135295Srandyf } 1145295Srandyf 1150Sstevel@tonic-gate /* 1163028Smh27603 * Check for valid cpupm behavior and communicate it to the kernel. 1173028Smh27603 */ 1183028Smh27603 int 1193028Smh27603 cpupm(void) 1203028Smh27603 { 1218906SEric.Saxe@Sun.COM struct bmtoc { 1228906SEric.Saxe@Sun.COM char *behavior; 1238906SEric.Saxe@Sun.COM char *mode; 1248906SEric.Saxe@Sun.COM int cmd; 1258906SEric.Saxe@Sun.COM int Errno; 1268906SEric.Saxe@Sun.COM }; 1278906SEric.Saxe@Sun.COM 1288906SEric.Saxe@Sun.COM static struct bmtoc bmlist[] = { 1298906SEric.Saxe@Sun.COM "disable", "\0", PM_STOP_CPUPM, EINVAL, 1308906SEric.Saxe@Sun.COM "enable", "poll-mode", PM_START_CPUPM_POLL, EBUSY, 1318906SEric.Saxe@Sun.COM "enable", "event-mode", PM_START_CPUPM_EV, EBUSY, 1328906SEric.Saxe@Sun.COM "enable", "\0", PM_START_CPUPM, EBUSY, 1338906SEric.Saxe@Sun.COM NULL, 0, 0, 0 1348906SEric.Saxe@Sun.COM }; 1358906SEric.Saxe@Sun.COM struct bmtoc *bp; 1368906SEric.Saxe@Sun.COM char *behavior; 1378906SEric.Saxe@Sun.COM char *mode; 1388906SEric.Saxe@Sun.COM 1398906SEric.Saxe@Sun.COM behavior = LINEARG(1); 1408906SEric.Saxe@Sun.COM if ((mode = LINEARG(2)) == NULL) 1418906SEric.Saxe@Sun.COM mode = "\0"; 1428906SEric.Saxe@Sun.COM 1438906SEric.Saxe@Sun.COM for (bp = bmlist; bp->cmd; bp++) { 1448906SEric.Saxe@Sun.COM if (strcmp(behavior, bp->behavior) == 0 && 1458906SEric.Saxe@Sun.COM strcmp(mode, bp->mode) == 0) { 1468906SEric.Saxe@Sun.COM break; 1478906SEric.Saxe@Sun.COM } 1488906SEric.Saxe@Sun.COM } 1498906SEric.Saxe@Sun.COM if (bp->cmd == 0) { 1508906SEric.Saxe@Sun.COM if (LINEARG(2) == NULL) { 1518906SEric.Saxe@Sun.COM mesg(MERR, "invalid cpupm behavior \"%s\"\n", behavior); 1528906SEric.Saxe@Sun.COM } else { 1538906SEric.Saxe@Sun.COM mesg(MERR, "invalid cpupm behavior \"%s %s\"\n", 1548906SEric.Saxe@Sun.COM behavior, mode); 1558906SEric.Saxe@Sun.COM } 1568906SEric.Saxe@Sun.COM return (NOUP); 1578906SEric.Saxe@Sun.COM } 1588906SEric.Saxe@Sun.COM if (ioctl(pm_fd, bp->cmd, NULL) == -1 && errno != bp->Errno) { 1598906SEric.Saxe@Sun.COM mesg(MERR, "cpupm %s failed, %s\n", 1608906SEric.Saxe@Sun.COM behavior, strerror(errno)); 1618906SEric.Saxe@Sun.COM return (NOUP); 1628906SEric.Saxe@Sun.COM } 1638906SEric.Saxe@Sun.COM return (OKUP); 1648906SEric.Saxe@Sun.COM } 1658906SEric.Saxe@Sun.COM 1668906SEric.Saxe@Sun.COM /* 1678906SEric.Saxe@Sun.COM * Check for valid cpu_deep_idle option and communicate it to the kernel. 1688906SEric.Saxe@Sun.COM */ 1698906SEric.Saxe@Sun.COM int 1708906SEric.Saxe@Sun.COM cpuidle(void) 1718906SEric.Saxe@Sun.COM { 1723028Smh27603 struct btoc { 1733028Smh27603 char *behavior; 1743028Smh27603 int cmd; 1753028Smh27603 int Errno; 1763028Smh27603 }; 1773028Smh27603 static struct btoc blist[] = { 1788906SEric.Saxe@Sun.COM "disable", PM_DISABLE_CPU_DEEP_IDLE, EINVAL, 1798906SEric.Saxe@Sun.COM "enable", PM_ENABLE_CPU_DEEP_IDLE, EBUSY, 1808906SEric.Saxe@Sun.COM "default", PM_DEFAULT_CPU_DEEP_IDLE, EBUSY, 1813028Smh27603 NULL, 0, 0 1823028Smh27603 }; 1833028Smh27603 struct btoc *bp; 1843028Smh27603 char *behavior; 1853028Smh27603 1863028Smh27603 for (behavior = LINEARG(1), bp = blist; bp->cmd; bp++) { 1873028Smh27603 if (strcmp(behavior, bp->behavior) == 0) 1883028Smh27603 break; 1893028Smh27603 } 1903028Smh27603 if (bp->cmd == 0) { 1918906SEric.Saxe@Sun.COM mesg(MERR, "invalid cpu_deep_idle behavior \"%s\"\n", behavior); 1923028Smh27603 return (NOUP); 1933028Smh27603 } 1943028Smh27603 if (ioctl(pm_fd, bp->cmd, NULL) == -1 && errno != bp->Errno) { 1958906SEric.Saxe@Sun.COM mesg(MERR, "cpu_deep_idle %s failed, %s\n", 1963028Smh27603 behavior, strerror(errno)); 1973028Smh27603 return (NOUP); 1983028Smh27603 } 1993028Smh27603 return (OKUP); 2003028Smh27603 } 2013028Smh27603 2023028Smh27603 /* 2035295Srandyf * Two decisions are identical except for the list names and ioctl commands 2045295Srandyf * inputs: whitelist, blacklist, yes, no 2055295Srandyf * if (! ("S3" kstat exists)) 2065295Srandyf * return (no) 2075295Srandyf * if (SystemInformation.Manufacturer == "Sun Microsystems" && 2085295Srandyf * (Pref_PM_Profile == Workstation || Pref_PM_Profile == Desktop)) { 2095295Srandyf * if (platform on blacklist) 2105295Srandyf * return (no) 2115295Srandyf * return (yes) 2125295Srandyf * } else { 2135295Srandyf * if (platform on whitelist) 2145295Srandyf * return (yes) 2155295Srandyf * return (no) 2165295Srandyf * } 2175295Srandyf */ 2185295Srandyf 2195295Srandyf int 2205295Srandyf S3_helper(char *whitelist, char *blacklist, int yes, int no, char *keyword, 2215295Srandyf char *behavior, int *didyes, int suppress) 2225295Srandyf { 2235295Srandyf int oflags = SMB_O_NOCKSUM | SMB_O_NOVERS; 2245295Srandyf smbios_hdl_t *shp; 2255295Srandyf smbios_system_t sys; 2265295Srandyf id_t id; 2275295Srandyf int ret; 2285295Srandyf kstat_ctl_t *kc; 2295295Srandyf kstat_t *ksp; 2305295Srandyf kstat_named_t *dp; 2315295Srandyf smbios_info_t info; 2325295Srandyf int preferred_pm_profile = 0; 2335295Srandyf char yesstr[32], nostr[32]; /* DEBUG */ 2345295Srandyf 2355295Srandyf *didyes = 0; 2365295Srandyf 2375295Srandyf strncpy(yesstr, pm_map(yes), sizeof (yesstr)); 2385295Srandyf strncpy(nostr, pm_map(no), sizeof (nostr)); 2395295Srandyf mesg(MDEBUG, "S3_helper(%s, %s, %s, %s, %s, %s)\n", whitelist, 2405295Srandyf blacklist, yesstr, nostr, keyword, behavior); 2415295Srandyf if ((kc = kstat_open()) == NULL) { 2425295Srandyf mesg(MDEBUG, "kstat_open failed\n"); 2435295Srandyf return (OKUP); 2445295Srandyf } 2455295Srandyf ksp = kstat_lookup(kc, "acpi", -1, "acpi"); 2465295Srandyf if (ksp == NULL) { 2475295Srandyf mesg(MDEBUG, "kstat_lookup 'acpi', -1, 'acpi' failed\n"); 2485295Srandyf kstat_close(kc); 2495295Srandyf return (OKUP); 2505295Srandyf } 2515295Srandyf (void) kstat_read(kc, ksp, NULL); 2525295Srandyf dp = kstat_data_lookup(ksp, "S3"); 2535295Srandyf if (dp == NULL || dp->value.l == 0) { 2545295Srandyf mesg(MDEBUG, "kstat_data_lookup 'S3' fails\n"); 2555295Srandyf if (dp != NULL) 2565295Srandyf mesg(MDEBUG, "value.l %lx\n", dp->value.l); 2575295Srandyf kstat_close(kc); 2585295Srandyf return (do_ioctl(no, keyword, behavior, suppress)); 2595295Srandyf } 2605295Srandyf mesg(MDEBUG, "kstat indicates S3 support (%lx)\n", dp->value.l); 2615295Srandyf 2625295Srandyf if (!whitelist_only) { 2635295Srandyf /* 2645295Srandyf * We still have an ACPI ksp, search it again for 2655295Srandyf * 'preferred_pm_profile' (needs to be valid if we don't 2665295Srandyf * aren't only using a whitelist). 2675295Srandyf */ 2685295Srandyf dp = kstat_data_lookup(ksp, "preferred_pm_profile"); 2695295Srandyf if (dp == NULL) { 2705295Srandyf mesg(MDEBUG, "kstat_data_lookup 'ppmp fails\n"); 2715295Srandyf kstat_close(kc); 2725295Srandyf return (do_ioctl(no, keyword, behavior, suppress)); 2735295Srandyf } 2746171Smh27603 mesg(MDEBUG, "kstat indicates preferred_pm_profile is %lx\n", 2755295Srandyf dp->value.l); 2765295Srandyf preferred_pm_profile = dp->value.l; 2775295Srandyf } 2785295Srandyf kstat_close(kc); 2795295Srandyf 2805295Srandyf if ((shp = smbios_open(NULL, 2815295Srandyf SMB_VERSION, oflags, &ret)) == NULL) { 2825295Srandyf /* we promised not to complain */ 2835295Srandyf /* we bail leaving it to the kernel default */ 2845295Srandyf mesg(MDEBUG, "smbios_open failed %d\n", errno); 2855295Srandyf return (OKUP); 2865295Srandyf } 2875295Srandyf if ((id = smbios_info_system(shp, &sys)) == SMB_ERR) { 2885295Srandyf mesg(MDEBUG, "smbios_info_system failed %d\n", errno); 2895295Srandyf smbios_close(shp); 2905295Srandyf return (OKUP); 2915295Srandyf } 2925295Srandyf if (smbios_info_common(shp, id, &info) == SMB_ERR) { 2935295Srandyf mesg(MDEBUG, "smbios_info_common failed %d\n", errno); 2945295Srandyf smbios_close(shp); 2955295Srandyf return (OKUP); 2965295Srandyf } 2975295Srandyf mesg(MDEBUG, "Manufacturer: %s\n", info.smbi_manufacturer); 2985295Srandyf mesg(MDEBUG, "Product: %s\n", info.smbi_product); 2995295Srandyf smbios_close(shp); 3005295Srandyf 3015295Srandyf if (!whitelist_only) { 3025295Srandyf #define PPP_DESKTOP 1 3035295Srandyf #define PPP_WORKSTATION 3 3045295Srandyf if (strcmp(info.smbi_manufacturer, "Sun Microsystems") == 0 && 3055295Srandyf (preferred_pm_profile == PPP_DESKTOP || 3065295Srandyf preferred_pm_profile == PPP_WORKSTATION)) { 3075295Srandyf if (isonlist(blacklist, 3085295Srandyf info.smbi_manufacturer, info.smbi_product)) { 3095295Srandyf return (do_ioctl(no, keyword, behavior, 3105295Srandyf suppress)); 3115295Srandyf } else { 3125295Srandyf ret = do_ioctl(yes, keyword, behavior, 3135295Srandyf suppress); 3145295Srandyf *didyes = (ret == OKUP); 3155295Srandyf return (ret); 3165295Srandyf } 3175295Srandyf } 3185295Srandyf } 3195295Srandyf if (isonlist(whitelist, 3205295Srandyf info.smbi_manufacturer, info.smbi_product)) { 3215295Srandyf ret = do_ioctl(yes, keyword, behavior, suppress); 3225295Srandyf *didyes = (ret == OKUP); 3235295Srandyf return (ret); 3245295Srandyf } else { 3255295Srandyf return (do_ioctl(no, keyword, behavior, suppress)); 3265295Srandyf } 3275295Srandyf } 3285295Srandyf 3295295Srandyf int 3305295Srandyf S3sup(void) /* S3-support keyword handler */ 3315295Srandyf { 3325295Srandyf struct btoc { 3335295Srandyf char *behavior; 3345295Srandyf int cmd; 3355295Srandyf }; 3365295Srandyf static struct btoc blist[] = { 3375295Srandyf "default", PM_DEFAULT_ALGORITHM, 3385295Srandyf "enable", PM_ENABLE_S3, 3395295Srandyf "disable", PM_DISABLE_S3, 3405295Srandyf NULL, 0 3415295Srandyf }; 3425295Srandyf struct btoc *bp; 3435295Srandyf char *behavior; 3445295Srandyf int dontcare; 3455295Srandyf 3465295Srandyf for (behavior = LINEARG(1), bp = blist; bp->cmd; bp++) { 3475295Srandyf if (strcmp(behavior, bp->behavior) == 0) 3485295Srandyf break; 3495295Srandyf } 3505295Srandyf if (bp->cmd == 0) { 3515295Srandyf mesg(MERR, "invalid S3-support behavior \"%s\"\n", behavior); 3525295Srandyf return (NOUP); 3535295Srandyf } 3545295Srandyf 3555295Srandyf 3565295Srandyf switch (bp->cmd) { 3575295Srandyf 3585295Srandyf case PM_ENABLE_S3: 3595295Srandyf case PM_DISABLE_S3: 3605295Srandyf return (do_ioctl(bp->cmd, "S3-support", behavior, EBUSY)); 3615295Srandyf 3625295Srandyf case PM_DEFAULT_ALGORITHM: 3635295Srandyf /* 3645295Srandyf * we suppress errors in the "default" case because we 3655295Srandyf * already did an invisible default call, so we know we'll 3665295Srandyf * get EBUSY 3675295Srandyf */ 3685295Srandyf return (S3_helper("S3-support-enable", "S3-support-disable", 3695295Srandyf PM_ENABLE_S3, PM_DISABLE_S3, "S3-support", behavior, 3705295Srandyf &dontcare, EBUSY)); 3715295Srandyf 3725295Srandyf default: 3735295Srandyf mesg(MERR, "S3-support %s failed, %s\n", behavior, 3745295Srandyf strerror(errno)); 3755295Srandyf return (NOUP); 3765295Srandyf } 3775295Srandyf } 3785295Srandyf 3795295Srandyf /* 3805295Srandyf * Check for valid autoS3 behavior and save after ioctl success. 3815295Srandyf */ 3825295Srandyf int 3835295Srandyf autoS3(void) 3845295Srandyf { 3855295Srandyf struct btoc { 3865295Srandyf char *behavior; 3875295Srandyf int cmd; 3885295Srandyf }; 3895295Srandyf static struct btoc blist[] = { 3905295Srandyf "default", PM_DEFAULT_ALGORITHM, 3915295Srandyf "disable", PM_STOP_AUTOS3, 3925295Srandyf "enable", PM_START_AUTOS3, 3935295Srandyf NULL, 0 3945295Srandyf }; 3955295Srandyf struct btoc *bp; 3965295Srandyf char *behavior; 3975295Srandyf int dontcare; 3985295Srandyf 3995295Srandyf for (behavior = LINEARG(1), bp = blist; bp->cmd; bp++) { 4005295Srandyf if (strcmp(behavior, bp->behavior) == 0) 4015295Srandyf break; 4025295Srandyf } 4035295Srandyf if (bp->cmd == 0) { 4045295Srandyf mesg(MERR, "invalid autoS3 behavior \"%s\"\n", behavior); 4055295Srandyf return (NOUP); 4065295Srandyf } 4075295Srandyf 4085295Srandyf switch (bp->cmd) { 4095295Srandyf default: 4105295Srandyf mesg(MERR, "autoS3 %s failed, %s\n", 4115295Srandyf behavior, strerror(errno)); 4125295Srandyf mesg(MDEBUG, "unknown command\n", bp->cmd); 4135295Srandyf return (OKUP); 4145295Srandyf 4155295Srandyf case PM_STOP_AUTOS3: 4165295Srandyf case PM_START_AUTOS3: 4175295Srandyf return (do_ioctl(bp->cmd, "autoS3", behavior, EBUSY)); 4185295Srandyf 4195295Srandyf case PM_DEFAULT_ALGORITHM: 4205295Srandyf return (S3_helper("S3-autoenable", "S3-autodisable", 4215295Srandyf PM_START_AUTOS3, PM_STOP_AUTOS3, "autoS3", behavior, 4225295Srandyf &dontcare, EBUSY)); 4235295Srandyf } 4245295Srandyf } 4255295Srandyf 4265295Srandyf 4275295Srandyf /* 4280Sstevel@tonic-gate * Check for valid autopm behavior and save after ioctl success. 4290Sstevel@tonic-gate */ 4300Sstevel@tonic-gate int 4310Sstevel@tonic-gate autopm(void) 4320Sstevel@tonic-gate { 4330Sstevel@tonic-gate struct btoc { 4340Sstevel@tonic-gate char *behavior; 4350Sstevel@tonic-gate int cmd, Errno, isdef; 4360Sstevel@tonic-gate }; 4370Sstevel@tonic-gate static struct btoc blist[] = { 4385295Srandyf "default", PM_START_PM, -1, 1, 4390Sstevel@tonic-gate "disable", PM_STOP_PM, EINVAL, 0, 4400Sstevel@tonic-gate "enable", PM_START_PM, EBUSY, 0, 4410Sstevel@tonic-gate NULL, 0, 0, 0, 4420Sstevel@tonic-gate }; 4430Sstevel@tonic-gate struct btoc *bp; 4440Sstevel@tonic-gate char *behavior; 4450Sstevel@tonic-gate 4460Sstevel@tonic-gate for (behavior = LINEARG(1), bp = blist; bp->cmd; bp++) { 4470Sstevel@tonic-gate if (strcmp(behavior, bp->behavior) == 0) 4480Sstevel@tonic-gate break; 4490Sstevel@tonic-gate } 4500Sstevel@tonic-gate if (bp->cmd == 0) { 4510Sstevel@tonic-gate mesg(MERR, "invalid autopm behavior \"%s\"\n", behavior); 4520Sstevel@tonic-gate return (NOUP); 4530Sstevel@tonic-gate } 4540Sstevel@tonic-gate 4550Sstevel@tonic-gate /* 4560Sstevel@tonic-gate * for "default" behavior, do not enable autopm if not ESTAR_V3 4570Sstevel@tonic-gate */ 4585295Srandyf #if defined(__sparc) 4590Sstevel@tonic-gate if (!bp->isdef || (estar_vers == ESTAR_V3)) { 4600Sstevel@tonic-gate if (ioctl(pm_fd, bp->cmd, NULL) == -1 && errno != bp->Errno) { 4610Sstevel@tonic-gate mesg(MERR, "autopm %s failed, %s\n", 4620Sstevel@tonic-gate behavior, strerror(errno)); 4630Sstevel@tonic-gate return (NOUP); 4640Sstevel@tonic-gate } 4650Sstevel@tonic-gate } 4660Sstevel@tonic-gate (void) strcpy(new_cc.apm_behavior, behavior); 4670Sstevel@tonic-gate return (OKUP); 4685295Srandyf #endif 4695295Srandyf #if defined(__x86) 4705295Srandyf if (!bp->isdef) { 4715295Srandyf if (ioctl(pm_fd, bp->cmd, NULL) == -1 && errno != bp->Errno) { 4725295Srandyf mesg(MERR, "autopm %s failed, %s\n", 4735295Srandyf behavior, strerror(errno)); 4745295Srandyf return (NOUP); 4755295Srandyf } 4765295Srandyf mesg(MDEBUG, "autopm %s succeeded\n", behavior); 4775295Srandyf 4785295Srandyf return (OKUP); 4795295Srandyf } else { 4805295Srandyf int didenable; 4815295Srandyf int ret = S3_helper("autopm-enable", "autopm-disable", 4825295Srandyf PM_START_PM, PM_STOP_PM, "autopm", behavior, &didenable, 4835295Srandyf bp->Errno); 4845295Srandyf if (didenable) { 4855295Srandyf /* tell powerd to attach all devices */ 4865295Srandyf new_cc.is_autopm_default = 1; 4875295Srandyf (void) strcpy(new_cc.apm_behavior, behavior); 4885295Srandyf } 4895295Srandyf return (ret); 4905295Srandyf } 4915295Srandyf #endif 4920Sstevel@tonic-gate } 4930Sstevel@tonic-gate 4940Sstevel@tonic-gate 4950Sstevel@tonic-gate static int 4960Sstevel@tonic-gate gethm(char *src, int *hour, int *min) 4970Sstevel@tonic-gate { 4980Sstevel@tonic-gate if (sscanf(src, "%d:%d", hour, min) != 2) { 4990Sstevel@tonic-gate mesg(MERR, "bad time format (%s)\n", src); 5000Sstevel@tonic-gate return (-1); 5010Sstevel@tonic-gate } 5020Sstevel@tonic-gate return (0); 5030Sstevel@tonic-gate } 5040Sstevel@tonic-gate 5050Sstevel@tonic-gate 5060Sstevel@tonic-gate static void 5070Sstevel@tonic-gate strcpy_limit(char *dst, char *src, size_t limit, char *info) 5080Sstevel@tonic-gate { 5090Sstevel@tonic-gate if (strlcpy(dst, src, limit) >= limit) 5100Sstevel@tonic-gate mesg(MEXIT, "%s is too long (%s)\n", info, src); 5110Sstevel@tonic-gate } 5120Sstevel@tonic-gate 5130Sstevel@tonic-gate 5140Sstevel@tonic-gate /* 5150Sstevel@tonic-gate * Convert autoshutdown idle and start/finish times; 5160Sstevel@tonic-gate * check and record autoshutdown behavior. 5170Sstevel@tonic-gate */ 5180Sstevel@tonic-gate int 5190Sstevel@tonic-gate autosd(void) 5200Sstevel@tonic-gate { 521763Smh27603 char **bp, *behavior; 522763Smh27603 char *unrec = gettext("unrecognized autoshutdown behavior"); 5230Sstevel@tonic-gate static char *blist[] = { 5240Sstevel@tonic-gate "autowakeup", "default", "noshutdown", 5250Sstevel@tonic-gate "shutdown", "unconfigured", NULL 5260Sstevel@tonic-gate }; 5270Sstevel@tonic-gate 5280Sstevel@tonic-gate new_cc.as_idle = atoi(LINEARG(1)); 5290Sstevel@tonic-gate if (gethm(LINEARG(2), &new_cc.as_sh, &new_cc.as_sm) || 5300Sstevel@tonic-gate gethm(LINEARG(3), &new_cc.as_fh, &new_cc.as_fm)) 5310Sstevel@tonic-gate return (NOUP); 532763Smh27603 mesg(MDEBUG, "idle %d, start %d:%02d, finish %d:%02d\n", 5330Sstevel@tonic-gate new_cc.as_idle, new_cc.as_sh, new_cc.as_sm, 5340Sstevel@tonic-gate new_cc.as_fh, new_cc.as_fm); 5350Sstevel@tonic-gate 5360Sstevel@tonic-gate for (behavior = LINEARG(4), bp = blist; *bp; bp++) { 5370Sstevel@tonic-gate if (strcmp(behavior, *bp) == 0) 5380Sstevel@tonic-gate break; 5390Sstevel@tonic-gate } 5400Sstevel@tonic-gate if (*bp == NULL) { 5410Sstevel@tonic-gate mesg(MERR, "%s: \"%s\"\n", unrec, behavior); 5420Sstevel@tonic-gate return (NOUP); 5430Sstevel@tonic-gate } 5440Sstevel@tonic-gate STRCPYLIM(new_cc.as_behavior, *bp, unrec); 5450Sstevel@tonic-gate return (OKUP); 5460Sstevel@tonic-gate } 5470Sstevel@tonic-gate 5480Sstevel@tonic-gate 5490Sstevel@tonic-gate /* 5500Sstevel@tonic-gate * Check for a real device and try to resolve to a full path. 5510Sstevel@tonic-gate * The orig/resolved path may be modified into a prom pathname, 5520Sstevel@tonic-gate * and an allocated copy of the result is stored at *destp; 5530Sstevel@tonic-gate * the caller will need to free that space. Returns 1 for any 5540Sstevel@tonic-gate * error, otherwise 0; also sets *errp after an alloc error. 5550Sstevel@tonic-gate */ 5560Sstevel@tonic-gate static int 5570Sstevel@tonic-gate devpath(char **destp, char *src, int *errp) 5580Sstevel@tonic-gate { 5590Sstevel@tonic-gate struct stat stbuf; 5600Sstevel@tonic-gate char buf[PATH_MAX]; 5610Sstevel@tonic-gate char *cp, *dstr; 5620Sstevel@tonic-gate int devok, dcs = 0; 5630Sstevel@tonic-gate size_t len; 5640Sstevel@tonic-gate 5650Sstevel@tonic-gate /* 5660Sstevel@tonic-gate * When there's a real device, try to resolve the path 5670Sstevel@tonic-gate * and trim the leading "/devices" component. 5680Sstevel@tonic-gate */ 5690Sstevel@tonic-gate if ((devok = (stat(src, &stbuf) == 0 && stbuf.st_rdev)) != 0) { 5700Sstevel@tonic-gate if (realpath(src, buf) == NULL) { 5710Sstevel@tonic-gate mesg(MERR, "realpath cannot resolve \"%s\"\n", 5720Sstevel@tonic-gate src, strerror(errno)); 5730Sstevel@tonic-gate return (1); 5740Sstevel@tonic-gate } 5750Sstevel@tonic-gate src = buf; 5760Sstevel@tonic-gate dstr = "/devices"; 5770Sstevel@tonic-gate len = strlen(dstr); 5780Sstevel@tonic-gate dcs = (strncmp(src, dstr, len) == 0); 5790Sstevel@tonic-gate if (dcs) 5800Sstevel@tonic-gate src += len; 5810Sstevel@tonic-gate } else 5820Sstevel@tonic-gate mesg(MDEBUG, stat_fmt, src, strerror(errno)); 5830Sstevel@tonic-gate 5840Sstevel@tonic-gate /* 5850Sstevel@tonic-gate * When the path has ":anything", display an error for 5860Sstevel@tonic-gate * a non-device or truncate a resolved+modifed path. 5870Sstevel@tonic-gate */ 5880Sstevel@tonic-gate if (cp = strchr(src, ':')) { 5890Sstevel@tonic-gate if (devok == 0) { 5900Sstevel@tonic-gate mesg(MERR, "physical path may not contain " 5910Sstevel@tonic-gate "a minor string (%s)\n", src); 5920Sstevel@tonic-gate return (1); 5930Sstevel@tonic-gate } else if (dcs) 5940Sstevel@tonic-gate *cp = '\0'; 5950Sstevel@tonic-gate } 5960Sstevel@tonic-gate 5970Sstevel@tonic-gate if ((*destp = strdup(src)) == NULL) { 5980Sstevel@tonic-gate *errp = NOUP; 5990Sstevel@tonic-gate mesg(MERR, alloc_fmt, src, strerror(errno)); 6000Sstevel@tonic-gate } 6010Sstevel@tonic-gate return (*destp == NULL); 6020Sstevel@tonic-gate } 6030Sstevel@tonic-gate 6040Sstevel@tonic-gate 6050Sstevel@tonic-gate /* 6060Sstevel@tonic-gate * Call pm ioctl request(s) to set property/device dependencies. 6070Sstevel@tonic-gate */ 6080Sstevel@tonic-gate static int 6090Sstevel@tonic-gate dev_dep_common(int isprop) 6100Sstevel@tonic-gate { 6110Sstevel@tonic-gate int cmd, argn, upval = OKUP; 6120Sstevel@tonic-gate char *src, *first, **destp; 6130Sstevel@tonic-gate pm_req_t pmreq; 6140Sstevel@tonic-gate 6150Sstevel@tonic-gate bzero(&pmreq, sizeof (pmreq)); 6160Sstevel@tonic-gate src = LINEARG(1); 6170Sstevel@tonic-gate if (isprop) { 6180Sstevel@tonic-gate cmd = PM_ADD_DEPENDENT_PROPERTY; 6190Sstevel@tonic-gate first = NULL; 6200Sstevel@tonic-gate pmreq.pmreq_kept = src; 6210Sstevel@tonic-gate } else { 6220Sstevel@tonic-gate cmd = PM_ADD_DEPENDENT; 6230Sstevel@tonic-gate if (devpath(&first, src, &upval)) 6240Sstevel@tonic-gate return (upval); 6250Sstevel@tonic-gate pmreq.pmreq_kept = first; 6260Sstevel@tonic-gate } 6270Sstevel@tonic-gate destp = &pmreq.pmreq_keeper; 6280Sstevel@tonic-gate 6290Sstevel@tonic-gate /* 6300Sstevel@tonic-gate * Now loop through any dependents. 6310Sstevel@tonic-gate */ 6320Sstevel@tonic-gate for (argn = 2; (src = LINEARG(argn)) != NULL; argn++) { 6330Sstevel@tonic-gate if (devpath(destp, src, &upval)) { 6340Sstevel@tonic-gate if (upval != OKUP) 6350Sstevel@tonic-gate return (upval); 6360Sstevel@tonic-gate break; 6370Sstevel@tonic-gate } 6380Sstevel@tonic-gate if ((upval = ioctl(pm_fd, cmd, &pmreq)) == -1) { 6390Sstevel@tonic-gate mesg(MDEBUG, "pm ioctl, cmd %d, errno %d\n" 6400Sstevel@tonic-gate "kept \"%s\", keeper \"%s\"\n", 6410Sstevel@tonic-gate cmd, errno, pmreq.pmreq_kept, pmreq.pmreq_keeper); 6420Sstevel@tonic-gate mesg(MERR, "cannot set \"%s\" dependency " 6430Sstevel@tonic-gate "for \"%s\", %s\n", pmreq.pmreq_keeper, 6440Sstevel@tonic-gate pmreq.pmreq_kept, strerror(errno)); 6450Sstevel@tonic-gate } 6460Sstevel@tonic-gate free(*destp); 6470Sstevel@tonic-gate *destp = NULL; 6480Sstevel@tonic-gate if (upval != OKUP) 6490Sstevel@tonic-gate break; 6500Sstevel@tonic-gate } 6510Sstevel@tonic-gate 6520Sstevel@tonic-gate free(first); 6530Sstevel@tonic-gate return (upval); 6540Sstevel@tonic-gate } 6550Sstevel@tonic-gate 6560Sstevel@tonic-gate 6570Sstevel@tonic-gate int 6580Sstevel@tonic-gate ddprop(void) 6590Sstevel@tonic-gate { 6600Sstevel@tonic-gate return (dev_dep_common(1)); 6610Sstevel@tonic-gate } 6620Sstevel@tonic-gate 6630Sstevel@tonic-gate 6640Sstevel@tonic-gate int 6650Sstevel@tonic-gate devdep(void) 6660Sstevel@tonic-gate { 6670Sstevel@tonic-gate return (dev_dep_common(0)); 6680Sstevel@tonic-gate } 6690Sstevel@tonic-gate 6700Sstevel@tonic-gate 6710Sstevel@tonic-gate /* 6720Sstevel@tonic-gate * Convert a numeric string (with a possible trailing scaling byte) 6730Sstevel@tonic-gate * into an integer. Returns a converted value and *nerrp unchanged, 6740Sstevel@tonic-gate * or 0 with *nerrp set to 1 for a conversion error. 6750Sstevel@tonic-gate */ 6760Sstevel@tonic-gate static int 6770Sstevel@tonic-gate get_scaled_value(char *str, int *nerrp) 6780Sstevel@tonic-gate { 6790Sstevel@tonic-gate longlong_t svalue = 0, factor = 1; 6800Sstevel@tonic-gate char *sp; 6810Sstevel@tonic-gate 6820Sstevel@tonic-gate errno = 0; 6830Sstevel@tonic-gate svalue = strtol(str, &sp, 0); 6840Sstevel@tonic-gate if (errno || (*str != '-' && (*str < '0' || *str > '9'))) 6850Sstevel@tonic-gate *nerrp = 1; 6860Sstevel@tonic-gate else if (sp && *sp != '\0') { 6870Sstevel@tonic-gate if (*sp == 'h') 6880Sstevel@tonic-gate factor = 3600; 6890Sstevel@tonic-gate else if (*sp == 'm') 6900Sstevel@tonic-gate factor = 60; 6910Sstevel@tonic-gate else if (*sp != 's') 6920Sstevel@tonic-gate *nerrp = 1; 6930Sstevel@tonic-gate } 6940Sstevel@tonic-gate /* any bytes following sp are ignored */ 6950Sstevel@tonic-gate 6960Sstevel@tonic-gate if (*nerrp == 0) { 6970Sstevel@tonic-gate svalue *= factor; 6980Sstevel@tonic-gate if (svalue < INT_MIN || svalue > INT_MAX) 6990Sstevel@tonic-gate *nerrp = 1; 7000Sstevel@tonic-gate } 7010Sstevel@tonic-gate if (*nerrp) 7020Sstevel@tonic-gate mesg(MERR, nerr_fmt, str); 7030Sstevel@tonic-gate mesg(MDEBUG, "got scaled value %d\n", (int)svalue); 7040Sstevel@tonic-gate return ((int)svalue); 7050Sstevel@tonic-gate } 7060Sstevel@tonic-gate 7070Sstevel@tonic-gate 7080Sstevel@tonic-gate /* 7090Sstevel@tonic-gate * Increment the count of threshold values, 7100Sstevel@tonic-gate * reallocate *vlistp and append another element. 7110Sstevel@tonic-gate * Returns 1 on error, otherwise 0. 7120Sstevel@tonic-gate */ 7130Sstevel@tonic-gate static int 7140Sstevel@tonic-gate vlist_append(int **vlistp, int *vcntp, int value) 7150Sstevel@tonic-gate { 7160Sstevel@tonic-gate (*vcntp)++; 7170Sstevel@tonic-gate if (*vlistp = realloc(*vlistp, *vcntp * sizeof (**vlistp))) 7180Sstevel@tonic-gate *(*vlistp + *vcntp - 1) = value; 7190Sstevel@tonic-gate else 7200Sstevel@tonic-gate mesg(MERR, alloc_fmt, "threshold list", strerror(errno)); 7210Sstevel@tonic-gate return (*vlistp == NULL); 7220Sstevel@tonic-gate } 7230Sstevel@tonic-gate 7240Sstevel@tonic-gate 7250Sstevel@tonic-gate /* 7260Sstevel@tonic-gate * Convert a single threshold string or paren groups of thresh's as 7270Sstevel@tonic-gate * described below. All thresh's are saved to an allocated list at 7280Sstevel@tonic-gate * *vlistp; the caller will need to free that space. On return: 7290Sstevel@tonic-gate * *vcntp is the count of the vlist array, and vlist is either 7300Sstevel@tonic-gate * a single thresh or N groups of thresh's with a trailing zero: 7310Sstevel@tonic-gate * (cnt_1 thr_1a thr_1b [...]) ... (cnt_N thr_Na thr_Nb [...]) 0. 7320Sstevel@tonic-gate * Returns 0 when all conversions were OK, and 1 for any syntax, 7330Sstevel@tonic-gate * conversion, or alloc error. 7340Sstevel@tonic-gate */ 7350Sstevel@tonic-gate static int 7360Sstevel@tonic-gate get_thresh(int **vlistp, int *vcntp) 7370Sstevel@tonic-gate { 7380Sstevel@tonic-gate int argn, value, gci, grp_cnt = 0, paren = 0, nerr = 0; 7390Sstevel@tonic-gate char *rp, *src; 7400Sstevel@tonic-gate 7410Sstevel@tonic-gate for (argn = 2; (src = LINEARG(argn)) != NULL; argn++) { 7420Sstevel@tonic-gate if (*src == LPAREN) { 7430Sstevel@tonic-gate gci = *vcntp; 7440Sstevel@tonic-gate if (nerr = vlist_append(vlistp, vcntp, 0)) 7450Sstevel@tonic-gate break; 7460Sstevel@tonic-gate paren = 1; 7470Sstevel@tonic-gate src++; 7480Sstevel@tonic-gate } 7490Sstevel@tonic-gate if (*(rp = LASTBYTE(src)) == RPAREN) { 7500Sstevel@tonic-gate if (paren) { 7510Sstevel@tonic-gate grp_cnt = *vcntp - gci; 7520Sstevel@tonic-gate *(*vlistp + gci) = grp_cnt; 7530Sstevel@tonic-gate paren = 0; 7540Sstevel@tonic-gate *rp = '\0'; 7550Sstevel@tonic-gate } else { 7560Sstevel@tonic-gate nerr = 1; 7570Sstevel@tonic-gate break; 7580Sstevel@tonic-gate } 7590Sstevel@tonic-gate } 7600Sstevel@tonic-gate 7610Sstevel@tonic-gate value = get_scaled_value(src, &nerr); 7620Sstevel@tonic-gate if (nerr || (nerr = vlist_append(vlistp, vcntp, value))) 7630Sstevel@tonic-gate break; 7640Sstevel@tonic-gate } 7650Sstevel@tonic-gate 7660Sstevel@tonic-gate if (nerr == 0 && grp_cnt) 7670Sstevel@tonic-gate nerr = vlist_append(vlistp, vcntp, 0); 7680Sstevel@tonic-gate return (nerr); 7690Sstevel@tonic-gate } 7700Sstevel@tonic-gate 7710Sstevel@tonic-gate 7720Sstevel@tonic-gate /* 7730Sstevel@tonic-gate * Set device thresholds from (3) formats: 7740Sstevel@tonic-gate * path "always-on" 7750Sstevel@tonic-gate * path time-spec: [0-9]+[{h,m,s}] 7760Sstevel@tonic-gate * path (ts1 ts2 ...)+ 7770Sstevel@tonic-gate */ 7780Sstevel@tonic-gate int 7790Sstevel@tonic-gate devthr(void) 7800Sstevel@tonic-gate { 7810Sstevel@tonic-gate int cmd, upval = OKUP, nthresh = 0, *vlist = NULL; 7820Sstevel@tonic-gate pm_req_t pmreq; 7830Sstevel@tonic-gate 7840Sstevel@tonic-gate bzero(&pmreq, sizeof (pmreq)); 7850Sstevel@tonic-gate if (devpath(&pmreq.physpath, LINEARG(1), &upval)) 7860Sstevel@tonic-gate return (upval); 7870Sstevel@tonic-gate 7880Sstevel@tonic-gate if (strcmp(LINEARG(2), always_on) == 0) { 7890Sstevel@tonic-gate cmd = PM_SET_DEVICE_THRESHOLD; 7900Sstevel@tonic-gate pmreq.value = INT_MAX; 7910Sstevel@tonic-gate } else if (get_thresh(&vlist, &nthresh)) { 7920Sstevel@tonic-gate mesg(MERR, bad_thresh_fmt); 7930Sstevel@tonic-gate upval = NOUP; 7940Sstevel@tonic-gate } else if (nthresh == 1) { 7950Sstevel@tonic-gate pmreq.value = *vlist; 7960Sstevel@tonic-gate cmd = PM_SET_DEVICE_THRESHOLD; 7970Sstevel@tonic-gate } else { 7980Sstevel@tonic-gate pmreq.data = vlist; 7990Sstevel@tonic-gate pmreq.datasize = (nthresh * sizeof (*vlist)); 8000Sstevel@tonic-gate cmd = PM_SET_COMPONENT_THRESHOLDS; 8010Sstevel@tonic-gate } 8020Sstevel@tonic-gate 8030Sstevel@tonic-gate if (upval != NOUP && (upval = ioctl(pm_fd, cmd, &pmreq)) == -1) 8040Sstevel@tonic-gate mesg(MERR, set_thresh_fmt, pmreq.physpath, strerror(errno)); 8050Sstevel@tonic-gate 8060Sstevel@tonic-gate free(vlist); 8070Sstevel@tonic-gate free(pmreq.physpath); 8080Sstevel@tonic-gate return (upval); 8090Sstevel@tonic-gate } 8100Sstevel@tonic-gate 8110Sstevel@tonic-gate 8120Sstevel@tonic-gate static int 8130Sstevel@tonic-gate scan_int(char *src, int *dst) 8140Sstevel@tonic-gate { 8150Sstevel@tonic-gate long lval; 8160Sstevel@tonic-gate 8170Sstevel@tonic-gate errno = 0; 8180Sstevel@tonic-gate 8190Sstevel@tonic-gate lval = strtol(LINEARG(1), NULL, 0); 8200Sstevel@tonic-gate if (errno || lval > INT_MAX || lval < 0) { 8210Sstevel@tonic-gate mesg(MERR, nerr_fmt, src); 8220Sstevel@tonic-gate return (NOUP); 8230Sstevel@tonic-gate } 8240Sstevel@tonic-gate 8250Sstevel@tonic-gate *dst = (int)lval; 8260Sstevel@tonic-gate return (OKUP); 8270Sstevel@tonic-gate } 8280Sstevel@tonic-gate 8290Sstevel@tonic-gate static int 8300Sstevel@tonic-gate scan_float(char *src, float *dst) 8310Sstevel@tonic-gate { 8320Sstevel@tonic-gate float fval; 8330Sstevel@tonic-gate 8340Sstevel@tonic-gate errno = 0; 8350Sstevel@tonic-gate 8360Sstevel@tonic-gate fval = strtof(src, NULL); 8370Sstevel@tonic-gate if (errno || fval < 0.0) { 8380Sstevel@tonic-gate mesg(MERR, nerr_fmt, src); 8390Sstevel@tonic-gate return (NOUP); 8400Sstevel@tonic-gate } 8410Sstevel@tonic-gate 8420Sstevel@tonic-gate *dst = fval; 8430Sstevel@tonic-gate return (OKUP); 8440Sstevel@tonic-gate } 8450Sstevel@tonic-gate 8460Sstevel@tonic-gate 8470Sstevel@tonic-gate int 8480Sstevel@tonic-gate dreads(void) 8490Sstevel@tonic-gate { 8500Sstevel@tonic-gate return (scan_int(LINEARG(1), &new_cc.diskreads_thold)); 8510Sstevel@tonic-gate } 8520Sstevel@tonic-gate 8530Sstevel@tonic-gate 8540Sstevel@tonic-gate /* 8550Sstevel@tonic-gate * Set pathname for idlecheck; 8560Sstevel@tonic-gate * an overflowed pathname is treated as a fatal error. 8570Sstevel@tonic-gate */ 8580Sstevel@tonic-gate int 8590Sstevel@tonic-gate idlechk(void) 8600Sstevel@tonic-gate { 8610Sstevel@tonic-gate STRCPYLIM(new_cc.idlecheck_path, LINEARG(1), "idle path"); 8620Sstevel@tonic-gate return (OKUP); 8630Sstevel@tonic-gate } 8640Sstevel@tonic-gate 8650Sstevel@tonic-gate 8660Sstevel@tonic-gate int 8670Sstevel@tonic-gate loadavg(void) 8680Sstevel@tonic-gate { 8690Sstevel@tonic-gate return (scan_float(LINEARG(1), &new_cc.loadaverage_thold)); 8700Sstevel@tonic-gate } 8710Sstevel@tonic-gate 8720Sstevel@tonic-gate 8730Sstevel@tonic-gate int 8740Sstevel@tonic-gate nfsreq(void) 8750Sstevel@tonic-gate { 8760Sstevel@tonic-gate return (scan_int(LINEARG(1), &new_cc.nfsreqs_thold)); 8770Sstevel@tonic-gate } 8780Sstevel@tonic-gate 8790Sstevel@tonic-gate #ifdef sparc 8800Sstevel@tonic-gate static char open_fmt[] = "cannot open \"%s\", %s\n"; 8810Sstevel@tonic-gate 8820Sstevel@tonic-gate /* 8830Sstevel@tonic-gate * Verify the filesystem type for a regular statefile is "ufs" 8840Sstevel@tonic-gate * or verify a block device is not in use as a mounted filesytem. 8850Sstevel@tonic-gate * Returns 1 if any error, otherwise 0. 8860Sstevel@tonic-gate */ 8870Sstevel@tonic-gate static int 8880Sstevel@tonic-gate check_mount(char *sfile, dev_t sfdev, int ufs) 8890Sstevel@tonic-gate { 8900Sstevel@tonic-gate char *src, *err_fmt = NULL, *mnttab = MNTTAB; 8910Sstevel@tonic-gate int rgent, match = 0; 8926423Sgw25295 struct mnttab zroot = { 0 }; 8936423Sgw25295 struct mnttab entry; 8940Sstevel@tonic-gate struct extmnttab ent; 8950Sstevel@tonic-gate FILE *fp; 8960Sstevel@tonic-gate 8970Sstevel@tonic-gate if ((fp = fopen(mnttab, "r")) == NULL) { 8980Sstevel@tonic-gate mesg(MERR, open_fmt, mnttab, strerror(errno)); 8990Sstevel@tonic-gate return (1); 9000Sstevel@tonic-gate } 9010Sstevel@tonic-gate 9026423Sgw25295 if (ufs) { 9036423Sgw25295 zroot.mnt_mountp = "/"; 9046423Sgw25295 zroot.mnt_fstype = "zfs"; 9056423Sgw25295 if (getmntany(fp, &entry, &zroot) == 0) { 9066423Sgw25295 err_fmt = "ufs statefile with zfs root is not" 9076423Sgw25295 " supported\n"; 9086423Sgw25295 mesg(MERR, err_fmt, sfile); 9096423Sgw25295 fclose(fp); 9106423Sgw25295 return (1); 9116423Sgw25295 } 9126423Sgw25295 resetmnttab(fp); 9136423Sgw25295 } 9140Sstevel@tonic-gate /* 9150Sstevel@tonic-gate * Search for a matching dev_t; 9160Sstevel@tonic-gate * ignore non-ufs filesystems for a regular statefile. 9170Sstevel@tonic-gate */ 9180Sstevel@tonic-gate while ((rgent = getextmntent(fp, &ent, sizeof (ent))) != -1) { 9190Sstevel@tonic-gate if (rgent > 0) { 9200Sstevel@tonic-gate mesg(MERR, "error reading \"%s\"\n", mnttab); 9210Sstevel@tonic-gate (void) fclose(fp); 9220Sstevel@tonic-gate return (1); 9230Sstevel@tonic-gate } else if (ufs && strcmp(ent.mnt_fstype, "ufs")) 9240Sstevel@tonic-gate continue; 9250Sstevel@tonic-gate else if (makedev(ent.mnt_major, ent.mnt_minor) == sfdev) { 9260Sstevel@tonic-gate match = 1; 9270Sstevel@tonic-gate break; 9280Sstevel@tonic-gate } 9290Sstevel@tonic-gate } 9300Sstevel@tonic-gate 9310Sstevel@tonic-gate /* 9320Sstevel@tonic-gate * No match is needed for a block device statefile, 9330Sstevel@tonic-gate * a match is needed for a regular statefile. 9340Sstevel@tonic-gate */ 9350Sstevel@tonic-gate if (match == 0) { 9366423Sgw25295 if (new_cc.cf_type != CFT_UFS) 9370Sstevel@tonic-gate STRCPYLIM(new_cc.cf_devfs, sfile, "block statefile"); 9380Sstevel@tonic-gate else 9390Sstevel@tonic-gate err_fmt = "cannot find ufs mount point for \"%s\"\n"; 9400Sstevel@tonic-gate } else if (new_cc.cf_type == CFT_UFS) { 9410Sstevel@tonic-gate STRCPYLIM(new_cc.cf_fs, ent.mnt_mountp, "mnt entry"); 9420Sstevel@tonic-gate STRCPYLIM(new_cc.cf_devfs, ent.mnt_special, "mnt special"); 9430Sstevel@tonic-gate while (*(sfile + 1) == '/') sfile++; 9440Sstevel@tonic-gate src = sfile + strlen(ent.mnt_mountp); 9450Sstevel@tonic-gate while (*src == '/') src++; 9460Sstevel@tonic-gate STRCPYLIM(new_cc.cf_path, src, "statefile path"); 9470Sstevel@tonic-gate } else 9480Sstevel@tonic-gate err_fmt = "statefile device \"%s\" is a mounted filesystem\n"; 9496423Sgw25295 (void) fclose(fp); 9500Sstevel@tonic-gate if (err_fmt) 9510Sstevel@tonic-gate mesg(MERR, err_fmt, sfile); 9520Sstevel@tonic-gate return (err_fmt != NULL); 9530Sstevel@tonic-gate } 9540Sstevel@tonic-gate 9550Sstevel@tonic-gate 9560Sstevel@tonic-gate /* 9570Sstevel@tonic-gate * Convert a Unix device to a prom device and save on success, 9580Sstevel@tonic-gate * log any ioctl/conversion error. 9590Sstevel@tonic-gate */ 9600Sstevel@tonic-gate static int 9616423Sgw25295 utop(char *fs_name, char *prom_name) 9620Sstevel@tonic-gate { 9630Sstevel@tonic-gate union obpbuf { 9640Sstevel@tonic-gate char buf[OBP_MAXPATHLEN + sizeof (uint_t)]; 9650Sstevel@tonic-gate struct openpromio oppio; 9660Sstevel@tonic-gate }; 9670Sstevel@tonic-gate union obpbuf oppbuf; 9680Sstevel@tonic-gate struct openpromio *opp; 9690Sstevel@tonic-gate char *promdev = "/dev/openprom"; 9700Sstevel@tonic-gate int fd, upval; 9710Sstevel@tonic-gate 9720Sstevel@tonic-gate if ((fd = open(promdev, O_RDONLY)) == -1) { 9730Sstevel@tonic-gate mesg(MERR, open_fmt, promdev, strerror(errno)); 9740Sstevel@tonic-gate return (NOUP); 9750Sstevel@tonic-gate } 9760Sstevel@tonic-gate 9770Sstevel@tonic-gate opp = &oppbuf.oppio; 9780Sstevel@tonic-gate opp->oprom_size = OBP_MAXPATHLEN; 9796423Sgw25295 strcpy_limit(opp->oprom_array, fs_name, 9800Sstevel@tonic-gate OBP_MAXPATHLEN, "statefile device"); 9810Sstevel@tonic-gate upval = ioctl(fd, OPROMDEV2PROMNAME, opp); 9820Sstevel@tonic-gate (void) close(fd); 9836423Sgw25295 if (upval == OKUP) { 9846423Sgw25295 strcpy_limit(prom_name, opp->oprom_array, OBP_MAXPATHLEN, 9856423Sgw25295 "prom device"); 9866423Sgw25295 } else { 9870Sstevel@tonic-gate openlog("pmconfig", 0, LOG_DAEMON); 9880Sstevel@tonic-gate syslog(LOG_NOTICE, 9890Sstevel@tonic-gate gettext("cannot convert \"%s\" to prom device"), 9906423Sgw25295 fs_name); 9910Sstevel@tonic-gate closelog(); 9920Sstevel@tonic-gate } 9930Sstevel@tonic-gate 9940Sstevel@tonic-gate return (upval); 9950Sstevel@tonic-gate } 9960Sstevel@tonic-gate 9976423Sgw25295 /* 9986423Sgw25295 * given the path to a zvol, return the cXtYdZ name 9996423Sgw25295 * returns < 0 on error, 0 if it isn't a zvol, > 1 on success 10006423Sgw25295 */ 10016423Sgw25295 static int 10026423Sgw25295 ztop(char *arg, char *diskname) 10036423Sgw25295 { 10046423Sgw25295 zpool_handle_t *zpool_handle; 10056423Sgw25295 nvlist_t *config, *nvroot; 10066423Sgw25295 nvlist_t **child; 10076423Sgw25295 uint_t children; 10086423Sgw25295 libzfs_handle_t *lzfs; 10096423Sgw25295 char *vname; 10106423Sgw25295 char *p; 10116423Sgw25295 char pool_name[MAXPATHLEN]; 10126423Sgw25295 10136423Sgw25295 if (strncmp(arg, "/dev/zvol/dsk/", 14)) { 10146423Sgw25295 return (0); 10156423Sgw25295 } 10166423Sgw25295 arg += 14; 10176423Sgw25295 strncpy(pool_name, arg, MAXPATHLEN); 10186423Sgw25295 if (p = strchr(pool_name, '/')) 10196423Sgw25295 *p = '\0'; 10206423Sgw25295 STRCPYLIM(new_cc.cf_fs, p + 1, "statefile path"); 10216423Sgw25295 10226423Sgw25295 if ((lzfs = libzfs_init()) == NULL) { 10236423Sgw25295 mesg(MERR, "failed to initialize ZFS library\n"); 10246423Sgw25295 return (-1); 10256423Sgw25295 } 10266423Sgw25295 if ((zpool_handle = zpool_open(lzfs, pool_name)) == NULL) { 10276423Sgw25295 mesg(MERR, "couldn't open pool '%s'\n", pool_name); 10286423Sgw25295 libzfs_fini(lzfs); 10296423Sgw25295 return (-1); 10306423Sgw25295 } 10316423Sgw25295 config = zpool_get_config(zpool_handle, NULL); 10326423Sgw25295 if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, 10336423Sgw25295 &nvroot) != 0) { 10346423Sgw25295 zpool_close(zpool_handle); 10356423Sgw25295 libzfs_fini(lzfs); 10366423Sgw25295 return (-1); 10376423Sgw25295 } 10386423Sgw25295 verify(nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN, 10396423Sgw25295 &child, &children) == 0); 10406423Sgw25295 if (children != 1) { 10416423Sgw25295 mesg(MERR, "expected one vdev, got %d\n", children); 10426423Sgw25295 zpool_close(zpool_handle); 10436423Sgw25295 libzfs_fini(lzfs); 10446423Sgw25295 return (-1); 10456423Sgw25295 } 1046*10594SGeorge.Wilson@Sun.COM vname = zpool_vdev_name(lzfs, zpool_handle, child[0], B_FALSE); 10476423Sgw25295 if (vname == NULL) { 10486423Sgw25295 mesg(MERR, "couldn't determine vdev name\n"); 10496423Sgw25295 zpool_close(zpool_handle); 10506423Sgw25295 libzfs_fini(lzfs); 10516423Sgw25295 return (-1); 10526423Sgw25295 } 10536423Sgw25295 strcpy(diskname, "/dev/dsk/"); 10546423Sgw25295 strcat(diskname, vname); 10556423Sgw25295 free(vname); 10566423Sgw25295 zpool_close(zpool_handle); 10576423Sgw25295 libzfs_fini(lzfs); 10586423Sgw25295 return (1); 10596423Sgw25295 } 10606423Sgw25295 10616423Sgw25295 /* 10626423Sgw25295 * returns NULL if the slice is good (e.g. does not start at block 10636423Sgw25295 * zero, or a string describing the error if it doesn't 10646423Sgw25295 */ 10656423Sgw25295 static boolean_t 10666423Sgw25295 is_good_slice(char *sfile, char **err) 10676423Sgw25295 { 10686423Sgw25295 int fd, rc; 10696423Sgw25295 struct vtoc vtoc; 10706423Sgw25295 dk_gpt_t *gpt; 10716423Sgw25295 char rdskname[MAXPATHLEN]; 10726423Sgw25295 char *x, *y; 10736423Sgw25295 10746423Sgw25295 *err = NULL; 10756423Sgw25295 /* convert from dsk to rdsk */ 10766423Sgw25295 STRCPYLIM(rdskname, sfile, "disk name"); 10776423Sgw25295 x = strstr(rdskname, "dsk/"); 10786423Sgw25295 y = strstr(sfile, "dsk/"); 10796423Sgw25295 if (x != NULL) { 10806423Sgw25295 *x++ = 'r'; 10816423Sgw25295 strcpy(x, y); 10826423Sgw25295 } 10836423Sgw25295 10846423Sgw25295 if ((fd = open(rdskname, O_RDONLY)) == -1) { 10856423Sgw25295 *err = "could not open '%s'\n"; 10866423Sgw25295 } else if ((rc = read_vtoc(fd, &vtoc)) >= 0) { 10876423Sgw25295 /* 10886423Sgw25295 * we got a slice number; now check the block 10896423Sgw25295 * number where the slice starts 10906423Sgw25295 */ 10916423Sgw25295 if (vtoc.v_part[rc].p_start < 2) 10926423Sgw25295 *err = "using '%s' would clobber the disk label\n"; 10936423Sgw25295 close(fd); 10946423Sgw25295 return (*err ? B_FALSE : B_TRUE); 10956423Sgw25295 } else if ((rc == VT_ENOTSUP) && 10966423Sgw25295 (efi_alloc_and_read(fd, &gpt)) >= 0) { 10976423Sgw25295 /* EFI slices don't clobber the disk label */ 10986423Sgw25295 free(gpt); 10996423Sgw25295 close(fd); 11006423Sgw25295 return (B_TRUE); 11016423Sgw25295 } else 11026423Sgw25295 *err = "could not read partition table from '%s'\n"; 11036423Sgw25295 return (B_FALSE); 11046423Sgw25295 } 11050Sstevel@tonic-gate 11060Sstevel@tonic-gate /* 11070Sstevel@tonic-gate * Check for a valid statefile pathname, inode and mount status. 11080Sstevel@tonic-gate */ 11090Sstevel@tonic-gate int 11100Sstevel@tonic-gate sfpath(void) 11110Sstevel@tonic-gate { 11120Sstevel@tonic-gate static int statefile; 11130Sstevel@tonic-gate char *err_fmt = NULL; 11140Sstevel@tonic-gate char *sfile, *sp, ch; 11156423Sgw25295 char diskname[256]; 11160Sstevel@tonic-gate struct stat stbuf; 11170Sstevel@tonic-gate int dir = 0; 11180Sstevel@tonic-gate dev_t dev; 11190Sstevel@tonic-gate 11200Sstevel@tonic-gate if (statefile) { 11210Sstevel@tonic-gate mesg(MERR, "ignored redundant statefile entry\n"); 11220Sstevel@tonic-gate return (OKUP); 11230Sstevel@tonic-gate } else if (ua_err) { 11240Sstevel@tonic-gate if (ua_err != ENOTSUP) 11250Sstevel@tonic-gate mesg(MERR, "uadmin(A_FREEZE, A_CHECK, 0): %s\n", 11260Sstevel@tonic-gate strerror(ua_err)); 11270Sstevel@tonic-gate return (NOUP); 11280Sstevel@tonic-gate } 11290Sstevel@tonic-gate 11300Sstevel@tonic-gate /* 11310Sstevel@tonic-gate * Check for an absolute path and trim any trailing '/'. 11320Sstevel@tonic-gate */ 11330Sstevel@tonic-gate sfile = LINEARG(1); 11340Sstevel@tonic-gate if (*sfile != '/') { 11350Sstevel@tonic-gate mesg(MERR, "statefile requires an absolute path\n"); 11360Sstevel@tonic-gate return (NOUP); 11370Sstevel@tonic-gate } 11380Sstevel@tonic-gate for (sp = sfile + strlen(sfile) - 1; sp > sfile && *sp == '/'; sp--) 11390Sstevel@tonic-gate *sp = '\0'; 11400Sstevel@tonic-gate 11410Sstevel@tonic-gate /* 11420Sstevel@tonic-gate * If the statefile doesn't exist, the leading path must be a dir. 11430Sstevel@tonic-gate */ 11440Sstevel@tonic-gate if (stat(sfile, &stbuf) == -1) { 11450Sstevel@tonic-gate if (errno == ENOENT) { 11460Sstevel@tonic-gate dir = 1; 11470Sstevel@tonic-gate if ((sp = strrchr(sfile, '/')) == sfile) 11480Sstevel@tonic-gate sp++; 11490Sstevel@tonic-gate ch = *sp; 11500Sstevel@tonic-gate *sp = '\0'; 11510Sstevel@tonic-gate if (stat(sfile, &stbuf) == -1) 11520Sstevel@tonic-gate err_fmt = stat_fmt; 11530Sstevel@tonic-gate *sp = ch; 11540Sstevel@tonic-gate } else 11550Sstevel@tonic-gate err_fmt = stat_fmt; 11560Sstevel@tonic-gate if (err_fmt) { 11570Sstevel@tonic-gate mesg(MERR, err_fmt, sfile, strerror(errno)); 11580Sstevel@tonic-gate return (NOUP); 11590Sstevel@tonic-gate } 11600Sstevel@tonic-gate } 11610Sstevel@tonic-gate 11620Sstevel@tonic-gate /* 11630Sstevel@tonic-gate * Check for regular/dir/block types, set cf_type and dev. 11640Sstevel@tonic-gate */ 11650Sstevel@tonic-gate if (S_ISREG(stbuf.st_mode) || (dir && S_ISDIR(stbuf.st_mode))) { 11660Sstevel@tonic-gate new_cc.cf_type = CFT_UFS; 11670Sstevel@tonic-gate dev = stbuf.st_dev; 11680Sstevel@tonic-gate } else if (S_ISBLK(stbuf.st_mode)) { 11696423Sgw25295 if (is_good_slice(sfile, &err_fmt)) { 11706423Sgw25295 switch (ztop(sfile, diskname)) { 11716423Sgw25295 case 1: 11726423Sgw25295 new_cc.cf_type = CFT_ZVOL; 11736423Sgw25295 break; 11746423Sgw25295 case 0: 11756423Sgw25295 new_cc.cf_type = CFT_SPEC; 11766423Sgw25295 break; 11776423Sgw25295 case -1: 11786423Sgw25295 default: 11796423Sgw25295 return (NOUP); 11806423Sgw25295 } 11810Sstevel@tonic-gate dev = stbuf.st_rdev; 11826423Sgw25295 } 11830Sstevel@tonic-gate } else 11840Sstevel@tonic-gate err_fmt = "bad file type for \"%s\"\n" 11850Sstevel@tonic-gate "statefile must be a regular file or block device\n"; 11860Sstevel@tonic-gate if (err_fmt) { 11870Sstevel@tonic-gate mesg(MERR, err_fmt, sfile); 11880Sstevel@tonic-gate return (NOUP); 11890Sstevel@tonic-gate } 11906423Sgw25295 if (check_mount(sfile, dev, (new_cc.cf_type == CFT_UFS))) 11910Sstevel@tonic-gate return (NOUP); 11926423Sgw25295 if (new_cc.cf_type == CFT_ZVOL) { 11936423Sgw25295 if (utop(diskname, new_cc.cf_dev_prom)) 11946423Sgw25295 return (NOUP); 11956423Sgw25295 } else if (utop(new_cc.cf_devfs, new_cc.cf_dev_prom)) { 11966423Sgw25295 return (NOUP); 11976423Sgw25295 } 11980Sstevel@tonic-gate new_cc.cf_magic = CPR_CONFIG_MAGIC; 11990Sstevel@tonic-gate statefile = 1; 12000Sstevel@tonic-gate return (OKUP); 12010Sstevel@tonic-gate } 12020Sstevel@tonic-gate #endif /* sparc */ 12030Sstevel@tonic-gate 12040Sstevel@tonic-gate 12050Sstevel@tonic-gate /* 12063028Smh27603 * Common function to set a system or cpu threshold. 12070Sstevel@tonic-gate */ 12083028Smh27603 static int 12093028Smh27603 cmnthr(int req) 12100Sstevel@tonic-gate { 12110Sstevel@tonic-gate int value, nerr = 0, upval = OKUP; 12120Sstevel@tonic-gate char *thresh = LINEARG(1); 12130Sstevel@tonic-gate 12140Sstevel@tonic-gate if (strcmp(thresh, always_on) == 0) 12150Sstevel@tonic-gate value = INT_MAX; 12160Sstevel@tonic-gate else if ((value = get_scaled_value(thresh, &nerr)) < 0 || nerr) { 12170Sstevel@tonic-gate mesg(MERR, "%s must be a positive value\n", LINEARG(0)); 12180Sstevel@tonic-gate upval = NOUP; 12190Sstevel@tonic-gate } 12200Sstevel@tonic-gate if (upval == OKUP) 12213028Smh27603 (void) ioctl(pm_fd, req, value); 12220Sstevel@tonic-gate return (upval); 12230Sstevel@tonic-gate } 12240Sstevel@tonic-gate 12250Sstevel@tonic-gate 12263028Smh27603 /* 12273028Smh27603 * Try setting system threshold. 12283028Smh27603 */ 12293028Smh27603 int 12303028Smh27603 systhr(void) 12313028Smh27603 { 12323028Smh27603 return (cmnthr(PM_SET_SYSTEM_THRESHOLD)); 12333028Smh27603 } 12343028Smh27603 12353028Smh27603 12363028Smh27603 /* 12373028Smh27603 * Try setting cpu threshold. 12383028Smh27603 */ 12393028Smh27603 int 12403028Smh27603 cputhr(void) 12413028Smh27603 { 12423028Smh27603 return (cmnthr(PM_SET_CPU_THRESHOLD)); 12433028Smh27603 } 12443028Smh27603 12453028Smh27603 12460Sstevel@tonic-gate int 12470Sstevel@tonic-gate tchars(void) 12480Sstevel@tonic-gate { 12490Sstevel@tonic-gate return (scan_int(LINEARG(1), &new_cc.ttychars_thold)); 12500Sstevel@tonic-gate } 1251