xref: /illumos-gate/usr/src/cmd/rcap/rcapadm/rcapadm.c (revision 150d2c5288c645a1c1a7d2bee61199a3729406c7)
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 2006 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/types.h>
29 #include <sys/stat.h>
30 #include <sys/wait.h>
31 #include <fcntl.h>
32 #include <errno.h>
33 #include <signal.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <strings.h>
37 #include <unistd.h>
38 #include <libscf.h>
39 #include <libscf_priv.h>
40 #include <libintl.h>
41 #include <locale.h>
42 #include <zone.h>
43 #include <libzonecfg.h>
44 
45 #include "utils.h"
46 #include "rcapd.h"
47 #include "rcapd_conf.h"
48 #include "rcapd_stat.h"
49 
50 #define	RCAP_FMRI		"system/rcap:default"
51 
52 static void
53 usage()
54 {
55 	(void) fprintf(stderr,
56 	    gettext("usage: rcapadm\n"
57 	    "               [-E|-D]                                "
58 	    "# enable/disable rcapd\n"
59 	    "               [-n]                                   "
60 	    "# don't start/stop rcapd\n"
61 	    "               [-i <scan|sample|report|config>=value] "
62 	    "# set intervals\n"
63 	    "               [-c <percent>]                         "
64 	    "# set memory cap\n"
65 	    "                                                      "
66 	    "# enforcement threshold\n"
67 	    "               [-z <zonename> -m <max-rss>]               "
68 	    "# update zone memory cap\n"));
69 	exit(E_USAGE);
70 }
71 
72 static rcfg_t conf;
73 static int enable = -1;
74 static int disable = -1;
75 static int pressure = -1;
76 static int no_starting_stopping = -1;
77 static int scan_interval = -1;
78 static int report_interval = -1;
79 static int config_interval = -1;
80 static int sample_interval = -1;
81 static char *fname = RCAPD_DEFAULT_CONF_FILE;
82 
83 static char *subopt_v[] = {
84 	"scan",
85 	"sample",
86 	"report",
87 	"config",
88 	NULL
89 };
90 
91 typedef enum {
92 	OPT_SCAN = 0,
93 	OPT_SAMPLE,
94 	OPT_REPORT,
95 	OPT_CONFIG
96 } subopt_idx_t;
97 
98 static void
99 print_state(void)
100 {
101 	scf_simple_prop_t *persistent_prop = NULL;
102 	scf_simple_prop_t *temporary_prop = NULL;
103 	uint8_t *persistent = NULL;
104 	uint8_t *temporary = NULL;
105 	scf_handle_t *h;
106 	/* LINTED: conditionally assigned and used in function */
107 	ssize_t numvals;
108 
109 	if ((h = scf_handle_create(SCF_VERSION)) == NULL ||
110 	    scf_handle_bind(h) != 0)
111 		goto out;
112 
113 	if ((persistent_prop = scf_simple_prop_get(h, RCAP_FMRI,
114 	    SCF_PG_GENERAL, SCF_PROPERTY_ENABLED)) != NULL && (numvals =
115 	    scf_simple_prop_numvalues(persistent_prop)) > 0)
116 		persistent = scf_simple_prop_next_boolean(persistent_prop);
117 
118 	if ((temporary_prop = scf_simple_prop_get(h, RCAP_FMRI,
119 	    SCF_PG_GENERAL_OVR, SCF_PROPERTY_ENABLED)) != NULL && (numvals =
120 	    scf_simple_prop_numvalues(temporary_prop)) > 0)
121 		temporary = scf_simple_prop_next_boolean(temporary_prop);
122 
123 out:
124 	if (!persistent)
125 		(void) printf(gettext("                                      "
126 		    "state: unknown"));
127 	else if (temporary && *temporary != *persistent)
128 		(void) printf(gettext("                                      "
129 		    "state: %s (%s at next boot)\n"), *temporary ?
130 		    gettext("enabled") : gettext("disabled"), *persistent ?
131 		    gettext("enabled") : gettext("disabled"));
132 	else
133 		(void) printf(gettext("                                      "
134 		    "state: %s\n"), *persistent ? gettext("enabled") :
135 			gettext("disabled"));
136 
137 	scf_simple_prop_free(temporary_prop);
138 	scf_simple_prop_free(persistent_prop);
139 	scf_handle_destroy(h);
140 }
141 
142 /*
143  * Update the in-kernel memory cap for the specified zone.
144  */
145 static int
146 update_zone_mcap(char *zonename, char *maxrss)
147 {
148 	zoneid_t zone_id;
149 	uint64_t num;
150 
151 	if (getzoneid() != GLOBAL_ZONEID || zonecfg_in_alt_root())
152 		return (E_SUCCESS);
153 
154 	/* get the running zone from the kernel */
155 	if ((zone_id = getzoneidbyname(zonename)) == -1) {
156 		(void) fprintf(stderr, gettext("zone '%s' must be running\n"),
157 		    zonename);
158 		return (E_ERROR);
159 	}
160 
161 	if (zonecfg_str_to_bytes(maxrss, &num) == -1) {
162 		(void) fprintf(stderr, gettext("invalid max-rss value\n"));
163 		return (E_ERROR);
164 	}
165 
166 	if (zone_setattr(zone_id, ZONE_ATTR_PHYS_MCAP, &num, 0) == -1) {
167 		(void) fprintf(stderr, gettext("could not set memory "
168 		    "cap for zone '%s'\n"), zonename);
169 		return (E_ERROR);
170 	}
171 
172 	return (E_SUCCESS);
173 }
174 
175 int
176 main(int argc, char *argv[])
177 {
178 	char *subopts, *optval;
179 	int modified = 0;
180 	boolean_t refresh = B_FALSE;
181 	int opt;
182 	char *zonename;
183 	char *maxrss = NULL;
184 
185 	(void) setprogname("rcapadm");
186 	(void) setlocale(LC_ALL, "");
187 	(void) textdomain(TEXT_DOMAIN);
188 
189 	while ((opt = getopt(argc, argv, "DEc:i:m:nz:")) != EOF) {
190 		switch (opt) {
191 		case 'n':
192 			no_starting_stopping = 1;
193 			break;
194 		case 'c':
195 			if ((pressure = xatoi(optarg)) < 0 ||
196 			    pressure > 100 ||
197 			    errno == EINVAL)
198 				usage();
199 			modified++;
200 			break;
201 		case 'E':
202 			enable = 1;
203 			disable = 0;
204 			modified++;
205 			break;
206 		case 'D':
207 			disable = 1;
208 			enable = 0;
209 			modified++;
210 			break;
211 		case 'i':
212 			subopts = optarg;
213 			while (*subopts != '\0') {
214 				switch (getsubopt(&subopts, subopt_v,
215 				    &optval)) {
216 					case OPT_SCAN:
217 						if (optval == NULL ||
218 						    (scan_interval =
219 						    xatoi(optval)) <= 0)
220 							usage();
221 						break;
222 					case OPT_SAMPLE:
223 						if (optval == NULL ||
224 						    (sample_interval =
225 						    xatoi(optval)) <= 0)
226 							usage();
227 						break;
228 					case OPT_REPORT:
229 						if (optval == NULL ||
230 						    (report_interval =
231 						    xatoi(optval)) < 0)
232 							usage();
233 						break;
234 					case OPT_CONFIG:
235 						if (optval == NULL ||
236 						    (config_interval =
237 						    xatoi(optval)) < 0)
238 							usage();
239 						break;
240 					default:
241 						usage();
242 				}
243 			}
244 			modified++;
245 			break;
246 		case 'm':
247 			maxrss = optarg;
248 			break;
249 		case 'z':
250 			refresh = B_TRUE;
251 			zonename = optarg;
252 			break;
253 		default:
254 			usage();
255 		}
256 	}
257 
258 	/* the -z & -m options must be used together */
259 	if (argc > optind || (refresh && maxrss == NULL) ||
260 	    (!refresh && maxrss != NULL))
261 		usage();
262 
263 	if (refresh && (no_starting_stopping > 0 || modified))
264 		usage();
265 
266 	if (rcfg_read(fname, -1, &conf, NULL) < 0) {
267 		if (!(errno == ENOENT && modified)) {
268 			die(gettext("resource caps not configured\n"));
269 			return (E_ERROR);
270 		}
271 		rcfg_init(&conf);
272 		conf.rcfg_mode_name = "project";
273 	} else {
274 		/*
275 		 * The configuration file has been read.  Warn that any lnode
276 		 * (or non-project) mode specification (by an SRM
277 		 * 1.3 configuration file, for example) is ignored.
278 		 */
279 		if (strcmp(conf.rcfg_mode_name, "project") != 0) {
280 			warn(gettext("%s mode specification ignored -- using"
281 			    " project mode\n"), conf.rcfg_mode_name);
282 			conf.rcfg_mode_name = "project";
283 			conf.rcfg_mode = rctype_project;
284 		}
285 	}
286 
287 	if (refresh)
288 		return (update_zone_mcap(zonename, maxrss));
289 
290 	if (modified) {
291 		if (pressure >= 0)
292 			conf.rcfg_memory_cap_enforcement_pressure = pressure;
293 		if (config_interval >= 0)
294 			conf.rcfg_reconfiguration_interval = config_interval;
295 		if (scan_interval >= 0)
296 			conf.rcfg_proc_walk_interval = scan_interval;
297 		if (report_interval >= 0)
298 			conf.rcfg_report_interval = report_interval;
299 		if (sample_interval >= 0)
300 			conf.rcfg_rss_sample_interval = sample_interval;
301 
302 		/*
303 		 * Create config file with the new parameter(s). The
304 		 * create_config_file will exit if it fails.
305 		 */
306 		create_config_file(&conf);
307 
308 		if (enable > 0 && smf_enable_instance(RCAP_FMRI,
309 		    no_starting_stopping > 0 ? SMF_AT_NEXT_BOOT : 0) != 0)
310 			die(gettext("cannot enable service: %s\n"),
311 			    scf_strerror(scf_error()));
312 		else if (disable > 0 && smf_disable_instance(RCAP_FMRI,
313 		    no_starting_stopping > 0 ? SMF_AT_NEXT_BOOT : 0) != 0)
314 			die(gettext("cannot disable service: %s\n"),
315 			    scf_strerror(scf_error()));
316 
317 		return (E_SUCCESS);
318 	}
319 
320 	/*
321 	 * Display current configuration
322 	 */
323 	print_state();
324 	(void) printf(gettext("           memory cap enforcement"
325 	    " threshold: %d%%\n"), conf.rcfg_memory_cap_enforcement_pressure);
326 	(void) printf(gettext("                    process scan rate"
327 	    " (sec): %d\n"), conf.rcfg_proc_walk_interval);
328 	(void) printf(gettext("                 reconfiguration rate"
329 	    " (sec): %d\n"), conf.rcfg_reconfiguration_interval);
330 	(void) printf(gettext("                          report rate"
331 	    " (sec): %d\n"), conf.rcfg_report_interval);
332 	(void) printf(gettext("                    RSS sampling rate"
333 	    " (sec): %d\n"), conf.rcfg_rss_sample_interval);
334 
335 	return (E_SUCCESS);
336 }
337