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