Home | History | Annotate | Download | only in power
      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 <deflt.h>
     28 #include <pwd.h>
     29 
     30 #ifdef sparc
     31 #include <libdevinfo.h>
     32 static char sf_cmt[] = "# Statefile\t\tPath\n";
     33 #endif
     34 
     35 static char as_cmt[] =
     36 	"# Auto-Shutdown\t\tIdle(min)\tStart/Finish(hh:mm)\tBehavior\n";
     37 
     38 char **line_args;
     39 int lineno = 0;
     40 
     41 /*
     42  * cpr and pm combined permission/update status
     43  */
     44 prmup_t cpr_status = { 0, OKUP, "cpr" };
     45 prmup_t pm_status  = { 0, OKUP, "pm" };
     46 
     47 
     48 /*
     49  * For config file parsing to work correctly/efficiently, this table
     50  * needs to be sorted by .keyword and any longer string like "device"
     51  * must appear before a substring like "dev".
     52  */
     53 static cinfo_t conftab[] = {
     54 	"S3-support",		S3sup,   &pm_status,	NULL,	2, 0, 1,
     55 	"autoS3",		autoS3,  &pm_status,	NULL,	2, 0, 1,
     56 	"autopm",		autopm,  &pm_status,	NULL,	2, 0, 1,
     57 	"autoshutdown",		autosd,  &cpr_status,	as_cmt,	5, 0, 1,
     58 	"cpu-threshold",	cputhr,  &pm_status,	NULL,	2, 0, 1,
     59 	"cpu_deep_idle",	cpuidle, &pm_status,	NULL,	2, 0, 1,
     60 	"cpupm",		cpupm,   &pm_status,	NULL,	2, 1, 1,
     61 	"device-dependency-property",
     62 				ddprop,  &pm_status,	NULL,	3, 1, 1,
     63 	"device-dependency",	devdep,  &pm_status,	NULL,	3, 1, 1,
     64 	"device-thresholds",	devthr,  &pm_status,	NULL,	3, 1, 1,
     65 	"diskreads",		dreads,  &cpr_status,	NULL,	2, 0, 1,
     66 	"idlecheck",		idlechk, &cpr_status,	NULL,	2, 0, 0,
     67 	"loadaverage",		loadavg, &cpr_status,	NULL,	2, 0, 1,
     68 	"nfsreqs",		nfsreq,  &cpr_status,	NULL,	2, 0, 1,
     69 #ifdef  sparc
     70 	"statefile",		sfpath,  &cpr_status,	sf_cmt,	2, 0, 0,
     71 #endif
     72 	"system-threshold",	systhr,  &pm_status,	NULL,	2, 0, 1,
     73 	"ttychars",		tchars,  &cpr_status,	NULL,	2, 0, 1,
     74 	NULL,			NULL,	 NULL,		NULL,	0, 0, 0,
     75 };
     76 
     77 
     78 /*
     79  * Set cpr/pm permission from default file info.
     80  */
     81 static void
     82 set_perm(char *defstr, char *user, int *perm, int cons)
     83 {
     84 	char *dinfo, *tk;
     85 
     86 	/*
     87 	 * /etc/default/power entries are:
     88 	 *	all			(all users + root)
     89 	 *	-			(none + root)
     90 	 *	<user1[, user2...>	(list users + root)
     91 	 *	console-owner		(console onwer + root)
     92 	 * Any error in reading/parsing the file limits the
     93 	 * access requirement to root.
     94 	 */
     95 	dinfo = defread(defstr);
     96 	mesg(MDEBUG, "set_perm: \"%s\", value \"%s\"\n",
     97 	    defstr, dinfo ? dinfo : "NULL");
     98 	if (dinfo == NULL)
     99 		return;
    100 	else if (strcmp(dinfo, "all") == 0)
    101 		*perm = 1;
    102 	else if (strcmp(dinfo, "console-owner") == 0)
    103 		*perm = cons;
    104 	else if (user != NULL &&
    105 	    (*dinfo == '<') && (tk = strrchr(++dinfo, '>'))) {
    106 		/* Scan dinfo for a matching user. */
    107 		for (*tk = '\0'; tk = strtok(dinfo, ", "); dinfo = NULL) {
    108 			mesg(MDEBUG, "match_user: cmp (\"%s\", \"%s\")\n",
    109 			    tk, user);
    110 			if (strcmp(tk, user) == 0) {
    111 				*perm = 1;
    112 				break;
    113 			}
    114 		}
    115 	}
    116 }
    117 
    118 
    119 /*
    120  * Lookup cpr/pm user permissions in "/etc/default/power".
    121  */
    122 void
    123 lookup_perms(void)
    124 {
    125 	struct passwd *pent;
    126 	struct stat stbuf;
    127 	int cons_perm;
    128 	char *user;
    129 
    130 	if ((ruid = getuid()) == 0) {
    131 		cpr_status.perm = pm_status.perm = 1;
    132 		return;
    133 	} else if ((pent = getpwuid(ruid)) != NULL) {
    134 		user = pent->pw_name;
    135 	} else {
    136 		user = NULL;
    137 	}
    138 
    139 	if (defopen("/etc/default/power") == -1)
    140 		return;
    141 	if (stat("/dev/console", &stbuf) == -1)
    142 		cons_perm = 0;
    143 	else
    144 		cons_perm = (ruid == stbuf.st_uid);
    145 
    146 	set_perm("PMCHANGEPERM=", user, &pm_status.perm, cons_perm);
    147 	set_perm("CPRCHANGEPERM=", user, &cpr_status.perm, cons_perm);
    148 
    149 	(void) defopen(NULL);
    150 }
    151 
    152 
    153 #ifdef sparc
    154 /*
    155  * Lookup energystar-v[23] property and set estar_vers.
    156  */
    157 void
    158 lookup_estar_vers(void)
    159 {
    160 	char es_prop[] = "energystar-v?", *fmt = "%s init/access error\n";
    161 	di_prom_handle_t ph;
    162 	di_node_t node;
    163 	uchar_t *prop_data;
    164 	int last;
    165 	char ch;
    166 
    167 	if ((node = di_init("/", DINFOPROP)) == DI_NODE_NIL) {
    168 		mesg(MERR, fmt, "di_init");
    169 		return;
    170 	} else if ((ph = di_prom_init()) == DI_PROM_HANDLE_NIL) {
    171 		mesg(MERR, fmt, "di_prom_init");
    172 		di_fini(node);
    173 		return;
    174 	}
    175 	last = strlen(es_prop) - 1;
    176 	for (ch = ESTAR_V2; ch <= ESTAR_V3; ch++) {
    177 		es_prop[last] = ch;
    178 		if (di_prom_prop_lookup_bytes(ph, node,
    179 		    es_prop, &prop_data) == 0) {
    180 			mesg(MDEBUG, "get_estar_vers: %s prop found\n",
    181 			    es_prop);
    182 			estar_vers = ch;
    183 			break;
    184 		}
    185 	}
    186 	di_prom_fini(ph);
    187 	di_fini(node);
    188 }
    189 #endif /* sparc */
    190 
    191 
    192 /*
    193  * limit open() to the real user
    194  */
    195 static int
    196 pmc_open(char *name, int oflag)
    197 {
    198 	uid_t euid;
    199 	int fd;
    200 
    201 	euid = geteuid();
    202 	if (seteuid(ruid) == -1)
    203 		mesg(MEXIT, "cannot reset euid to %d, %s\n",
    204 		    ruid, strerror(errno));
    205 	fd = open(name, oflag);
    206 	(void) seteuid(euid);
    207 	return (fd);
    208 }
    209 
    210 
    211 /*
    212  * Alloc space and read a config file; caller needs to free the space.
    213  */
    214 static char *
    215 get_conf_data(char *name)
    216 {
    217 	struct stat stbuf;
    218 	ssize_t nread;
    219 	size_t size;
    220 	char *buf;
    221 	int fd;
    222 
    223 	if ((fd = pmc_open(name, O_RDONLY)) == -1)
    224 		mesg(MEXIT, "cannot open %s\n", name);
    225 	else if (fstat(fd, &stbuf) == -1)
    226 		mesg(MEXIT, "cannot stat %s\n", name);
    227 	size = (size_t)stbuf.st_size;
    228 	def_src = (stbuf.st_ino == def_info.st_ino &&
    229 	    stbuf.st_dev == def_info.st_dev);
    230 	if ((buf = malloc(size + 1)) == NULL)
    231 		mesg(MEXIT, "cannot allocate %u for \"%s\"\n", size + 1, name);
    232 	nread = read(fd, buf, size);
    233 	(void) close(fd);
    234 	if (nread != (ssize_t)size)
    235 		mesg(MEXIT, "read error, expect %u, got %d, file \"%s\"\n",
    236 		    size, nread, name);
    237 	*(buf + size) = '\0';
    238 	return (buf);
    239 }
    240 
    241 
    242 /*
    243  * Add an arg to line_args, adding space if needed.
    244  */
    245 static void
    246 newarg(char *arg, int index)
    247 {
    248 	static int alcnt;
    249 	size_t size;
    250 
    251 	if ((index + 1) > alcnt) {
    252 		alcnt += 4;
    253 		size = alcnt * sizeof (*line_args);
    254 		if ((line_args = realloc(line_args, size)) == NULL)
    255 			mesg(MEXIT, "cannot alloc %u for line args\n", size);
    256 	}
    257 	*(line_args + index) = arg;
    258 }
    259 
    260 
    261 /*
    262  * Convert blank-delimited words into an arg vector and return
    263  * the arg count; character strings get null-terminated in place.
    264  */
    265 static int
    266 build_args(char *cline, char *tail)
    267 {
    268 	extern int debug;
    269 	char **vec, *arg, *cp;
    270 	int cnt = 0;
    271 
    272 	/*
    273 	 * Search logic: look for "\\\n" as a continuation marker,
    274 	 * treat any other "\\*" as ordinary arg data, scan until a
    275 	 * white-space delimiter is found, and if the arg has length,
    276 	 * null-terminate and save arg to line_args.  The scan includes
    277 	 * tail so the last arg is found without any special-case code.
    278 	 */
    279 	for (arg = cp = cline; cp <= tail; cp++) {
    280 		if (*cp == '\\') {
    281 			if (*(cp + 1) && *(cp + 1) != '\n') {
    282 				cp++;
    283 				continue;
    284 			}
    285 		} else if (strchr(" \t\n", *cp) == NULL)
    286 			continue;
    287 		if (cp - arg) {
    288 			*cp = '\0';
    289 			newarg(arg, cnt++);
    290 		}
    291 		arg = cp + 1;
    292 	}
    293 	newarg(NULL, cnt);
    294 
    295 	if (debug) {
    296 		mesg(MDEBUG, "\nline %d, found %d args:\n", lineno, cnt);
    297 		for (vec = line_args; *vec; vec++)
    298 			mesg(MDEBUG, "    \"%s\"\n", *vec);
    299 	}
    300 
    301 	return (cnt);
    302 }
    303 
    304 
    305 /*
    306  * Match leading keyword from a conf line and
    307  * return a reference to a config info struct.
    308  */
    309 static cinfo_t *
    310 get_cinfo(void)
    311 {
    312 	cinfo_t *cip, *info = NULL;
    313 	char *keyword;
    314 	int chr_diff;
    315 
    316 	/*
    317 	 * Scan the config table for a matching keyword; since the table
    318 	 * is sorted by keyword strings, a few optimizations can be done:
    319 	 * first compare only the first byte of the keyword, skip any
    320 	 * table string that starts with a lower ASCII value, compare the
    321 	 * full string only when the first byte matches, and stop checking
    322 	 * if the table string starts with a higher ASCII value.
    323 	 */
    324 	keyword = LINEARG(0);
    325 	for (cip = conftab; cip->keyword; cip++) {
    326 		chr_diff = (int)(*cip->keyword - *keyword);
    327 #if 0
    328 		mesg(MDEBUG, "line %d, ('%c' - '%c') = %d\n",
    329 		    lineno, *cip->keyword, *line, chr_diff);
    330 #endif
    331 		if (chr_diff < 0)
    332 			continue;
    333 		else if (chr_diff == 0) {
    334 			if (strcmp(keyword, cip->keyword) == 0) {
    335 				info = cip;
    336 				break;
    337 			}
    338 		} else
    339 			break;
    340 	}
    341 	return (info);
    342 }
    343 
    344 
    345 /*
    346  * Find the end of a [possibly continued] conf line
    347  * and record the real/lf-delimited line count at *lcnt.
    348  */
    349 static char *
    350 find_line_end(char *line, int *lcnt)
    351 {
    352 	char *next, *lf;
    353 
    354 	*lcnt = 0;
    355 	next = line;
    356 	while (lf = strchr(next, '\n')) {
    357 		(*lcnt)++;
    358 		if (lf == line || (*(lf - 1) != '\\') || *(lf + 1) == '\0')
    359 			break;
    360 		next = lf + 1;
    361 	}
    362 	return (lf);
    363 }
    364 
    365 
    366 /*
    367  * Parse the named conf file and for each conf line
    368  * call the action routine or conftab handler routine.
    369  */
    370 void
    371 parse_conf_file(char *name, vact_t action, boolean_t first_parse)
    372 {
    373 	char *file_buf, *cline, *line, *lend;
    374 	cinfo_t *cip;
    375 	int linc, cnt;
    376 	size_t llen;
    377 	int dontcare;
    378 
    379 	/*
    380 	 * Do the "implied default" for autoS3, but only before we
    381 	 * start parsing the first conf file.
    382 	 */
    383 	if (first_parse) {
    384 		(void) S3_helper("S3-support-enable", "S3-support-disable",
    385 		    PM_ENABLE_S3, PM_DISABLE_S3, "S3-support", "default",
    386 		    &dontcare, -1);
    387 	}
    388 
    389 	file_buf = get_conf_data(name);
    390 	mesg(MDEBUG, "\nnow parsing \"%s\"...\n", name);
    391 
    392 	lineno = 1;
    393 	line = file_buf;
    394 	while (lend = find_line_end(line, &linc)) {
    395 		/*
    396 		 * Each line should start with valid data
    397 		 * but leading white-space can be ignored
    398 		 */
    399 		while (line < lend) {
    400 			if (*line != ' ' && *line != '\t')
    401 				break;
    402 			line++;
    403 		}
    404 
    405 		/*
    406 		 * Copy line into allocated space and null-terminate
    407 		 * without the trailing line-feed.
    408 		 */
    409 		if ((llen = (lend - line)) != 0) {
    410 			if ((cline = malloc(llen + 1)) == NULL)
    411 				mesg(MEXIT, "cannot alloc %u bytes "
    412 				    "for line copy\n", llen);
    413 			(void) memcpy(cline, line, llen);
    414 			*(cline + llen) = '\0';
    415 		} else
    416 			cline = NULL;
    417 
    418 		/*
    419 		 * For blank and comment lines: possibly show a debug
    420 		 * message and otherwise ignore them.  For other lines:
    421 		 * parse into an arg vector and try to match the first
    422 		 * arg with conftab keywords.  When a match is found,
    423 		 * check for exact or minimum arg count, and call the
    424 		 * action or handler routine; if handler does not return
    425 		 * OKUP, set the referenced update value to NOUP so that
    426 		 * later CPR or PM updates are skipped.
    427 		 */
    428 		if (llen == 0)
    429 			mesg(MDEBUG, "\nline %d, blank...\n", lineno);
    430 		else if (*line == '#')
    431 			mesg(MDEBUG, "\nline %d, comment...\n", lineno);
    432 		else if (cnt = build_args(cline, cline + llen)) {
    433 			if ((cip = get_cinfo()) == NULL) {
    434 				mesg(MEXIT, "unrecognized keyword \"%s\"\n",
    435 				    LINEARG(0));
    436 			} else if (cnt != cip->argc &&
    437 			    (cip->any == 0 || cnt < cip->argc)) {
    438 				mesg(MEXIT, "found %d args, expect %d%s\n",
    439 				    cnt, cip->argc, cip->any ? "+" : "");
    440 			} else if (action)
    441 				(*action)(line, llen + 1, cip);
    442 			else if (cip->status->perm && (def_src || cip->alt)) {
    443 				if ((*cip->handler)() != OKUP)
    444 					cip->status->update = NOUP;
    445 			} else {
    446 				mesg(MDEBUG,
    447 				    "==> handler skipped: %s_perm %d, "
    448 				    "def_src %d, alt %d\n", cip->status->set,
    449 				    cip->status->perm, def_src, cip->alt);
    450 			}
    451 		}
    452 
    453 		if (cline)
    454 			free(cline);
    455 		line = lend + 1;
    456 		lineno += linc;
    457 	}
    458 	lineno = 0;
    459 
    460 	free(file_buf);
    461 
    462 	if (verify) {
    463 		int ret = ioctl(pm_fd, PM_GET_PM_STATE, NULL);
    464 		if (ret < 0) {
    465 			mesg(MDEBUG, "Cannot get PM state: %s\n",
    466 			    strerror(errno));
    467 		}
    468 		switch (ret) {
    469 		case PM_SYSTEM_PM_ENABLED:
    470 			mesg(MDEBUG, "Autopm Enabled\n");
    471 			break;
    472 		case PM_SYSTEM_PM_DISABLED:
    473 			mesg(MDEBUG, "Autopm Disabled\n");
    474 			break;
    475 		}
    476 		ret = ioctl(pm_fd, PM_GET_S3_SUPPORT_STATE, NULL);
    477 		if (ret < 0) {
    478 			mesg(MDEBUG, "Cannot get PM state: %s\n",
    479 			    strerror(errno));
    480 		}
    481 		switch (ret) {
    482 		case PM_S3_SUPPORT_ENABLED:
    483 			mesg(MDEBUG, "S3 support Enabled\n");
    484 			break;
    485 		case PM_S3_SUPPORT_DISABLED:
    486 			mesg(MDEBUG, "S3 support Disabled\n");
    487 			break;
    488 		}
    489 		ret = ioctl(pm_fd, PM_GET_AUTOS3_STATE, NULL);
    490 		if (ret < 0) {
    491 			mesg(MDEBUG, "Cannot get PM state: %s\n",
    492 			    strerror(errno));
    493 		}
    494 		switch (ret) {
    495 		case PM_AUTOS3_ENABLED:
    496 			mesg(MDEBUG, "AutoS3 Enabled\n");
    497 			break;
    498 		case PM_AUTOS3_DISABLED:
    499 			mesg(MDEBUG, "AutoS3  Disabled\n");
    500 			break;
    501 		}
    502 	}
    503 }
    504