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