xref: /illumos-gate/usr/src/cmd/acctadm/main.c (revision 257873cfc1dd3337766407f80397db60a56f2f5a)
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 2008 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/acctctl.h>
29 #include <assert.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include <errno.h>
35 #include <libintl.h>
36 #include <locale.h>
37 #include <priv.h>
38 #include <libscf.h>
39 #include <zone.h>
40 
41 #include "utils.h"
42 #include "aconf.h"
43 #include "res.h"
44 
45 static const char USAGE[] = "\
46 Usage:\n\
47     acctadm [ {process | task | flow} ]\n\
48     acctadm -s\n\
49     acctadm -r [ {process | task | flow} ]\n\
50     acctadm -x|-E|-D {process | task | flow}\n\
51     acctadm -f filename {process | task | flow}\n\
52     acctadm -e resources -d resources {process | task | flow}\n";
53 
54 static const char OPTS[] = "rsxf:e:d:ED";
55 
56 static void
57 usage()
58 {
59 	(void) fprintf(stderr, gettext(USAGE));
60 	exit(E_USAGE);
61 }
62 
63 static void
64 setup_privs()
65 {
66 	priv_set_t *privset;
67 
68 	if (seteuid(getuid()) == -1 || setegid(getgid()) == -1)
69 		die(gettext("seteuid()/setegid() failed"));
70 
71 	/*
72 	 * Add our privileges and remove unneeded 'basic' privileges from the
73 	 * permitted set.
74 	 */
75 	if ((privset = priv_str_to_set("basic", ",", NULL)) == NULL)
76 		die(gettext("cannot setup privileges"));
77 
78 	(void) priv_addset(privset, PRIV_SYS_ACCT);
79 	(void) priv_addset(privset, PRIV_FILE_DAC_WRITE);
80 	(void) priv_delset(privset, PRIV_FILE_LINK_ANY);
81 	(void) priv_delset(privset, PRIV_PROC_EXEC);
82 	(void) priv_delset(privset, PRIV_PROC_FORK);
83 	(void) priv_delset(privset, PRIV_PROC_INFO);
84 	(void) priv_delset(privset, PRIV_PROC_SESSION);
85 	priv_inverse(privset);
86 	if (setppriv(PRIV_OFF, PRIV_PERMITTED, privset) == -1)
87 		die(gettext("cannot setup privileges"));
88 	priv_freeset(privset);
89 
90 	/*
91 	 * Clear the Inheritable and Limit sets.
92 	 */
93 	if ((privset = priv_allocset()) == NULL)
94 		die(gettext("cannot setup privileges"));
95 	priv_emptyset(privset);
96 	if (setppriv(PRIV_SET, PRIV_INHERITABLE, privset) == -1 ||
97 	    setppriv(PRIV_SET, PRIV_LIMIT, privset) == -1)
98 		die(gettext("cannot setup privileges"));
99 
100 	/*
101 	 * Turn off the sys_acct and file_dac_write privileges until needed.
102 	 */
103 	(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_FILE_DAC_WRITE,
104 	    PRIV_SYS_ACCT, NULL);
105 }
106 
107 int
108 main(int argc, char *argv[])
109 {
110 	int c;			/* options character */
111 	int type = 0;		/* type of accounting */
112 	int modified = 0;	/* have we modified any properties? */
113 	acctconf_t ac;		/* current configuration */
114 	char *typestr = NULL;	/* type of accounting argument string */
115 	char *enabled = NULL;	/* enabled resources string */
116 	char *disabled = NULL;	/* disabled resources string */
117 	char *file = NULL;
118 	int Eflg = 0;
119 	int Dflg = 0;
120 	int rflg = 0;
121 	int sflg = 0;
122 	int xflg = 0;
123 	int optcnt = 0;
124 	int state;
125 	const char *fmri;	/* FMRI for this instance */
126 
127 	setup_privs();
128 
129 	(void) setlocale(LC_ALL, "");
130 	(void) textdomain(TEXT_DOMAIN);
131 	(void) setprogname(argv[0]);
132 
133 	for (; optind < argc; optind++) {
134 		while ((c = getopt(argc, argv, OPTS)) != (int)EOF) {
135 			switch (c) {
136 			case 'd':
137 				disabled = optarg;
138 				break;
139 			case 'e':
140 				enabled = optarg;
141 				break;
142 			case 'D':
143 				Dflg = 1;
144 				optcnt++;
145 				break;
146 			case 'E':
147 				Eflg = 1;
148 				optcnt++;
149 				break;
150 			case 'f':
151 				file = optarg;
152 				optcnt++;
153 				break;
154 			case 'r':
155 				rflg = 1;
156 				optcnt++;
157 				break;
158 			case 's':
159 				sflg = 1;
160 				optcnt++;
161 				break;
162 			case 'x':
163 				xflg = 1;
164 				optcnt++;
165 				break;
166 			case '?':
167 			default:
168 				usage();
169 			}
170 		}
171 
172 		/*
173 		 * Permanently give up euid 0, egid 0 and privileges we
174 		 * don't need for the specified options.
175 		 */
176 		if (!(file || sflg)) {
177 			if (setreuid(getuid(), getuid()) == -1 ||
178 			    setregid(getgid(), getgid()) == -1)
179 				die(gettext("setreuid()/setregid() failed"));
180 			(void) priv_set(PRIV_OFF, PRIV_PERMITTED,
181 			    PRIV_FILE_DAC_WRITE, NULL);
182 		}
183 		if (!(disabled || enabled || Dflg || Eflg || file || sflg ||
184 		    xflg))
185 			(void) priv_set(PRIV_OFF, PRIV_PERMITTED,
186 			    PRIV_SYS_ACCT, NULL);
187 
188 		if (optind < argc) {
189 			if (typestr != NULL) {
190 				warn(gettext("illegal argument -- %s\n"),
191 				    argv[optind]);
192 				usage();
193 			} else {
194 				typestr = argv[optind];
195 			}
196 		}
197 	}
198 	if (typestr != NULL) {
199 		if (strcmp(typestr, "process") == 0 ||
200 		    strcmp(typestr, "proc") == 0)
201 			type |= AC_PROC;
202 		else if (strcmp(typestr, "task") == 0)
203 			type |= AC_TASK;
204 		else if (strcmp(typestr, "flow") == 0)
205 			type |= AC_FLOW;
206 		else {
207 			warn(gettext("unknown accounting type -- %s\n"),
208 			    typestr);
209 			usage();
210 		}
211 	} else
212 		type = AC_PROC | AC_TASK | AC_FLOW;
213 
214 	/*
215 	 * check for invalid options
216 	 */
217 	if (optcnt > 1)
218 		usage();
219 
220 	if ((enabled || disabled) && (rflg || Dflg || sflg || xflg || Eflg))
221 		usage();
222 
223 	if ((file || xflg || Dflg || Eflg || enabled || disabled) &&
224 	    !typestr) {
225 		warn(gettext("accounting type must be specified\n"));
226 		usage();
227 	}
228 
229 	if (rflg) {
230 		printgroups(type);
231 		return (E_SUCCESS);
232 	}
233 
234 	/*
235 	 * If no arguments have been passed then just print out the current
236 	 * state and exit.
237 	 */
238 	if (!enabled && !disabled && !file &&
239 	    !Eflg && !rflg && !Dflg && !sflg && !xflg) {
240 		aconf_print(stdout, type);
241 		return (E_SUCCESS);
242 	}
243 
244 	/*
245 	 * smf(5) start method.  The FMRI to operate on is retrieved from the
246 	 * SMF_FMRI environment variable that the restarter provides.
247 	 */
248 	if (sflg) {
249 		if ((fmri = getenv("SMF_FMRI")) != NULL)
250 			return (aconf_setup(fmri));
251 
252 		warn(gettext("-s option should only be invoked by smf(5)\n"));
253 		return (E_ERROR);
254 	}
255 
256 	assert(type == AC_PROC || type == AC_TASK || type == AC_FLOW);
257 
258 	if (type == AC_FLOW && getzoneid() != GLOBAL_ZONEID)
259 		die(gettext("%s accounting cannot be configured in "
260 		    "non-global zones\n"), ac_type_name(type));
261 
262 	fmri = aconf_type2fmri(type);
263 	if (aconf_scf_init(fmri) == -1)
264 		die(gettext("cannot connect to repository for %s\n"), fmri);
265 
266 	/*
267 	 * Since the sys_acct the privilege allows use of acctctl() regardless
268 	 * of the accounting type, we check the smf(5) authorizations granted
269 	 * to the user to determine whether the user is allowed to change the
270 	 * configuration for this particular accounting type.
271 	 */
272 	if (!aconf_have_smf_auths())
273 		die(gettext("insufficient authorization to change %s extended "
274 		    "accounting configuration\n"), ac_type_name(type));
275 
276 	if (xflg) {
277 		/*
278 		 * Turn off the specified accounting and close its file
279 		 */
280 		state = AC_OFF;
281 
282 		(void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
283 		if (acctctl(type | AC_STATE_SET, &state, sizeof (int)) == -1)
284 			die(gettext("cannot disable %s accounting"),
285 			    ac_type_name(type));
286 		if (acctctl(type | AC_FILE_SET, NULL, 0) == -1)
287 			die(gettext("cannot close %s accounting file\n"),
288 			    ac_type_name(type));
289 		(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
290 
291 		if (aconf_set_bool(AC_PROP_STATE, B_FALSE) == -1)
292 			die(gettext("cannot update %s property\n"),
293 			    AC_PROP_STATE);
294 		if (aconf_set_string(AC_PROP_FILE, AC_STR_NONE) == -1)
295 			die(gettext("cannot update %s property\n"),
296 			    AC_PROP_FILE);
297 		modified++;
298 	}
299 
300 	if (enabled || disabled) {
301 		char *tracked, *untracked;
302 		ac_res_t *buf;
303 
304 		/*
305 		 * Enable/disable resources
306 		 */
307 		if ((buf = malloc(AC_BUFSIZE)) == NULL)
308 			die(gettext("not enough memory\n"));
309 		(void) memset(buf, 0, AC_BUFSIZE);
310 		if (acctctl(type | AC_RES_GET, buf, AC_BUFSIZE) == -1) {
311 			free(buf);
312 			die(gettext("cannot obtain list of resources\n"));
313 		}
314 		if (disabled)
315 			str2buf(buf, disabled, AC_OFF, type);
316 		if (enabled)
317 			str2buf(buf, enabled, AC_ON, type);
318 
319 		(void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
320 		if (acctctl(type | AC_RES_SET, buf, AC_BUFSIZE) == -1) {
321 			free(buf);
322 			die(gettext("cannot enable/disable %s accounting "
323 			    "resources\n"), ac_type_name(type));
324 		}
325 		(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
326 
327 		tracked = buf2str(buf, AC_BUFSIZE, AC_ON, type);
328 		untracked = buf2str(buf, AC_BUFSIZE, AC_OFF, type);
329 		if (aconf_set_string(AC_PROP_TRACKED, tracked) == -1)
330 			die(gettext("cannot update %s property\n"),
331 			    AC_PROP_TRACKED);
332 		if (aconf_set_string(AC_PROP_UNTRACKED, untracked) == -1)
333 			die(gettext("cannot update %s property\n"),
334 			    AC_PROP_UNTRACKED);
335 		free(tracked);
336 		free(untracked);
337 		free(buf);
338 		modified++;
339 	}
340 
341 	if (file) {
342 		/*
343 		 * Open new accounting file
344 		 */
345 		(void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
346 		if (open_exacct_file(file, type) == -1)
347 			exit(E_ERROR);
348 		if (aconf_set_string(AC_PROP_FILE, file) == -1)
349 			die(gettext("cannot update %s property\n"),
350 			    AC_PROP_FILE);
351 		state = AC_ON;
352 
353 		if (acctctl(type | AC_STATE_SET, &state, sizeof (int)) == -1)
354 			die(gettext("cannot enable %s accounting"),
355 			    ac_type_name(type));
356 		(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
357 
358 		if (aconf_set_bool(AC_PROP_STATE, B_TRUE) == -1)
359 			die(gettext("cannot update %s property\n"),
360 			    AC_PROP_STATE);
361 		modified++;
362 	}
363 
364 	if (Dflg) {
365 		/*
366 		 * Disable accounting
367 		 */
368 		state = AC_OFF;
369 
370 		(void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
371 		if (acctctl(type | AC_STATE_SET, &state, sizeof (int)) == -1)
372 			die(gettext("cannot disable %s accounting"),
373 			    ac_type_name(type));
374 		(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
375 
376 		if (aconf_set_bool(AC_PROP_STATE, B_FALSE) == -1)
377 			die(gettext("cannot update %s property\n"),
378 			    AC_PROP_STATE);
379 		modified++;
380 	}
381 
382 	if (Eflg) {
383 		/*
384 		 * Enable accounting
385 		 */
386 		state = AC_ON;
387 
388 		(void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
389 		if (acctctl(type | AC_STATE_SET, &state, sizeof (int)) == -1)
390 			die(gettext("cannot enable %s accounting"),
391 			    ac_type_name(type));
392 		(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
393 
394 		if (aconf_set_bool(AC_PROP_STATE, B_TRUE) == -1)
395 			die(gettext("cannot update %s property\n"),
396 			    AC_PROP_STATE);
397 		modified++;
398 	}
399 	(void) priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_SYS_ACCT, NULL);
400 
401 	if (modified) {
402 		char *smf_state;
403 
404 		if (aconf_save() == -1)
405 			die(gettext("cannot save %s accounting "
406 			    "configuration\n"), ac_type_name(type));
407 
408 		/*
409 		 * Enable or disable the instance depending on the effective
410 		 * configuration.  If the effective configuration results in
411 		 * extended accounting being 'on', the instance is enabled so
412 		 * the configuration is applied at the next boot.
413 		 */
414 		smf_state = smf_get_state(fmri);
415 		aconf_init(&ac, type);
416 
417 		if (ac.state == AC_ON ||
418 		    strcmp(ac.file, AC_STR_NONE) != 0 ||
419 		    strcmp(ac.tracked, AC_STR_NONE) != 0) {
420 			if (strcmp(smf_state, SCF_STATE_STRING_ONLINE) != 0)
421 				if (smf_enable_instance(fmri, 0) == -1)
422 					die(gettext("cannot enable %s\n"),
423 					    fmri);
424 		} else {
425 			if (strcmp(smf_state, SCF_STATE_STRING_ONLINE) == 0)
426 				if (smf_disable_instance(fmri, 0) == -1)
427 					die(gettext("cannot disable %s\n"),
428 					    fmri);
429 		}
430 		free(smf_state);
431 	}
432 	aconf_scf_fini();
433 	return (E_SUCCESS);
434 }
435