Home | History | Annotate | Download | only in acctadm
      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 2009 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 		}
    379 		if (enabled) {
    380 			/*
    381 			 * Lets us get network logging started.
    382 			 */
    383 			if (type & AC_NET) {
    384 				/*
    385 				 * Default logging interval for AC_NET is
    386 				 * ACCTADM_NET_LOG_INTERVAL.
    387 				 */
    388 				(void) priv_set(PRIV_ON, PRIV_EFFECTIVE,
    389 				    PRIV_SYS_DL_CONFIG, NULL);
    390 				err = dladm_start_usagelog(dld_handle,
    391 				    strcmp(enabled, "basic") == 0 ?
    392 				    DLADM_LOGTYPE_LINK : DLADM_LOGTYPE_FLOW,
    393 				    ACCTADM_NET_LOG_INTERVAL);
    394 				(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE,
    395 				    PRIV_SYS_DL_CONFIG, NULL);
    396 				if (err != DLADM_STATUS_OK) {
    397 					die(gettext("failed to start logging "
    398 					    "network information, error %d\n"),
    399 					    errno);
    400 				}
    401 			}
    402 			str2buf(buf, enabled, AC_ON, type);
    403 		}
    404 		(void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
    405 		if (acctctl(type | AC_RES_SET, buf, AC_BUFSIZE) == -1) {
    406 			free(buf);
    407 			die(gettext("cannot enable/disable %s accounting "
    408 			    "resources\n"), ac_type_name(type));
    409 		}
    410 		(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
    411 
    412 		tracked = buf2str(buf, AC_BUFSIZE, AC_ON, type);
    413 		untracked = buf2str(buf, AC_BUFSIZE, AC_OFF, type);
    414 		if (aconf_set_string(AC_PROP_TRACKED, tracked) == -1)
    415 			die(gettext("cannot update %s property\n"),
    416 			    AC_PROP_TRACKED);
    417 		if (aconf_set_string(AC_PROP_UNTRACKED, untracked) == -1)
    418 			die(gettext("cannot update %s property\n"),
    419 			    AC_PROP_UNTRACKED);
    420 		free(tracked);
    421 		free(untracked);
    422 		free(buf);
    423 		modified++;
    424 	}
    425 
    426 	if (file) {
    427 		/*
    428 		 * Open new accounting file
    429 		 */
    430 		(void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
    431 		if (open_exacct_file(file, type) == -1) {
    432 			dladm_close(dld_handle);
    433 			exit(E_ERROR);
    434 		}
    435 		if (aconf_set_string(AC_PROP_FILE, file) == -1)
    436 			die(gettext("cannot update %s property\n"),
    437 			    AC_PROP_FILE);
    438 		state = AC_ON;
    439 
    440 		if (acctctl(type | AC_STATE_SET, &state, sizeof (int)) == -1)
    441 			die(gettext("cannot enable %s accounting"),
    442 			    ac_type_name(type));
    443 		(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
    444 
    445 		if (aconf_set_bool(AC_PROP_STATE, B_TRUE) == -1)
    446 			die(gettext("cannot update %s property\n"),
    447 			    AC_PROP_STATE);
    448 		modified++;
    449 	}
    450 
    451 	if (Dflg) {
    452 		/*
    453 		 * Disable accounting
    454 		 */
    455 
    456 		/*
    457 		 * Stop net logging before turning it off so that the last
    458 		 * set of logs can be written.
    459 		 */
    460 		if (type & AC_NET) {
    461 			(void) priv_set(PRIV_ON, PRIV_EFFECTIVE,
    462 			    PRIV_SYS_DL_CONFIG, NULL);
    463 			err = dladm_stop_usagelog(dld_handle,
    464 			    DLADM_LOGTYPE_FLOW);
    465 			(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE,
    466 			    PRIV_SYS_DL_CONFIG, NULL);
    467 			if (err != DLADM_STATUS_OK) {
    468 				die(gettext("failed to stop logging "
    469 				    "network information, error %d\n"), errno);
    470 			}
    471 		}
    472 		state = AC_OFF;
    473 
    474 		(void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
    475 		if (acctctl(type | AC_STATE_SET, &state, sizeof (int)) == -1)
    476 			die(gettext("cannot disable %s accounting"),
    477 			    ac_type_name(type));
    478 		(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
    479 
    480 		if (aconf_set_bool(AC_PROP_STATE, B_FALSE) == -1)
    481 			die(gettext("cannot update %s property\n"),
    482 			    AC_PROP_STATE);
    483 		modified++;
    484 	}
    485 
    486 	if (Eflg) {
    487 		/*
    488 		 * Enable accounting
    489 		 */
    490 
    491 		/*
    492 		 * Let's get network logging started.
    493 		 */
    494 		if (type & AC_NET) {
    495 			/*
    496 			 * Default logging interval for AC_NET is
    497 			 * ACCTADM_NET_LOG_INTERVAL.
    498 			 */
    499 			(void) priv_set(PRIV_ON, PRIV_EFFECTIVE,
    500 			    PRIV_SYS_DL_CONFIG, NULL);
    501 			err = dladm_start_usagelog(dld_handle,
    502 			    DLADM_LOGTYPE_FLOW, ACCTADM_NET_LOG_INTERVAL);
    503 			(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE,
    504 			    PRIV_SYS_DL_CONFIG, NULL);
    505 			if (err != DLADM_STATUS_OK) {
    506 				die(gettext("failed to start logging "
    507 				    "network information, error %d\n"), errno);
    508 			}
    509 		}
    510 		state = AC_ON;
    511 
    512 		(void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
    513 		if (acctctl(type | AC_STATE_SET, &state, sizeof (int)) == -1)
    514 			die(gettext("cannot enable %s accounting"),
    515 			    ac_type_name(type));
    516 		(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
    517 
    518 		if (aconf_set_bool(AC_PROP_STATE, B_TRUE) == -1)
    519 			die(gettext("cannot update %s property\n"),
    520 			    AC_PROP_STATE);
    521 		modified++;
    522 	}
    523 	(void) priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_SYS_ACCT, NULL);
    524 
    525 	if (modified) {
    526 		char *smf_state;
    527 
    528 		if (aconf_save() == -1)
    529 			die(gettext("cannot save %s accounting "
    530 			    "configuration\n"), ac_type_name(type));
    531 
    532 		/*
    533 		 * Enable or disable the instance depending on the effective
    534 		 * configuration.  If the effective configuration results in
    535 		 * extended accounting being 'on', the instance is enabled so
    536 		 * the configuration is applied at the next boot.
    537 		 */
    538 		smf_state = smf_get_state(fmri);
    539 		aconf_init(&ac, type);
    540 
    541 		if (ac.state == AC_ON ||
    542 		    strcmp(ac.file, AC_STR_NONE) != 0 ||
    543 		    strcmp(ac.tracked, AC_STR_NONE) != 0) {
    544 			if (strcmp(smf_state, SCF_STATE_STRING_ONLINE) != 0)
    545 				if (smf_enable_instance(fmri, 0) == -1)
    546 					die(gettext("cannot enable %s\n"),
    547 					    fmri);
    548 		} else {
    549 			if (strcmp(smf_state, SCF_STATE_STRING_ONLINE) == 0)
    550 				if (smf_disable_instance(fmri, 0) == -1)
    551 					die(gettext("cannot disable %s\n"),
    552 					    fmri);
    553 		}
    554 		free(smf_state);
    555 	}
    556 	aconf_scf_fini();
    557 	dladm_close(dld_handle);
    558 	return (E_SUCCESS);
    559 }
    560