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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include "pmconfig.h" 30 #include <sys/mkdev.h> 31 #include <sys/syslog.h> 32 #include <sys/openpromio.h> 33 #include <sys/mnttab.h> 34 #include <syslog.h> 35 #include <stdlib.h> 36 37 38 #define STRCPYLIM(dst, src, str) strcpy_limit(dst, src, sizeof (dst), str) 39 #define LASTBYTE(str) (str + strlen(str) - 1) 40 41 static char nerr_fmt[] = "number is out of range (%s)\n"; 42 static char alloc_fmt[] = "cannot allocate space for \"%s\", %s\n"; 43 static char set_thresh_fmt[] = "error setting threshold(s) for \"%s\", %s\n"; 44 static char bad_thresh_fmt[] = "bad threshold(s)\n"; 45 static char stat_fmt[] = "cannot stat \"%s\", %s\n"; 46 static char always_on[] = "always-on"; 47 48 /* 49 * When lines in a config file (usually "/etc/power.conf") start with 50 * a recognized keyword, a "handler" routine is called for specific 51 * CPR or PM -related action(s). Each routine returns a status code 52 * indicating whether all tasks were successful; if any errors occured, 53 * future CPR or PM updates are skipped. Following are the handler 54 * routines for all keywords: 55 */ 56 57 58 /* 59 * Check for valid autopm behavior and save after ioctl success. 60 */ 61 int 62 autopm(void) 63 { 64 struct btoc { 65 char *behavior; 66 int cmd, Errno, isdef; 67 }; 68 static struct btoc blist[] = { 69 "default", PM_START_PM, EBUSY, 1, 70 "disable", PM_STOP_PM, EINVAL, 0, 71 "enable", PM_START_PM, EBUSY, 0, 72 NULL, 0, 0, 0, 73 }; 74 struct btoc *bp; 75 char *behavior; 76 77 for (behavior = LINEARG(1), bp = blist; bp->cmd; bp++) { 78 if (strcmp(behavior, bp->behavior) == 0) 79 break; 80 } 81 if (bp->cmd == 0) { 82 mesg(MERR, "invalid autopm behavior \"%s\"\n", behavior); 83 return (NOUP); 84 } 85 86 /* 87 * for "default" behavior, do not enable autopm if not ESTAR_V3 88 */ 89 if (!bp->isdef || (estar_vers == ESTAR_V3)) { 90 if (ioctl(pm_fd, bp->cmd, NULL) == -1 && errno != bp->Errno) { 91 mesg(MERR, "autopm %s failed, %s\n", 92 behavior, strerror(errno)); 93 return (NOUP); 94 } 95 } 96 (void) strcpy(new_cc.apm_behavior, behavior); 97 return (OKUP); 98 } 99 100 101 static int 102 gethm(char *src, int *hour, int *min) 103 { 104 if (sscanf(src, "%d:%d", hour, min) != 2) { 105 mesg(MERR, "bad time format (%s)\n", src); 106 return (-1); 107 } 108 return (0); 109 } 110 111 112 static void 113 strcpy_limit(char *dst, char *src, size_t limit, char *info) 114 { 115 if (strlcpy(dst, src, limit) >= limit) 116 mesg(MEXIT, "%s is too long (%s)\n", info, src); 117 } 118 119 120 /* 121 * Convert autoshutdown idle and start/finish times; 122 * check and record autoshutdown behavior. 123 */ 124 int 125 autosd(void) 126 { 127 char **bp, *behavior; 128 char *unrec = gettext("unrecognized autoshutdown behavior"); 129 static char *blist[] = { 130 "autowakeup", "default", "noshutdown", 131 "shutdown", "unconfigured", NULL 132 }; 133 134 new_cc.as_idle = atoi(LINEARG(1)); 135 if (gethm(LINEARG(2), &new_cc.as_sh, &new_cc.as_sm) || 136 gethm(LINEARG(3), &new_cc.as_fh, &new_cc.as_fm)) 137 return (NOUP); 138 mesg(MDEBUG, "idle %d, start %d:%02d, finish %d:%02d\n", 139 new_cc.as_idle, new_cc.as_sh, new_cc.as_sm, 140 new_cc.as_fh, new_cc.as_fm); 141 142 for (behavior = LINEARG(4), bp = blist; *bp; bp++) { 143 if (strcmp(behavior, *bp) == 0) 144 break; 145 } 146 if (*bp == NULL) { 147 mesg(MERR, "%s: \"%s\"\n", unrec, behavior); 148 return (NOUP); 149 } 150 STRCPYLIM(new_cc.as_behavior, *bp, unrec); 151 return (OKUP); 152 } 153 154 155 /* 156 * Check for a real device and try to resolve to a full path. 157 * The orig/resolved path may be modified into a prom pathname, 158 * and an allocated copy of the result is stored at *destp; 159 * the caller will need to free that space. Returns 1 for any 160 * error, otherwise 0; also sets *errp after an alloc error. 161 */ 162 static int 163 devpath(char **destp, char *src, int *errp) 164 { 165 struct stat stbuf; 166 char buf[PATH_MAX]; 167 char *cp, *dstr; 168 int devok, dcs = 0; 169 size_t len; 170 171 /* 172 * When there's a real device, try to resolve the path 173 * and trim the leading "/devices" component. 174 */ 175 if ((devok = (stat(src, &stbuf) == 0 && stbuf.st_rdev)) != 0) { 176 if (realpath(src, buf) == NULL) { 177 mesg(MERR, "realpath cannot resolve \"%s\"\n", 178 src, strerror(errno)); 179 return (1); 180 } 181 src = buf; 182 dstr = "/devices"; 183 len = strlen(dstr); 184 dcs = (strncmp(src, dstr, len) == 0); 185 if (dcs) 186 src += len; 187 } else 188 mesg(MDEBUG, stat_fmt, src, strerror(errno)); 189 190 /* 191 * When the path has ":anything", display an error for 192 * a non-device or truncate a resolved+modifed path. 193 */ 194 if (cp = strchr(src, ':')) { 195 if (devok == 0) { 196 mesg(MERR, "physical path may not contain " 197 "a minor string (%s)\n", src); 198 return (1); 199 } else if (dcs) 200 *cp = '\0'; 201 } 202 203 if ((*destp = strdup(src)) == NULL) { 204 *errp = NOUP; 205 mesg(MERR, alloc_fmt, src, strerror(errno)); 206 } 207 return (*destp == NULL); 208 } 209 210 211 /* 212 * Call pm ioctl request(s) to set property/device dependencies. 213 */ 214 static int 215 dev_dep_common(int isprop) 216 { 217 int cmd, argn, upval = OKUP; 218 char *src, *first, **destp; 219 pm_req_t pmreq; 220 221 bzero(&pmreq, sizeof (pmreq)); 222 src = LINEARG(1); 223 if (isprop) { 224 cmd = PM_ADD_DEPENDENT_PROPERTY; 225 first = NULL; 226 pmreq.pmreq_kept = src; 227 } else { 228 cmd = PM_ADD_DEPENDENT; 229 if (devpath(&first, src, &upval)) 230 return (upval); 231 pmreq.pmreq_kept = first; 232 } 233 destp = &pmreq.pmreq_keeper; 234 235 /* 236 * Now loop through any dependents. 237 */ 238 for (argn = 2; (src = LINEARG(argn)) != NULL; argn++) { 239 if (devpath(destp, src, &upval)) { 240 if (upval != OKUP) 241 return (upval); 242 break; 243 } 244 if ((upval = ioctl(pm_fd, cmd, &pmreq)) == -1) { 245 mesg(MDEBUG, "pm ioctl, cmd %d, errno %d\n" 246 "kept \"%s\", keeper \"%s\"\n", 247 cmd, errno, pmreq.pmreq_kept, pmreq.pmreq_keeper); 248 mesg(MERR, "cannot set \"%s\" dependency " 249 "for \"%s\", %s\n", pmreq.pmreq_keeper, 250 pmreq.pmreq_kept, strerror(errno)); 251 } 252 free(*destp); 253 *destp = NULL; 254 if (upval != OKUP) 255 break; 256 } 257 258 free(first); 259 return (upval); 260 } 261 262 263 int 264 ddprop(void) 265 { 266 return (dev_dep_common(1)); 267 } 268 269 270 int 271 devdep(void) 272 { 273 return (dev_dep_common(0)); 274 } 275 276 277 /* 278 * Convert a numeric string (with a possible trailing scaling byte) 279 * into an integer. Returns a converted value and *nerrp unchanged, 280 * or 0 with *nerrp set to 1 for a conversion error. 281 */ 282 static int 283 get_scaled_value(char *str, int *nerrp) 284 { 285 longlong_t svalue = 0, factor = 1; 286 char *sp; 287 288 errno = 0; 289 svalue = strtol(str, &sp, 0); 290 if (errno || (*str != '-' && (*str < '0' || *str > '9'))) 291 *nerrp = 1; 292 else if (sp && *sp != '\0') { 293 if (*sp == 'h') 294 factor = 3600; 295 else if (*sp == 'm') 296 factor = 60; 297 else if (*sp != 's') 298 *nerrp = 1; 299 } 300 /* any bytes following sp are ignored */ 301 302 if (*nerrp == 0) { 303 svalue *= factor; 304 if (svalue < INT_MIN || svalue > INT_MAX) 305 *nerrp = 1; 306 } 307 if (*nerrp) 308 mesg(MERR, nerr_fmt, str); 309 mesg(MDEBUG, "got scaled value %d\n", (int)svalue); 310 return ((int)svalue); 311 } 312 313 314 /* 315 * Increment the count of threshold values, 316 * reallocate *vlistp and append another element. 317 * Returns 1 on error, otherwise 0. 318 */ 319 static int 320 vlist_append(int **vlistp, int *vcntp, int value) 321 { 322 (*vcntp)++; 323 if (*vlistp = realloc(*vlistp, *vcntp * sizeof (**vlistp))) 324 *(*vlistp + *vcntp - 1) = value; 325 else 326 mesg(MERR, alloc_fmt, "threshold list", strerror(errno)); 327 return (*vlistp == NULL); 328 } 329 330 331 /* 332 * Convert a single threshold string or paren groups of thresh's as 333 * described below. All thresh's are saved to an allocated list at 334 * *vlistp; the caller will need to free that space. On return: 335 * *vcntp is the count of the vlist array, and vlist is either 336 * a single thresh or N groups of thresh's with a trailing zero: 337 * (cnt_1 thr_1a thr_1b [...]) ... (cnt_N thr_Na thr_Nb [...]) 0. 338 * Returns 0 when all conversions were OK, and 1 for any syntax, 339 * conversion, or alloc error. 340 */ 341 static int 342 get_thresh(int **vlistp, int *vcntp) 343 { 344 int argn, value, gci, grp_cnt = 0, paren = 0, nerr = 0; 345 char *rp, *src; 346 347 for (argn = 2; (src = LINEARG(argn)) != NULL; argn++) { 348 if (*src == LPAREN) { 349 gci = *vcntp; 350 if (nerr = vlist_append(vlistp, vcntp, 0)) 351 break; 352 paren = 1; 353 src++; 354 } 355 if (*(rp = LASTBYTE(src)) == RPAREN) { 356 if (paren) { 357 grp_cnt = *vcntp - gci; 358 *(*vlistp + gci) = grp_cnt; 359 paren = 0; 360 *rp = '\0'; 361 } else { 362 nerr = 1; 363 break; 364 } 365 } 366 367 value = get_scaled_value(src, &nerr); 368 if (nerr || (nerr = vlist_append(vlistp, vcntp, value))) 369 break; 370 } 371 372 if (nerr == 0 && grp_cnt) 373 nerr = vlist_append(vlistp, vcntp, 0); 374 return (nerr); 375 } 376 377 378 /* 379 * Set device thresholds from (3) formats: 380 * path "always-on" 381 * path time-spec: [0-9]+[{h,m,s}] 382 * path (ts1 ts2 ...)+ 383 */ 384 int 385 devthr(void) 386 { 387 int cmd, upval = OKUP, nthresh = 0, *vlist = NULL; 388 pm_req_t pmreq; 389 390 bzero(&pmreq, sizeof (pmreq)); 391 if (devpath(&pmreq.physpath, LINEARG(1), &upval)) 392 return (upval); 393 394 if (strcmp(LINEARG(2), always_on) == 0) { 395 cmd = PM_SET_DEVICE_THRESHOLD; 396 pmreq.value = INT_MAX; 397 } else if (get_thresh(&vlist, &nthresh)) { 398 mesg(MERR, bad_thresh_fmt); 399 upval = NOUP; 400 } else if (nthresh == 1) { 401 pmreq.value = *vlist; 402 cmd = PM_SET_DEVICE_THRESHOLD; 403 } else { 404 pmreq.data = vlist; 405 pmreq.datasize = (nthresh * sizeof (*vlist)); 406 cmd = PM_SET_COMPONENT_THRESHOLDS; 407 } 408 409 if (upval != NOUP && (upval = ioctl(pm_fd, cmd, &pmreq)) == -1) 410 mesg(MERR, set_thresh_fmt, pmreq.physpath, strerror(errno)); 411 412 free(vlist); 413 free(pmreq.physpath); 414 return (upval); 415 } 416 417 418 static int 419 scan_int(char *src, int *dst) 420 { 421 long lval; 422 423 errno = 0; 424 425 lval = strtol(LINEARG(1), NULL, 0); 426 if (errno || lval > INT_MAX || lval < 0) { 427 mesg(MERR, nerr_fmt, src); 428 return (NOUP); 429 } 430 431 *dst = (int)lval; 432 return (OKUP); 433 } 434 435 static int 436 scan_float(char *src, float *dst) 437 { 438 float fval; 439 440 errno = 0; 441 442 fval = strtof(src, NULL); 443 if (errno || fval < 0.0) { 444 mesg(MERR, nerr_fmt, src); 445 return (NOUP); 446 } 447 448 *dst = fval; 449 return (OKUP); 450 } 451 452 453 int 454 dreads(void) 455 { 456 return (scan_int(LINEARG(1), &new_cc.diskreads_thold)); 457 } 458 459 460 /* 461 * Set pathname for idlecheck; 462 * an overflowed pathname is treated as a fatal error. 463 */ 464 int 465 idlechk(void) 466 { 467 STRCPYLIM(new_cc.idlecheck_path, LINEARG(1), "idle path"); 468 return (OKUP); 469 } 470 471 472 int 473 loadavg(void) 474 { 475 return (scan_float(LINEARG(1), &new_cc.loadaverage_thold)); 476 } 477 478 479 int 480 nfsreq(void) 481 { 482 return (scan_int(LINEARG(1), &new_cc.nfsreqs_thold)); 483 } 484 485 486 #ifdef sparc 487 static char open_fmt[] = "cannot open \"%s\", %s\n"; 488 489 /* 490 * Verify the filesystem type for a regular statefile is "ufs" 491 * or verify a block device is not in use as a mounted filesytem. 492 * Returns 1 if any error, otherwise 0. 493 */ 494 static int 495 check_mount(char *sfile, dev_t sfdev, int ufs) 496 { 497 char *src, *err_fmt = NULL, *mnttab = MNTTAB; 498 int rgent, match = 0; 499 struct extmnttab ent; 500 FILE *fp; 501 502 if ((fp = fopen(mnttab, "r")) == NULL) { 503 mesg(MERR, open_fmt, mnttab, strerror(errno)); 504 return (1); 505 } 506 507 /* 508 * Search for a matching dev_t; 509 * ignore non-ufs filesystems for a regular statefile. 510 */ 511 while ((rgent = getextmntent(fp, &ent, sizeof (ent))) != -1) { 512 if (rgent > 0) { 513 mesg(MERR, "error reading \"%s\"\n", mnttab); 514 (void) fclose(fp); 515 return (1); 516 } else if (ufs && strcmp(ent.mnt_fstype, "ufs")) 517 continue; 518 else if (makedev(ent.mnt_major, ent.mnt_minor) == sfdev) { 519 match = 1; 520 break; 521 } 522 } 523 (void) fclose(fp); 524 525 /* 526 * No match is needed for a block device statefile, 527 * a match is needed for a regular statefile. 528 */ 529 if (match == 0) { 530 if (new_cc.cf_type == CFT_SPEC) 531 STRCPYLIM(new_cc.cf_devfs, sfile, "block statefile"); 532 else 533 err_fmt = "cannot find ufs mount point for \"%s\"\n"; 534 } else if (new_cc.cf_type == CFT_UFS) { 535 STRCPYLIM(new_cc.cf_fs, ent.mnt_mountp, "mnt entry"); 536 STRCPYLIM(new_cc.cf_devfs, ent.mnt_special, "mnt special"); 537 while (*(sfile + 1) == '/') sfile++; 538 src = sfile + strlen(ent.mnt_mountp); 539 while (*src == '/') src++; 540 STRCPYLIM(new_cc.cf_path, src, "statefile path"); 541 } else 542 err_fmt = "statefile device \"%s\" is a mounted filesystem\n"; 543 if (err_fmt) 544 mesg(MERR, err_fmt, sfile); 545 return (err_fmt != NULL); 546 } 547 548 549 /* 550 * Convert a Unix device to a prom device and save on success, 551 * log any ioctl/conversion error. 552 */ 553 static int 554 utop(void) 555 { 556 union obpbuf { 557 char buf[OBP_MAXPATHLEN + sizeof (uint_t)]; 558 struct openpromio oppio; 559 }; 560 union obpbuf oppbuf; 561 struct openpromio *opp; 562 char *promdev = "/dev/openprom"; 563 int fd, upval; 564 565 if ((fd = open(promdev, O_RDONLY)) == -1) { 566 mesg(MERR, open_fmt, promdev, strerror(errno)); 567 return (NOUP); 568 } 569 570 opp = &oppbuf.oppio; 571 opp->oprom_size = OBP_MAXPATHLEN; 572 strcpy_limit(opp->oprom_array, new_cc.cf_devfs, 573 OBP_MAXPATHLEN, "statefile device"); 574 upval = ioctl(fd, OPROMDEV2PROMNAME, opp); 575 (void) close(fd); 576 if (upval == OKUP) 577 STRCPYLIM(new_cc.cf_dev_prom, opp->oprom_array, "prom device"); 578 else { 579 openlog("pmconfig", 0, LOG_DAEMON); 580 syslog(LOG_NOTICE, 581 gettext("cannot convert \"%s\" to prom device"), 582 new_cc.cf_devfs); 583 closelog(); 584 } 585 586 return (upval); 587 } 588 589 590 /* 591 * Check for a valid statefile pathname, inode and mount status. 592 */ 593 int 594 sfpath(void) 595 { 596 static int statefile; 597 char *err_fmt = NULL; 598 char *sfile, *sp, ch; 599 struct stat stbuf; 600 int dir = 0; 601 dev_t dev; 602 603 if (statefile) { 604 mesg(MERR, "ignored redundant statefile entry\n"); 605 return (OKUP); 606 } else if (ua_err) { 607 if (ua_err != ENOTSUP) 608 mesg(MERR, "uadmin(A_FREEZE, A_CHECK, 0): %s\n", 609 strerror(ua_err)); 610 return (NOUP); 611 } 612 613 /* 614 * Check for an absolute path and trim any trailing '/'. 615 */ 616 sfile = LINEARG(1); 617 if (*sfile != '/') { 618 mesg(MERR, "statefile requires an absolute path\n"); 619 return (NOUP); 620 } 621 for (sp = sfile + strlen(sfile) - 1; sp > sfile && *sp == '/'; sp--) 622 *sp = '\0'; 623 624 /* 625 * If the statefile doesn't exist, the leading path must be a dir. 626 */ 627 if (stat(sfile, &stbuf) == -1) { 628 if (errno == ENOENT) { 629 dir = 1; 630 if ((sp = strrchr(sfile, '/')) == sfile) 631 sp++; 632 ch = *sp; 633 *sp = '\0'; 634 if (stat(sfile, &stbuf) == -1) 635 err_fmt = stat_fmt; 636 *sp = ch; 637 } else 638 err_fmt = stat_fmt; 639 if (err_fmt) { 640 mesg(MERR, err_fmt, sfile, strerror(errno)); 641 return (NOUP); 642 } 643 } 644 645 /* 646 * Check for regular/dir/block types, set cf_type and dev. 647 */ 648 if (S_ISREG(stbuf.st_mode) || (dir && S_ISDIR(stbuf.st_mode))) { 649 new_cc.cf_type = CFT_UFS; 650 dev = stbuf.st_dev; 651 } else if (S_ISBLK(stbuf.st_mode)) { 652 if (minor(stbuf.st_rdev) != 2) { 653 new_cc.cf_type = CFT_SPEC; 654 dev = stbuf.st_rdev; 655 } else 656 err_fmt = "statefile device cannot be slice 2 (%s)\n" 657 "would clobber the disk label and boot-block\n"; 658 } else 659 err_fmt = "bad file type for \"%s\"\n" 660 "statefile must be a regular file or block device\n"; 661 if (err_fmt) { 662 mesg(MERR, err_fmt, sfile); 663 return (NOUP); 664 } 665 666 if (check_mount(sfile, dev, (new_cc.cf_type == CFT_UFS)) || utop()) 667 return (NOUP); 668 new_cc.cf_magic = CPR_CONFIG_MAGIC; 669 statefile = 1; 670 return (OKUP); 671 } 672 #endif /* sparc */ 673 674 675 /* 676 * Try setting system threshold. 677 */ 678 int 679 systhr(void) 680 { 681 int value, nerr = 0, upval = OKUP; 682 char *thresh = LINEARG(1); 683 684 if (strcmp(thresh, always_on) == 0) 685 value = INT_MAX; 686 else if ((value = get_scaled_value(thresh, &nerr)) < 0 || nerr) { 687 mesg(MERR, "%s must be a positive value\n", LINEARG(0)); 688 upval = NOUP; 689 } 690 if (upval == OKUP) 691 (void) ioctl(pm_fd, PM_SET_SYSTEM_THRESHOLD, value); 692 return (upval); 693 } 694 695 696 int 697 tchars(void) 698 { 699 return (scan_int(LINEARG(1), &new_cc.ttychars_thold)); 700 } 701