Home | History | Annotate | Download | only in rctladm
      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 2007 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     27 
     28 #include <sys/rctl_impl.h>
     29 #include <sys/types.h>
     30 #include <sys/stat.h>
     31 
     32 #include <errno.h>
     33 #include <libintl.h>
     34 #include <locale.h>
     35 #include <rctl.h>
     36 #include <stdio.h>
     37 #include <stdlib.h>
     38 #include <string.h>
     39 #include <syslog.h>
     40 #include <unistd.h>
     41 #include <fcntl.h>
     42 
     43 #include "utils.h"
     44 
     45 #define	ENABLE	1
     46 #define	DISABLE	0
     47 
     48 #define	CONFIGPATH	"/etc/rctladm.conf"
     49 #define	CONFIGOWNER	0	/* uid 0 (root) */
     50 #define	CONFIGGROUP	1	/* gid 1 (other) */
     51 #define	CONFIGPERM	(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) /* 0644 */
     52 
     53 /*
     54  *	Macros to produce a quoted string containing the value of a
     55  *	preprocessor macro. For example, if SIZE is defined to be 256,
     56  *	VAL2STR(SIZE) is "256". This is used to construct format
     57  *	strings for scanf-family functions below.
     58  */
     59 #define	QUOTE(x)	#x
     60 #define	VAL2STR(x)	QUOTE(x)
     61 
     62 static const char USAGE[] =
     63 	"Usage:\trctladm -l\n"
     64 	"\trctladm -u\n"
     65 	"\trctladm -e actions -d actions rctl_name\n";
     66 
     67 static const char OPTS[] = "d:e:lu";
     68 static int dflg, eflg, lflg, uflg;
     69 
     70 static uint_t op_failures;
     71 
     72 static void rctladm_enable(const char *, char *);
     73 
     74 #define	BUFSIZE 256
     75 
     76 static void
     77 usage()
     78 {
     79 	(void) fprintf(stderr, gettext(USAGE));
     80 	exit(E_USAGE);
     81 }
     82 
     83 #define	LOG_HIGHEST LOG_DEBUG
     84 static const char *syslog_priorities[] = {
     85 	"emerg",	/* LOG_EMERG	*/
     86 	"alert",	/* LOG_ALERT	*/
     87 	"crit",		/* LOG_CRIT	*/
     88 	"err",		/* LOG_ERR	*/
     89 	"warning",	/* LOG_WARNING	*/
     90 	"notice",	/* LOG_NOTICE	*/
     91 	"info",		/* LOG_INFO	*/
     92 	"debug"		/* LOG_DEBUG	*/
     93 };
     94 
     95 static int
     96 rctladm_syslog_prio(const char *priority)
     97 {
     98 	uint_t i;
     99 
    100 	for (i = 0; i < LOG_HIGHEST + 1; i++) {
    101 		if ((strcasecmp(priority, syslog_priorities[i]) == 0))
    102 			return (i);
    103 	}
    104 
    105 	die(gettext("unknown syslog priority \"%s\"\n"), priority);
    106 
    107 	/*NOTREACHED*/
    108 }
    109 
    110 /*ARGSUSED*/
    111 static int
    112 rctl_save_walk_cb(const char *rctl_name, void *file)
    113 {
    114 	FILE *fp = file;
    115 	rctlblk_t *gblk;
    116 	uint_t action;
    117 	rctl_opaque_t *gopq;
    118 
    119 	if ((gblk = malloc(rctlblk_size())) == NULL)
    120 		die(gettext("unable to allocate control block"));
    121 
    122 
    123 	if (rctlctl(rctl_name, gblk, RCTLCTL_GET) == -1) {
    124 		warn(gettext("unable to obtain control block contents for %s"),
    125 		    rctl_name);
    126 	} else {
    127 		action = rctlblk_get_global_action(gblk);
    128 		gopq = (rctl_opaque_t *)gblk;
    129 
    130 		(void) fprintf(fp, "%s=", rctl_name);
    131 		if (action & RCTL_GLOBAL_SYSLOG)
    132 			(void) fprintf(fp, "syslog=%s\n",
    133 			    syslog_priorities[gopq->rcq_global_syslog_level]);
    134 		else
    135 			(void) fprintf(fp, "none\n");
    136 	}
    137 
    138 	free(gblk);
    139 
    140 	return (0);
    141 }
    142 
    143 static void
    144 rctladm_save_config()
    145 {
    146 	int fd;
    147 	FILE *fp;
    148 
    149 	/*
    150 	 * Non-root users shouldn't update the configuration file.
    151 	 */
    152 	if (geteuid() != 0)
    153 		return;
    154 
    155 	if ((fd = open(CONFIGPATH, O_WRONLY|O_CREAT|O_TRUNC, CONFIGPERM)) == -1)
    156 		die(gettext("failed to open %s"), CONFIGPATH);
    157 
    158 	if ((fp = fdopen(fd, "w")) == NULL)
    159 		die(gettext("failed to open stream for %s"), CONFIGPATH);
    160 
    161 	(void) fputs(
    162 	    "#\n"
    163 	    "# rctladm.conf\n"
    164 	    "#\n"
    165 	    "# Parameters for resource controls configuration.\n"
    166 	    "# Do NOT edit this file by hand -- use rctladm(1m) instead.\n"
    167 	    "#\n",
    168 	    fp);
    169 
    170 	(void) rctl_walk(rctl_save_walk_cb, fp);
    171 
    172 	(void) fflush(fp);
    173 	(void) fsync(fd);
    174 	(void) fchmod(fd, CONFIGPERM);
    175 	(void) fchown(fd, CONFIGOWNER, CONFIGGROUP);
    176 	(void) fclose(fp);
    177 }
    178 
    179 static void
    180 rctladm_setup_action(char *name, char *action, int line)
    181 {
    182 	if (action[0] == '\0') {
    183 		warn(gettext("\"%s\", line %d, syntax error\n"), CONFIGPATH,
    184 		    line);
    185 		return;
    186 	}
    187 	rctladm_enable(name, action);
    188 }
    189 
    190 static void
    191 rctladm_read_config()
    192 {
    193 	int fd;
    194 	FILE *fp;
    195 	char buf[BUFSIZE];
    196 	char name[BUFSIZE+1], actions[BUFSIZE+1];
    197 	char *action;
    198 	int line, len, n;
    199 	rctl_opaque_t *gblk;
    200 
    201 	/*
    202 	 * Non-root users shouldn't do this.
    203 	 */
    204 	if (geteuid() != 0)
    205 		die(gettext("you must be root to use this option\n"));
    206 
    207 	if ((fd = open(CONFIGPATH, O_RDONLY, CONFIGPERM)) == -1)
    208 		die(gettext("failed to open %s"), CONFIGPATH);
    209 
    210 	if ((fp = fdopen(fd, "r")) == NULL)
    211 		die(gettext("failed to open stream for %s"), CONFIGPATH);
    212 
    213 	if ((gblk = malloc(rctlblk_size())) == NULL)
    214 		die(gettext("unable to allocate control block"));
    215 
    216 	for (line = 1; fgets(buf, BUFSIZE, fp) != NULL; line++) {
    217 		/*
    218 		 * Skip comment lines and empty lines.
    219 		 */
    220 		if (buf[0] == '#' || buf[0] == '\n')
    221 			continue;
    222 
    223 		/*
    224 		 * Look for "rctl_name=action;action;...;action, with
    225 		 * optional whitespace on either side, terminated by a newline,
    226 		 * and consuming the whole line.
    227 		 */
    228 		n = sscanf(buf,
    229 		    " %" VAL2STR(BUFSIZE) "[^=]=%" VAL2STR(BUFSIZE) "s \n%n",
    230 		    name, actions, &len);
    231 		if (n >= 1 && name[0] != '\0' &&
    232 		    (n == 1 || len == strlen(buf))) {
    233 			if (n == 1) {
    234 				warn(gettext("\"%s\", line %d, syntax error\n"),
    235 				    CONFIGPATH, line);
    236 				continue;
    237 			}
    238 			if (rctlctl(name, (rctlblk_t *)gblk,
    239 			    RCTLCTL_GET) == -1) {
    240 				warn(gettext("\"%s\", line %d, unknown resource"
    241 				    " control: %s\n"), CONFIGPATH, line, name);
    242 				continue;
    243 			}
    244 			if (actions[0] == ';') {
    245 				warn(gettext("\"%s\", line %d, syntax error\n"),
    246 				    CONFIGPATH, line);
    247 				continue;
    248 			}
    249 			action = strtok(actions, ";");
    250 			rctladm_setup_action(name, action, line);
    251 			while (action = strtok(NULL, ";"))
    252 				rctladm_setup_action(name, action, line);
    253 		}
    254 	}
    255 
    256 	if (line == 1)
    257 		die(gettext("failed to read rctl configuration from \"%s\""),
    258 		    CONFIGPATH);
    259 	free(gblk);
    260 	(void) fclose(fp);
    261 }
    262 
    263 static void
    264 rctladm_modify_action(const char *rctl_name, uint_t enable, uint_t action,
    265     int log_level)
    266 {
    267 	rctl_opaque_t *gblk;
    268 
    269 	if ((gblk = malloc(rctlblk_size())) == NULL)
    270 		die(gettext("unable to allocate control block"));
    271 
    272 	if (rctlctl(rctl_name, (rctlblk_t *)gblk, RCTLCTL_GET) == -1)
    273 		die(gettext("unable to obtain resource control block"));
    274 
    275 	if ((gblk->rcq_global_flagaction & RCTL_GLOBAL_SYSLOG_NEVER) &&
    276 	    (action == RCTL_GLOBAL_SYSLOG)) {
    277 		warn(gettext("\"syslog\" action not valid for %s\n"),
    278 		    rctl_name);
    279 		op_failures++;
    280 		free(gblk);
    281 		return;
    282 	}
    283 
    284 	if (enable) {
    285 		gblk->rcq_global_flagaction |= (action &
    286 		    ~RCTL_GLOBAL_ACTION_MASK);
    287 		gblk->rcq_global_syslog_level = log_level;
    288 	} else {
    289 		gblk->rcq_global_flagaction &= ~(action &
    290 		    ~RCTL_GLOBAL_ACTION_MASK);
    291 		gblk->rcq_global_syslog_level = LOG_NOTICE;
    292 	}
    293 
    294 	if (rctlctl(rctl_name, (rctlblk_t *)gblk, RCTLCTL_SET) == -1) {
    295 		warn(gettext("unable to update control block contents"));
    296 		op_failures++;
    297 	}
    298 
    299 	free(gblk);
    300 }
    301 
    302 static int
    303 rctladm_get_log_level(char *action)
    304 {
    305 	char *log_lvl_str;
    306 
    307 	/*
    308 	 * Our syslog priority defaults to LOG_NOTICE.
    309 	 */
    310 	if (strcmp("syslog", action) == 0)
    311 		return (LOG_NOTICE);
    312 
    313 	if (strncmp("syslog=", action, strlen("syslog=")) != 0)
    314 		die(gettext("unknown action \"%s\"\n"), action);
    315 
    316 	log_lvl_str = action + strlen("syslog=");
    317 
    318 	return (rctladm_syslog_prio(log_lvl_str));
    319 }
    320 
    321 
    322 static void
    323 rctladm_enable(const char *rctl_name, char *action)
    324 {
    325 	/*
    326 	 * Two valid values:  "none" and "syslog[=level]".
    327 	 */
    328 	if (strcmp("none", action) == 0) {
    329 		rctladm_modify_action(rctl_name, DISABLE,
    330 		    ~RCTL_GLOBAL_ACTION_MASK, 0);
    331 		return;
    332 	}
    333 
    334 	rctladm_modify_action(rctl_name, ENABLE, RCTL_GLOBAL_SYSLOG,
    335 	    rctladm_get_log_level(action));
    336 }
    337 
    338 static void
    339 rctladm_disable(const char *rctl_name, char *action)
    340 {
    341 	/*
    342 	 * Two valid values:  "all" and "syslog".
    343 	 */
    344 	if (strcmp("all", action) == 0) {
    345 		rctladm_modify_action(rctl_name, DISABLE,
    346 		    ~RCTL_GLOBAL_ACTION_MASK, 0);
    347 		return;
    348 	} else if (strcmp("syslog", action) == 0) {
    349 		rctladm_modify_action(rctl_name, DISABLE, RCTL_GLOBAL_SYSLOG,
    350 		    0);
    351 		return;
    352 	}
    353 
    354 	die(gettext("unknown action \"%s\"\n"), action);
    355 }
    356 
    357 static void
    358 rctlblk_display(FILE *f, rctlblk_t *gblk)
    359 {
    360 	uint_t action = rctlblk_get_global_action(gblk);
    361 	uint_t flags = rctlblk_get_global_flags(gblk);
    362 	rctl_opaque_t *gopq = (rctl_opaque_t *)gblk;
    363 
    364 	if (flags & RCTL_GLOBAL_SYSLOG_NEVER)
    365 		(void) fprintf(f, "syslog=n/a    ");
    366 	else if (action & RCTL_GLOBAL_SYSLOG)
    367 		(void) fprintf(f, "syslog=%-7s",
    368 		    syslog_priorities[gopq->rcq_global_syslog_level]);
    369 	else
    370 		(void) fprintf(f, "syslog=off    ");
    371 
    372 	if (flags & RCTL_GLOBAL_ACTION_MASK)
    373 		(void) fprintf(f, " [");
    374 
    375 	if (flags & RCTL_GLOBAL_NOBASIC)
    376 		(void) fprintf(f, " no-basic");
    377 	if (flags & RCTL_GLOBAL_LOWERABLE)
    378 		(void) fprintf(f, " lowerable");
    379 	if (flags & RCTL_GLOBAL_DENY_ALWAYS)
    380 		(void) fprintf(f, " deny");
    381 	if (flags & RCTL_GLOBAL_DENY_NEVER)
    382 		(void) fprintf(f, " no-deny");
    383 	if (flags & RCTL_GLOBAL_CPU_TIME)
    384 		(void) fprintf(f, " cpu-time");
    385 	if (flags & RCTL_GLOBAL_FILE_SIZE)
    386 		(void) fprintf(f, " file-size");
    387 	if (flags & RCTL_GLOBAL_SIGNAL_NEVER)
    388 		(void) fprintf(f, " no-signal");
    389 	if (flags & RCTL_GLOBAL_UNOBSERVABLE)
    390 		(void) fprintf(f, " no-obs");
    391 	if (flags & RCTL_GLOBAL_INFINITE)
    392 		(void) fprintf(f, " inf");
    393 	if (flags & RCTL_GLOBAL_SYSLOG_NEVER)
    394 		(void) fprintf(f, " no-syslog");
    395 	if (flags & RCTL_GLOBAL_SECONDS)
    396 		(void) fprintf(f, " seconds");
    397 	if (flags & RCTL_GLOBAL_BYTES)
    398 		(void) fprintf(f, " bytes");
    399 	if (flags & RCTL_GLOBAL_COUNT)
    400 		(void) fprintf(f, " count");
    401 	if (flags & RCTL_GLOBAL_ACTION_MASK)
    402 		(void) fprintf(f, " ]");
    403 
    404 	(void) fprintf(f, "\n");
    405 }
    406 
    407 /*ARGSUSED*/
    408 static int
    409 rctl_walk_cb(const char *rctl_name, void *pvt)
    410 {
    411 	rctlblk_t *gblk;
    412 
    413 	if ((gblk = malloc(rctlblk_size())) == NULL)
    414 		die(gettext("unable to allocate control block"));
    415 
    416 	if (rctlctl(rctl_name, gblk, RCTLCTL_GET) == -1) {
    417 		if (errno == ESRCH)
    418 			warn(gettext("unknown resource control: %s\n"),
    419 			    rctl_name);
    420 		else
    421 			warn(gettext("unable to obtain %s properties"),
    422 			    rctl_name);
    423 		op_failures++;
    424 	} else {
    425 		(void) printf("%-27s ", rctl_name);
    426 		rctlblk_display(stdout, gblk);
    427 	}
    428 
    429 	free(gblk);
    430 
    431 	return (0);
    432 }
    433 
    434 static void
    435 rctladm_list_rctls(int optind, int argc, char *argv[])
    436 {
    437 	if (optind >= argc) {
    438 		(void) rctl_walk(rctl_walk_cb, NULL);
    439 		return;
    440 	}
    441 
    442 	for (; optind < argc; optind++)
    443 		(void) rctl_walk_cb(argv[optind], NULL);
    444 }
    445 
    446 int
    447 main(int argc, char *argv[])
    448 {
    449 	int c;			/* options character */
    450 	char *action;
    451 	char *rctl;
    452 
    453 	(void) setlocale(LC_ALL, "");
    454 	(void) textdomain(TEXT_DOMAIN);
    455 	(void) setprogname(argv[0]);
    456 
    457 	while ((c = getopt(argc, argv, OPTS)) != EOF) {
    458 		switch (c) {
    459 			case 'd':
    460 				dflg++;
    461 				action = optarg;
    462 				break;
    463 			case 'e':
    464 				eflg++;
    465 				action = optarg;
    466 				break;
    467 			case 'l':
    468 				lflg = 1;
    469 				break;
    470 			case 'u':
    471 				uflg = 1;
    472 				break;
    473 			case '?':
    474 			default:
    475 				usage();
    476 		}
    477 	}
    478 
    479 	if (uflg) {
    480 		rctladm_read_config();
    481 		return (E_SUCCESS);
    482 	}
    483 
    484 	if (lflg && (dflg || eflg)) {
    485 		warn(gettext("-l, -d, and -e flags are exclusive\n"));
    486 		usage();
    487 	}
    488 
    489 	if (dflg && eflg) {
    490 		warn(gettext("-d and -e flags are exclusive\n"));
    491 		usage();
    492 	}
    493 
    494 	if (dflg > 1 || eflg > 1) {
    495 		warn(gettext("only one -d or -e flag per line\n"));
    496 		usage();
    497 	}
    498 
    499 	if (lflg || !(dflg || eflg)) {
    500 		rctladm_list_rctls(optind, argc, argv);
    501 		rctladm_save_config();
    502 
    503 		return (op_failures ? E_ERROR : E_SUCCESS);
    504 	}
    505 
    506 	if (optind >= argc) {
    507 		warn(gettext("must specify one or more "
    508 		    "resource control names\n"));
    509 		usage();
    510 	}
    511 
    512 	for (; optind < argc; optind++) {
    513 		rctl = argv[optind];
    514 
    515 		if (eflg) {
    516 			rctladm_enable(rctl, action);
    517 			rctladm_save_config();
    518 		} else if (dflg) {
    519 			rctladm_disable(rctl, action);
    520 			rctladm_save_config();
    521 		} else {
    522 			usage();
    523 		}
    524 	}
    525 
    526 	return (op_failures ? E_ERROR : E_SUCCESS);
    527 }
    528