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