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