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 2008 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include <sys/types.h>
     27 #include <sys/acctctl.h>
     28 #include <unistd.h>
     29 #include <string.h>
     30 #include <stdlib.h>
     31 #include <errno.h>
     32 #include <limits.h>
     33 #include <libdllink.h>
     34 #include <libscf.h>
     35 #include <pwd.h>
     36 #include <auth_attr.h>
     37 #include <nss_dbdefs.h>
     38 #include <secdb.h>
     39 #include <priv.h>
     40 #include <zone.h>
     41 
     42 #include "aconf.h"
     43 #include "utils.h"
     44 #include "res.h"
     45 
     46 #define	FMRI_FLOW_ACCT	"svc:/system/extended-accounting:flow"
     47 #define	FMRI_PROC_ACCT	"svc:/system/extended-accounting:process"
     48 #define	FMRI_TASK_ACCT	"svc:/system/extended-accounting:task"
     49 #define	FMRI_NET_ACCT	"svc:/system/extended-accounting:net"
     50 
     51 #define	NELEM(x)	(sizeof (x)) / (sizeof (x[0]))
     52 
     53 typedef struct props {
     54 	char *propname;
     55 	int proptype;
     56 	scf_transaction_entry_t *entry;
     57 	scf_value_t *value;
     58 	struct props *next;
     59 } props_t;
     60 
     61 static void	aconf_print_type(acctconf_t *, FILE *, int);
     62 static int	aconf_get_bool(const char *, const char *, uint8_t *);
     63 static int	aconf_get_string(const char *, const char *, char *, size_t);
     64 static props_t	*aconf_prop(const char *, int);
     65 static int	aconf_fmri2type(const char *);
     66 
     67 static scf_handle_t	*handle = NULL;
     68 static scf_instance_t	*inst = NULL;
     69 static props_t		*props = NULL;
     70 
     71 void
     72 aconf_init(acctconf_t *acp, int type)
     73 {
     74 	void *buf;
     75 	char *tracked;
     76 	char *untracked;
     77 
     78 	if ((buf = malloc(AC_BUFSIZE)) == NULL)
     79 		die(gettext("not enough memory\n"));
     80 
     81 	if (acctctl(type | AC_STATE_GET, &acp->state,
     82 	    sizeof (acp->state)) == -1)
     83 		die(gettext("cannot get %s accounting state\n"),
     84 		    ac_type_name(type));
     85 
     86 	(void) memset(acp->file, 0, sizeof (acp->file));
     87 	if (acctctl(type | AC_FILE_GET, acp->file, sizeof (acp->file)) == -1) {
     88 		if (errno == ENOTACTIVE)
     89 			(void) strlcpy(acp->file, AC_STR_NONE,
     90 			    sizeof (acp->file));
     91 		else
     92 			die(gettext("cannot get %s accounting file name"),
     93 			    ac_type_name(type));
     94 	}
     95 	(void) memset(buf, 0, AC_BUFSIZE);
     96 	if (acctctl(type | AC_RES_GET, buf, AC_BUFSIZE) == -1)
     97 		die(gettext("cannot obtain the list of enabled resources\n"));
     98 
     99 	tracked = buf2str(buf, AC_BUFSIZE, AC_ON, type);
    100 	untracked = buf2str(buf, AC_BUFSIZE, AC_OFF, type);
    101 	(void) strlcpy(acp->tracked, tracked, sizeof (acp->tracked));
    102 	(void) strlcpy(acp->untracked, untracked, sizeof (acp->untracked));
    103 	free(tracked);
    104 	free(untracked);
    105 	free(buf);
    106 }
    107 
    108 /*
    109  * SMF start method: configure extended accounting from properties stored in
    110  * the repository.  Any errors encountered while retrieving properties from
    111  * the repository, such as missing properties or properties of the wrong type,
    112  * are fatal as they indicate severe damage to the service (all required
    113  * properties are delivered in the service manifest and should thus always be
    114  * present).  No attempts will be made to repair such damage;  the service will
    115  * be forced into maintenance state by returning SMF_EXIT_ERR_CONFIG.  For all
    116  * other errors we we try to configure as much as possible and return
    117  * SMF_EXIT_ERR_FATAL.
    118  */
    119 int
    120 aconf_setup(const char *fmri)
    121 {
    122 	char file[MAXPATHLEN];
    123 	char tracked[MAXRESLEN];
    124 	char untracked[MAXRESLEN];
    125 	void *buf;
    126 	int type;
    127 	int state;
    128 	uint8_t b;
    129 	int ret = SMF_EXIT_OK;
    130 
    131 	if ((type = aconf_fmri2type(fmri)) == -1) {
    132 		warn(gettext("no accounting type for %s\n"), fmri);
    133 		return (SMF_EXIT_ERR_FATAL);
    134 	}
    135 
    136 	/*
    137 	 * Net/Flow accounting is not available in non-global zones and
    138 	 * the service instance should therefore never be 'enabled' in
    139 	 * non-global zones.  This is enforced by acctadm(1M), but there is
    140 	 * nothing that prevents someone from calling svcadm enable directly,
    141 	 * so we handle that case here by disabling the instance.
    142 	 */
    143 	if ((type == AC_FLOW || type == AC_NET) &&
    144 	    getzoneid() != GLOBAL_ZONEID) {
    145 		(void) smf_disable_instance(fmri, 0);
    146 		warn(gettext("%s accounting cannot be configured in "
    147 		    "non-global zones\n"), ac_type_name(type));
    148 		return (SMF_EXIT_OK);
    149 	}
    150 
    151 	if (aconf_scf_init(fmri) == -1) {
    152 		warn(gettext("cannot connect to repository\n"));
    153 		return (SMF_EXIT_ERR_FATAL);
    154 	}
    155 	if (aconf_get_string(AC_PGNAME, AC_PROP_TRACKED, tracked,
    156 	    sizeof (tracked)) == -1) {
    157 		warn(gettext("cannot get %s property\n"), AC_PROP_TRACKED);
    158 		ret = SMF_EXIT_ERR_CONFIG;
    159 		goto out;
    160 	}
    161 	if (aconf_get_string(AC_PGNAME, AC_PROP_UNTRACKED, untracked,
    162 	    sizeof (untracked)) == -1) {
    163 		warn(gettext("cannot get %s property\n"), AC_PROP_UNTRACKED);
    164 		ret = SMF_EXIT_ERR_CONFIG;
    165 		goto out;
    166 	}
    167 	if (aconf_get_string(AC_PGNAME, AC_PROP_FILE, file,
    168 	    sizeof (file)) == -1) {
    169 		warn(gettext("cannot get %s property\n"), AC_PROP_FILE);
    170 		ret = SMF_EXIT_ERR_CONFIG;
    171 		goto out;
    172 	}
    173 	if (aconf_get_bool(AC_PGNAME, AC_PROP_STATE, &b) == -1) {
    174 		warn(gettext("cannot get %s property\n"), AC_PROP_STATE);
    175 		ret = SMF_EXIT_ERR_CONFIG;
    176 		goto out;
    177 	}
    178 	state = (b ? AC_ON : AC_OFF);
    179 
    180 	if ((buf = malloc(AC_BUFSIZE)) == NULL) {
    181 		warn(gettext("not enough memory\n"));
    182 		ret = SMF_EXIT_ERR_FATAL;
    183 		goto out;
    184 	}
    185 	(void) memset(buf, 0, AC_BUFSIZE);
    186 	str2buf(buf, untracked, AC_OFF, type);
    187 	str2buf(buf, tracked, AC_ON, type);
    188 
    189 	(void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
    190 	if (acctctl(type | AC_RES_SET, buf, AC_BUFSIZE) == -1) {
    191 		warn(gettext("cannot enable/disable %s accounting resources"),
    192 		    ac_type_name(type));
    193 		ret = SMF_EXIT_ERR_FATAL;
    194 	}
    195 	free(buf);
    196 
    197 	if (strcmp(file, AC_STR_NONE) != 0) {
    198 		if (open_exacct_file(file, type) == -1)
    199 			ret = SMF_EXIT_ERR_FATAL;
    200 	} else {
    201 		if (acctctl(type | AC_FILE_SET, NULL, 0) == -1) {
    202 			warn(gettext("cannot close %s accounting file"),
    203 			    ac_type_name(type));
    204 			ret = SMF_EXIT_ERR_FATAL;
    205 		}
    206 	}
    207 	if (acctctl(type | AC_STATE_SET, &state, sizeof (state)) == -1) {
    208 		warn(gettext("cannot %s %s accounting"),
    209 		    state == AC_ON ? gettext("enable") : gettext("disable"),
    210 		    ac_type_name(type));
    211 		ret = SMF_EXIT_ERR_FATAL;
    212 	}
    213 	(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
    214 
    215 	if (state == AC_ON && type == AC_NET) {
    216 		/*
    217 		 * Start logging.
    218 		 */
    219 		(void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_DL_CONFIG,
    220 		    NULL);
    221 		(void) dladm_start_usagelog(dld_handle,
    222 		    strncmp(tracked, "basic", strlen("basic")) == 0 ?
    223 		    DLADM_LOGTYPE_LINK : DLADM_LOGTYPE_FLOW, 20);
    224 		(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_DL_CONFIG,
    225 		    NULL);
    226 	}
    227 out:
    228 	aconf_scf_fini();
    229 	return (ret);
    230 }
    231 
    232 void
    233 aconf_print(FILE *fp, int types)
    234 {
    235 	acctconf_t ac;
    236 	int print_order[] = { AC_TASK, AC_PROC, AC_FLOW, AC_NET };
    237 	int i;
    238 
    239 	for (i = 0; i < NELEM(print_order); i++) {
    240 		if (types & print_order[i]) {
    241 			aconf_init(&ac, print_order[i]);
    242 			aconf_print_type(&ac, fp, print_order[i]);
    243 		}
    244 	}
    245 }
    246 
    247 static void
    248 aconf_print_type(acctconf_t *acp, FILE *fp, int type)
    249 {
    250 	switch (type) {
    251 	case AC_TASK:
    252 		(void) fprintf(fp,
    253 		    gettext("            Task accounting: %s\n"),
    254 		    acp->state == AC_ON ?
    255 		    gettext("active") : gettext("inactive"));
    256 		(void) fprintf(fp,
    257 		    gettext("       Task accounting file: %s\n"),
    258 		    acp->file);
    259 		(void) fprintf(fp,
    260 		    gettext("     Tracked task resources: %s\n"),
    261 		    acp->tracked);
    262 		(void) fprintf(fp,
    263 		    gettext("   Untracked task resources: %s\n"),
    264 		    acp->untracked);
    265 		break;
    266 	case AC_PROC:
    267 		(void) fprintf(fp,
    268 		    gettext("         Process accounting: %s\n"),
    269 		    acp->state == AC_ON ?
    270 		    gettext("active") : gettext("inactive"));
    271 		(void) fprintf(fp,
    272 		    gettext("    Process accounting file: %s\n"),
    273 		    acp->file);
    274 		(void) fprintf(fp,
    275 		    gettext("  Tracked process resources: %s\n"),
    276 		    acp->tracked);
    277 		(void) fprintf(fp,
    278 		    gettext("Untracked process resources: %s\n"),
    279 		    acp->untracked);
    280 		break;
    281 	case AC_FLOW:
    282 		(void) fprintf(fp,
    283 		    gettext("            Flow accounting: %s\n"),
    284 		    acp->state == AC_ON ?
    285 		    gettext("active") : gettext("inactive"));
    286 		(void) fprintf(fp,
    287 		    gettext("       Flow accounting file: %s\n"),
    288 		    acp->file);
    289 		(void) fprintf(fp,
    290 		    gettext("     Tracked flow resources: %s\n"),
    291 		    acp->tracked);
    292 		(void) fprintf(fp,
    293 		    gettext("   Untracked flow resources: %s\n"),
    294 		    acp->untracked);
    295 		break;
    296 	case AC_NET:
    297 		(void) fprintf(fp,
    298 		    gettext("            Net accounting: %s\n"),
    299 		    acp->state == AC_ON ?
    300 		    gettext("active") : gettext("inactive"));
    301 		(void) fprintf(fp,
    302 		    gettext("       Net accounting file: %s\n"),
    303 		    acp->file);
    304 		(void) fprintf(fp,
    305 		    gettext("     Tracked net resources: %s\n"),
    306 		    acp->tracked);
    307 		(void) fprintf(fp,
    308 		    gettext("   Untracked net resources: %s\n"),
    309 		    acp->untracked);
    310 		break;
    311 	}
    312 }
    313 
    314 /*
    315  * Modified properties are put on the 'props' linked list by aconf_set_string()
    316  * and aconf_set_bool().  Walk the list of modified properties and write them
    317  * to the repository.  The list is deleted on exit.
    318  */
    319 int
    320 aconf_save(void)
    321 {
    322 	scf_propertygroup_t *pg;
    323 	scf_transaction_t *tx;
    324 	props_t *p;
    325 	props_t *q;
    326 	int tx_result;
    327 
    328 	if (props == NULL)
    329 		return (0);
    330 
    331 	if ((pg = scf_pg_create(handle)) == NULL ||
    332 	    scf_instance_get_pg(inst, AC_PGNAME, pg) == -1 ||
    333 	    (tx = scf_transaction_create(handle)) == NULL)
    334 		goto out;
    335 
    336 	do {
    337 		if (scf_pg_update(pg) == -1 ||
    338 		    scf_transaction_start(tx, pg) == -1)
    339 			goto out;
    340 
    341 		for (p = props; p != NULL; p = p->next) {
    342 			if (scf_transaction_property_change(tx, p->entry,
    343 			    p->propname, p->proptype) == -1)
    344 				goto out;
    345 			(void) scf_entry_add_value(p->entry, p->value);
    346 		}
    347 		tx_result = scf_transaction_commit(tx);
    348 		scf_transaction_reset(tx);
    349 	} while (tx_result == 0);
    350 
    351 out:
    352 	p = props;
    353 	while (p != NULL) {
    354 		scf_value_destroy(p->value);
    355 		scf_entry_destroy(p->entry);
    356 		free(p->propname);
    357 		q = p->next;
    358 		free(p);
    359 		p = q;
    360 	}
    361 	props = NULL;
    362 	scf_transaction_destroy(tx);
    363 	scf_pg_destroy(pg);
    364 	return ((tx_result == 1) ? 0 : -1);
    365 }
    366 
    367 boolean_t
    368 aconf_have_smf_auths(void)
    369 {
    370 	char auth[NSS_BUFLEN_AUTHATTR];
    371 	struct passwd *pw;
    372 
    373 	if ((pw = getpwuid(getuid())) == NULL)
    374 		return (B_FALSE);
    375 
    376 	if (aconf_get_string("general", "action_authorization", auth,
    377 	    sizeof (auth)) == -1 || chkauthattr(auth, pw->pw_name) == 0)
    378 		return (B_FALSE);
    379 
    380 	if (aconf_get_string("general", "value_authorization", auth,
    381 	    sizeof (auth)) == -1 || chkauthattr(auth, pw->pw_name) == 0)
    382 		return (B_FALSE);
    383 
    384 	if (aconf_get_string("config", "value_authorization", auth,
    385 	    sizeof (auth)) == -1 || chkauthattr(auth, pw->pw_name) == 0)
    386 		return (B_FALSE);
    387 
    388 	return (B_TRUE);
    389 }
    390 
    391 const char *
    392 aconf_type2fmri(int type)
    393 {
    394 	switch (type) {
    395 	case AC_PROC:
    396 		return (FMRI_PROC_ACCT);
    397 	case AC_TASK:
    398 		return (FMRI_TASK_ACCT);
    399 	case AC_FLOW:
    400 		return (FMRI_FLOW_ACCT);
    401 	case AC_NET:
    402 		return (FMRI_NET_ACCT);
    403 	default:
    404 		die(gettext("invalid type %d\n"), type);
    405 	}
    406 	/* NOTREACHED */
    407 	return (NULL);
    408 }
    409 
    410 static int
    411 aconf_fmri2type(const char *fmri)
    412 {
    413 	if (strcmp(fmri, FMRI_PROC_ACCT) == 0)
    414 		return (AC_PROC);
    415 	else if (strcmp(fmri, FMRI_TASK_ACCT) == 0)
    416 		return (AC_TASK);
    417 	else if (strcmp(fmri, FMRI_FLOW_ACCT) == 0)
    418 		return (AC_FLOW);
    419 	else if (strcmp(fmri, FMRI_NET_ACCT) == 0)
    420 		return (AC_NET);
    421 	else
    422 		return (-1);
    423 }
    424 
    425 int
    426 aconf_scf_init(const char *fmri)
    427 {
    428 	if ((handle = scf_handle_create(SCF_VERSION)) == NULL ||
    429 	    scf_handle_bind(handle) == -1 ||
    430 	    (inst = scf_instance_create(handle)) == NULL ||
    431 	    scf_handle_decode_fmri(handle, fmri, NULL, NULL, inst, NULL, NULL,
    432 	    SCF_DECODE_FMRI_EXACT) == -1) {
    433 		aconf_scf_fini();
    434 		return (-1);
    435 	}
    436 	return (0);
    437 }
    438 
    439 void
    440 aconf_scf_fini(void)
    441 {
    442 	scf_instance_destroy(inst);
    443 	(void) scf_handle_unbind(handle);
    444 	scf_handle_destroy(handle);
    445 }
    446 
    447 static int
    448 aconf_get_string(const char *pgname, const char *propname, char *buf,
    449     size_t len)
    450 {
    451 	scf_propertygroup_t *pg;
    452 	scf_property_t *prop;
    453 	scf_value_t *value;
    454 	int ret = 0;
    455 
    456 	if ((pg = scf_pg_create(handle)) == NULL)
    457 		return (-1);
    458 
    459 	if (scf_instance_get_pg_composed(inst, NULL, pgname, pg) == -1) {
    460 		scf_pg_destroy(pg);
    461 		return (-1);
    462 	}
    463 
    464 	if ((prop = scf_property_create(handle)) == NULL ||
    465 	    (value = scf_value_create(handle)) == NULL ||
    466 	    scf_pg_get_property(pg, propname, prop) == -1 ||
    467 	    scf_property_get_value(prop, value) == -1 ||
    468 	    scf_value_get_astring(value, buf, len) == -1)
    469 		ret = -1;
    470 
    471 	scf_value_destroy(value);
    472 	scf_property_destroy(prop);
    473 	scf_pg_destroy(pg);
    474 	return (ret);
    475 }
    476 
    477 static int
    478 aconf_get_bool(const char *pgname, const char *propname, uint8_t *rval)
    479 {
    480 	scf_propertygroup_t *pg;
    481 	scf_property_t *prop;
    482 	scf_value_t *value;
    483 	int ret = 0;
    484 
    485 	if ((pg = scf_pg_create(handle)) == NULL)
    486 		return (-1);
    487 
    488 	if (scf_instance_get_pg_composed(inst, NULL, pgname, pg) == -1) {
    489 		scf_pg_destroy(pg);
    490 		return (-1);
    491 	}
    492 
    493 	if ((prop = scf_property_create(handle)) == NULL ||
    494 	    (value = scf_value_create(handle)) == NULL ||
    495 	    scf_pg_get_property(pg, propname, prop) == -1 ||
    496 	    scf_property_get_value(prop, value) == -1 ||
    497 	    scf_value_get_boolean(value, rval) == -1)
    498 		ret = -1;
    499 
    500 	scf_value_destroy(value);
    501 	scf_property_destroy(prop);
    502 	scf_pg_destroy(pg);
    503 	return (ret);
    504 }
    505 
    506 int
    507 aconf_set_string(const char *propname, const char *value)
    508 {
    509 	props_t *p;
    510 
    511 	if ((p = aconf_prop(propname, SCF_TYPE_ASTRING)) == NULL)
    512 		return (-1);
    513 
    514 	if (scf_value_set_astring(p->value, value) == -1)
    515 		return (-1);
    516 	return (0);
    517 }
    518 
    519 int
    520 aconf_set_bool(const char *propname, boolean_t value)
    521 {
    522 	props_t *p;
    523 
    524 	if ((p = aconf_prop(propname, SCF_TYPE_BOOLEAN)) == NULL)
    525 		return (-1);
    526 
    527 	scf_value_set_boolean(p->value, value);
    528 	return (0);
    529 }
    530 
    531 static props_t *
    532 aconf_prop(const char *propname, int proptype)
    533 {
    534 	props_t *p;
    535 
    536 	if ((p = malloc(sizeof (props_t))) != NULL) {
    537 		if ((p->propname = strdup(propname)) == NULL) {
    538 			free(p);
    539 			return (NULL);
    540 		}
    541 		if ((p->entry = scf_entry_create(handle)) == NULL) {
    542 			free(p->propname);
    543 			free(p);
    544 			return (NULL);
    545 		}
    546 		if ((p->value = scf_value_create(handle)) == NULL) {
    547 			scf_entry_destroy(p->entry);
    548 			free(p->propname);
    549 			free(p);
    550 			return (NULL);
    551 		}
    552 		p->proptype = proptype;
    553 		p->next = props;
    554 		props = p;
    555 	}
    556 	return (p);
    557 }
    558