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