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