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