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