# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # PROG = acctadm MANIFEST = extended-accounting.xml OBJS = main.o aconf.o res.o utils.o SRCS = $(OBJS:.o=.c) POFILES = $(OBJS:.o=.po) include ../Makefile.cmd ROOTMANIFESTDIR = $(ROOTSVCSYSTEM) CFLAGS += $(CCVERBOSE) CERRWARN += $(CNOWARN_UNINIT) CERRWARN += -Wno-parentheses LDLIBS += -lexacct -lscf -lsecdb -ldladm POFILE = acctadm.po XGETFLAGS = -a -x acctadm.xcl FILEMODE = 0555 .KEEP_STATE: all: $(PROG) $(PROG): $(OBJS) $(LINK.c) -o $@ $(OBJS) $(LDLIBS) $(POST_PROCESS) install: all $(ROOTUSRSBINPROG) $(ROOTMANIFEST) check: $(CHKMANIFEST) $(POFILE): $(POFILES) $(RM) $@ $(CAT) $(POFILES) > $@ clean: $(RM) $(OBJS) lint: lint_SRCS include ../Makefile.targ # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # msgid "ruxf:e:d:" msgid "/etc/acctadm.conf" msgid "" msgid "process" msgid "proc" msgid "task" msgid "flow" msgid "net" msgid "no" msgid "none" msgid "yes" msgid "ACCTADM_PROC_ENABLE" msgid "ACCTADM_PROC_FILE" msgid "ACCTADM_PROC_TRACKED" msgid "ACCTADM_PROC_UNTRACKED" msgid "ACCTADM_TASK_ENABLE" msgid "ACCTADM_TASK_FILE" msgid "ACCTADM_TASK_TRACKED" msgid "ACCTADM_TASK_UNTRACKED" msgid "ACCTADM_FLOW_ENABLE" msgid "ACCTADM_FLOW_FILE" msgid "ACCTADM_FLOW_TRACKED" msgid "ACCTADM_FLOW_UNTRACKED" msgid "ACCTADM_NET_ENABLE" msgid "ACCTADM_NET_FILE" msgid "ACCTADM_NET_TRACKED" msgid "ACCTADM_NET_UNTRACKED" msgid "r+" msgid "r" msgid " %[^=]=%s \n%n" msgid "\"%s\", line %d: " "warning: invalid %s\n" msgid "\"%s\", line %d: warning: " "invalid token: %s\n" msgid "\"%s\", line %d: syntax error\n" msgid "%s=" msgid "active" msgid "inactive" msgid "%s\n" msgid "pid" msgid "uid" msgid "gid" msgid "projid" msgid "taskid" msgid "cpu" msgid "time" msgid "command" msgid "tty" msgid "host" msgid "extended" msgid "basic" msgid "mstate" msgid "saddr" msgid "daddr" msgid "sport" msgid "dport" msgid "proto" msgid "dsfield" msgid "nbytes" msgid "npkts" msgid "ctime" msgid "lseen" msgid "%-10s" msgid "%s" msgid ",%s" msgid "\n" msgid ", " msgid "," msgid "%s: " msgid ": %s\n" /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "aconf.h" #include "utils.h" #include "res.h" #define FMRI_FLOW_ACCT "svc:/system/extended-accounting:flow" #define FMRI_PROC_ACCT "svc:/system/extended-accounting:process" #define FMRI_TASK_ACCT "svc:/system/extended-accounting:task" #define FMRI_NET_ACCT "svc:/system/extended-accounting:net" #define NELEM(x) (sizeof (x)) / (sizeof (x[0])) typedef struct props { char *propname; int proptype; scf_transaction_entry_t *entry; scf_value_t *value; struct props *next; } props_t; static void aconf_print_type(acctconf_t *, FILE *, int); static int aconf_get_bool(const char *, const char *, uint8_t *); static int aconf_get_string(const char *, const char *, char *, size_t); static props_t *aconf_prop(const char *, int); static int aconf_fmri2type(const char *); static scf_handle_t *handle = NULL; static scf_instance_t *inst = NULL; static props_t *props = NULL; void aconf_init(acctconf_t *acp, int type) { void *buf; char *tracked; char *untracked; if ((buf = malloc(AC_BUFSIZE)) == NULL) die(gettext("not enough memory\n")); if (acctctl(type | AC_STATE_GET, &acp->state, sizeof (acp->state)) == -1) die(gettext("cannot get %s accounting state\n"), ac_type_name(type)); (void) memset(acp->file, 0, sizeof (acp->file)); if (acctctl(type | AC_FILE_GET, acp->file, sizeof (acp->file)) == -1) { if (errno == ENOTACTIVE) (void) strlcpy(acp->file, AC_STR_NONE, sizeof (acp->file)); else die(gettext("cannot get %s accounting file name"), ac_type_name(type)); } (void) memset(buf, 0, AC_BUFSIZE); if (acctctl(type | AC_RES_GET, buf, AC_BUFSIZE) == -1) die(gettext("cannot obtain the list of enabled resources\n")); tracked = buf2str(buf, AC_BUFSIZE, AC_ON, type); untracked = buf2str(buf, AC_BUFSIZE, AC_OFF, type); (void) strlcpy(acp->tracked, tracked, sizeof (acp->tracked)); (void) strlcpy(acp->untracked, untracked, sizeof (acp->untracked)); free(tracked); free(untracked); free(buf); } /* * SMF start method: configure extended accounting from properties stored in * the repository. Any errors encountered while retrieving properties from * the repository, such as missing properties or properties of the wrong type, * are fatal as they indicate severe damage to the service (all required * properties are delivered in the service manifest and should thus always be * present). No attempts will be made to repair such damage; the service will * be forced into maintenance state by returning SMF_EXIT_ERR_CONFIG. For all * other errors we we try to configure as much as possible and return * SMF_EXIT_ERR_FATAL. */ int aconf_setup(const char *fmri) { char file[MAXPATHLEN]; char tracked[MAXRESLEN]; char untracked[MAXRESLEN]; void *buf; int type; int state; uint8_t b; int ret = SMF_EXIT_OK; if ((type = aconf_fmri2type(fmri)) == -1) { warn(gettext("no accounting type for %s\n"), fmri); return (SMF_EXIT_ERR_FATAL); } /* * Net/Flow accounting is not available in non-global zones and * the service instance should therefore never be 'enabled' in * non-global zones. This is enforced by acctadm(8), but there is * nothing that prevents someone from calling svcadm enable directly, * so we handle that case here by disabling the instance. */ if ((type == AC_FLOW || type == AC_NET) && getzoneid() != GLOBAL_ZONEID) { (void) smf_disable_instance(fmri, 0); warn(gettext("%s accounting cannot be configured in " "non-global zones\n"), ac_type_name(type)); return (SMF_EXIT_OK); } if (aconf_scf_init(fmri) == -1) { warn(gettext("cannot connect to repository\n")); return (SMF_EXIT_ERR_FATAL); } if (aconf_get_string(AC_PGNAME, AC_PROP_TRACKED, tracked, sizeof (tracked)) == -1) { warn(gettext("cannot get %s property\n"), AC_PROP_TRACKED); ret = SMF_EXIT_ERR_CONFIG; goto out; } if (aconf_get_string(AC_PGNAME, AC_PROP_UNTRACKED, untracked, sizeof (untracked)) == -1) { warn(gettext("cannot get %s property\n"), AC_PROP_UNTRACKED); ret = SMF_EXIT_ERR_CONFIG; goto out; } if (aconf_get_string(AC_PGNAME, AC_PROP_FILE, file, sizeof (file)) == -1) { warn(gettext("cannot get %s property\n"), AC_PROP_FILE); ret = SMF_EXIT_ERR_CONFIG; goto out; } if (aconf_get_bool(AC_PGNAME, AC_PROP_STATE, &b) == -1) { warn(gettext("cannot get %s property\n"), AC_PROP_STATE); ret = SMF_EXIT_ERR_CONFIG; goto out; } state = (b ? AC_ON : AC_OFF); if ((buf = malloc(AC_BUFSIZE)) == NULL) { warn(gettext("not enough memory\n")); ret = SMF_EXIT_ERR_FATAL; goto out; } (void) memset(buf, 0, AC_BUFSIZE); str2buf(buf, untracked, AC_OFF, type); str2buf(buf, tracked, AC_ON, type); (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL); if (acctctl(type | AC_RES_SET, buf, AC_BUFSIZE) == -1) { warn(gettext("cannot enable/disable %s accounting resources"), ac_type_name(type)); ret = SMF_EXIT_ERR_FATAL; } free(buf); if (strcmp(file, AC_STR_NONE) != 0) { if (open_exacct_file(file, type) == -1) ret = SMF_EXIT_ERR_FATAL; } else { if (acctctl(type | AC_FILE_SET, NULL, 0) == -1) { warn(gettext("cannot close %s accounting file"), ac_type_name(type)); ret = SMF_EXIT_ERR_FATAL; } } if (acctctl(type | AC_STATE_SET, &state, sizeof (state)) == -1) { warn(gettext("cannot %s %s accounting"), state == AC_ON ? gettext("enable") : gettext("disable"), ac_type_name(type)); ret = SMF_EXIT_ERR_FATAL; } (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL); if (state == AC_ON && type == AC_NET) { /* * Start logging. */ (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_DL_CONFIG, NULL); (void) dladm_start_usagelog(dld_handle, strncmp(tracked, "basic", strlen("basic")) == 0 ? DLADM_LOGTYPE_LINK : DLADM_LOGTYPE_FLOW, 20); (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_DL_CONFIG, NULL); } out: aconf_scf_fini(); return (ret); } void aconf_print(FILE *fp, int types) { acctconf_t ac; int print_order[] = { AC_TASK, AC_PROC, AC_FLOW, AC_NET }; int i; for (i = 0; i < NELEM(print_order); i++) { if (types & print_order[i]) { aconf_init(&ac, print_order[i]); aconf_print_type(&ac, fp, print_order[i]); } } } static void aconf_print_type(acctconf_t *acp, FILE *fp, int type) { switch (type) { case AC_TASK: (void) fprintf(fp, gettext(" Task accounting: %s\n"), acp->state == AC_ON ? gettext("active") : gettext("inactive")); (void) fprintf(fp, gettext(" Task accounting file: %s\n"), acp->file); (void) fprintf(fp, gettext(" Tracked task resources: %s\n"), acp->tracked); (void) fprintf(fp, gettext(" Untracked task resources: %s\n"), acp->untracked); break; case AC_PROC: (void) fprintf(fp, gettext(" Process accounting: %s\n"), acp->state == AC_ON ? gettext("active") : gettext("inactive")); (void) fprintf(fp, gettext(" Process accounting file: %s\n"), acp->file); (void) fprintf(fp, gettext(" Tracked process resources: %s\n"), acp->tracked); (void) fprintf(fp, gettext("Untracked process resources: %s\n"), acp->untracked); break; case AC_FLOW: (void) fprintf(fp, gettext(" Flow accounting: %s\n"), acp->state == AC_ON ? gettext("active") : gettext("inactive")); (void) fprintf(fp, gettext(" Flow accounting file: %s\n"), acp->file); (void) fprintf(fp, gettext(" Tracked flow resources: %s\n"), acp->tracked); (void) fprintf(fp, gettext(" Untracked flow resources: %s\n"), acp->untracked); break; case AC_NET: (void) fprintf(fp, gettext(" Net accounting: %s\n"), acp->state == AC_ON ? gettext("active") : gettext("inactive")); (void) fprintf(fp, gettext(" Net accounting file: %s\n"), acp->file); (void) fprintf(fp, gettext(" Tracked net resources: %s\n"), acp->tracked); (void) fprintf(fp, gettext(" Untracked net resources: %s\n"), acp->untracked); break; } } /* * Modified properties are put on the 'props' linked list by aconf_set_string() * and aconf_set_bool(). Walk the list of modified properties and write them * to the repository. The list is deleted on exit. */ int aconf_save(void) { scf_propertygroup_t *pg; scf_transaction_t *tx; props_t *p; props_t *q; int tx_result; if (props == NULL) return (0); if ((pg = scf_pg_create(handle)) == NULL || scf_instance_get_pg(inst, AC_PGNAME, pg) == -1 || (tx = scf_transaction_create(handle)) == NULL) goto out; do { if (scf_pg_update(pg) == -1 || scf_transaction_start(tx, pg) == -1) goto out; for (p = props; p != NULL; p = p->next) { if (scf_transaction_property_change(tx, p->entry, p->propname, p->proptype) == -1) goto out; (void) scf_entry_add_value(p->entry, p->value); } tx_result = scf_transaction_commit(tx); scf_transaction_reset(tx); } while (tx_result == 0); out: p = props; while (p != NULL) { scf_value_destroy(p->value); scf_entry_destroy(p->entry); free(p->propname); q = p->next; free(p); p = q; } props = NULL; scf_transaction_destroy(tx); scf_pg_destroy(pg); return ((tx_result == 1) ? 0 : -1); } boolean_t aconf_have_smf_auths(void) { char auth[NSS_BUFLEN_AUTHATTR]; struct passwd *pw; if ((pw = getpwuid(getuid())) == NULL) return (B_FALSE); if (aconf_get_string("general", "action_authorization", auth, sizeof (auth)) == -1 || chkauthattr(auth, pw->pw_name) == 0) return (B_FALSE); if (aconf_get_string("general", "value_authorization", auth, sizeof (auth)) == -1 || chkauthattr(auth, pw->pw_name) == 0) return (B_FALSE); if (aconf_get_string("config", "value_authorization", auth, sizeof (auth)) == -1 || chkauthattr(auth, pw->pw_name) == 0) return (B_FALSE); return (B_TRUE); } const char * aconf_type2fmri(int type) { switch (type) { case AC_PROC: return (FMRI_PROC_ACCT); case AC_TASK: return (FMRI_TASK_ACCT); case AC_FLOW: return (FMRI_FLOW_ACCT); case AC_NET: return (FMRI_NET_ACCT); default: die(gettext("invalid type %d\n"), type); } /* NOTREACHED */ return (NULL); } static int aconf_fmri2type(const char *fmri) { if (strcmp(fmri, FMRI_PROC_ACCT) == 0) return (AC_PROC); else if (strcmp(fmri, FMRI_TASK_ACCT) == 0) return (AC_TASK); else if (strcmp(fmri, FMRI_FLOW_ACCT) == 0) return (AC_FLOW); else if (strcmp(fmri, FMRI_NET_ACCT) == 0) return (AC_NET); else return (-1); } int aconf_scf_init(const char *fmri) { if ((handle = scf_handle_create(SCF_VERSION)) == NULL || scf_handle_bind(handle) == -1 || (inst = scf_instance_create(handle)) == NULL || scf_handle_decode_fmri(handle, fmri, NULL, NULL, inst, NULL, NULL, SCF_DECODE_FMRI_EXACT) == -1) { aconf_scf_fini(); return (-1); } return (0); } void aconf_scf_fini(void) { scf_instance_destroy(inst); (void) scf_handle_unbind(handle); scf_handle_destroy(handle); } static int aconf_get_string(const char *pgname, const char *propname, char *buf, size_t len) { scf_propertygroup_t *pg; scf_property_t *prop; scf_value_t *value; int ret = 0; if ((pg = scf_pg_create(handle)) == NULL) return (-1); if (scf_instance_get_pg_composed(inst, NULL, pgname, pg) == -1) { scf_pg_destroy(pg); return (-1); } if ((prop = scf_property_create(handle)) == NULL || (value = scf_value_create(handle)) == NULL || scf_pg_get_property(pg, propname, prop) == -1 || scf_property_get_value(prop, value) == -1 || scf_value_get_astring(value, buf, len) == -1) ret = -1; scf_value_destroy(value); scf_property_destroy(prop); scf_pg_destroy(pg); return (ret); } static int aconf_get_bool(const char *pgname, const char *propname, uint8_t *rval) { scf_propertygroup_t *pg; scf_property_t *prop; scf_value_t *value; int ret = 0; if ((pg = scf_pg_create(handle)) == NULL) return (-1); if (scf_instance_get_pg_composed(inst, NULL, pgname, pg) == -1) { scf_pg_destroy(pg); return (-1); } if ((prop = scf_property_create(handle)) == NULL || (value = scf_value_create(handle)) == NULL || scf_pg_get_property(pg, propname, prop) == -1 || scf_property_get_value(prop, value) == -1 || scf_value_get_boolean(value, rval) == -1) ret = -1; scf_value_destroy(value); scf_property_destroy(prop); scf_pg_destroy(pg); return (ret); } int aconf_set_string(const char *propname, const char *value) { props_t *p; if ((p = aconf_prop(propname, SCF_TYPE_ASTRING)) == NULL) return (-1); if (scf_value_set_astring(p->value, value) == -1) return (-1); return (0); } int aconf_set_bool(const char *propname, boolean_t value) { props_t *p; if ((p = aconf_prop(propname, SCF_TYPE_BOOLEAN)) == NULL) return (-1); scf_value_set_boolean(p->value, value); return (0); } static props_t * aconf_prop(const char *propname, int proptype) { props_t *p; if ((p = malloc(sizeof (props_t))) != NULL) { if ((p->propname = strdup(propname)) == NULL) { free(p); return (NULL); } if ((p->entry = scf_entry_create(handle)) == NULL) { free(p->propname); free(p); return (NULL); } if ((p->value = scf_value_create(handle)) == NULL) { scf_entry_destroy(p->entry); free(p->propname); free(p); return (NULL); } p->proptype = proptype; p->next = props; props = p; } return (p); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _ACONF_H #define _ACONF_H #include #include #include #ifdef __cplusplus extern "C" { #endif #define MAXRESLEN 256 typedef struct acctconf { int state; char file[MAXPATHLEN]; char tracked[MAXRESLEN]; char untracked[MAXRESLEN]; } acctconf_t; /* * Predefined strings */ #define AC_STR_NONE "none" /* * Configuration property group name */ #define AC_PGNAME "config" /* * Configuration property names */ #define AC_PROP_STATE "enabled" #define AC_PROP_FILE "file" #define AC_PROP_TRACKED "tracked" #define AC_PROP_UNTRACKED "untracked" extern void aconf_init(acctconf_t *, int); extern int aconf_setup(const char *); extern int aconf_scf_init(const char *); extern void aconf_scf_fini(void); extern int aconf_set_string(const char *, const char *); extern int aconf_set_bool(const char *, boolean_t); extern int aconf_save(void); extern void aconf_print(FILE *, int); extern boolean_t aconf_have_smf_auths(void); extern const char *aconf_type2fmri(int); #ifdef __cplusplus } #endif #endif /* _ACONF_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "aconf.h" #include "res.h" #define ACCTADM_NET_LOG_INTERVAL 20 static const char USAGE[] = "\ Usage:\n\ acctadm [ {process | task | flow | net} ]\n\ acctadm -s\n\ acctadm -r [ {process | task | flow | net} ]\n\ acctadm -x|-E|-D {process | task | flow | net}\n\ acctadm -f filename {process | task | flow | net}\n\ acctadm -e resources -d resources {process | task | flow | net}\n"; static const char OPTS[] = "rsxf:e:d:ED"; dladm_handle_t dld_handle = NULL; static void usage() { (void) fprintf(stderr, gettext(USAGE)); exit(E_USAGE); } static void setup_privs() { priv_set_t *privset; if (seteuid(getuid()) == -1 || setegid(getgid()) == -1) die(gettext("seteuid()/setegid() failed")); /* * Add our privileges and remove unneeded 'basic' privileges from the * permitted set. */ if ((privset = priv_str_to_set("basic", ",", NULL)) == NULL) die(gettext("cannot setup privileges")); (void) priv_addset(privset, PRIV_SYS_ACCT); (void) priv_addset(privset, PRIV_FILE_DAC_WRITE); (void) priv_addset(privset, PRIV_SYS_DL_CONFIG); (void) priv_delset(privset, PRIV_FILE_LINK_ANY); (void) priv_delset(privset, PRIV_PROC_EXEC); (void) priv_delset(privset, PRIV_PROC_FORK); (void) priv_delset(privset, PRIV_PROC_INFO); (void) priv_delset(privset, PRIV_PROC_SESSION); priv_inverse(privset); if (setppriv(PRIV_OFF, PRIV_PERMITTED, privset) == -1) die(gettext("cannot setup privileges")); priv_freeset(privset); /* * Clear the Inheritable and Limit sets. */ if ((privset = priv_allocset()) == NULL) die(gettext("cannot setup privileges")); priv_emptyset(privset); if (setppriv(PRIV_SET, PRIV_INHERITABLE, privset) == -1 || setppriv(PRIV_SET, PRIV_LIMIT, privset) == -1) die(gettext("cannot setup privileges")); /* * Turn off the sys_acct, file_dac_write and dl_config privileges * until needed. */ (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_FILE_DAC_WRITE, PRIV_SYS_ACCT, PRIV_SYS_DL_CONFIG, NULL); } int main(int argc, char *argv[]) { int c; /* options character */ int type = 0; /* type of accounting */ int modified = 0; /* have we modified any properties? */ acctconf_t ac; /* current configuration */ char *typestr = NULL; /* type of accounting argument string */ char *enabled = NULL; /* enabled resources string */ char *disabled = NULL; /* disabled resources string */ char *file = NULL; int Eflg = 0; int Dflg = 0; int rflg = 0; int sflg = 0; int xflg = 0; int optcnt = 0; int state; const char *fmri; /* FMRI for this instance */ int err = 0; setup_privs(); (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); (void) setpname(argv[0]); for (; optind < argc; optind++) { while ((c = getopt(argc, argv, OPTS)) != (int)EOF) { switch (c) { case 'd': disabled = optarg; break; case 'e': enabled = optarg; break; case 'D': Dflg = 1; optcnt++; break; case 'E': Eflg = 1; optcnt++; break; case 'f': file = optarg; optcnt++; break; case 'r': rflg = 1; optcnt++; break; case 's': sflg = 1; optcnt++; break; case 'x': xflg = 1; optcnt++; break; case '?': default: usage(); } } /* * Permanently give up euid 0, egid 0 and privileges we * don't need for the specified options. */ if (!(file || sflg)) { if (setreuid(getuid(), getuid()) == -1 || setregid(getgid(), getgid()) == -1) die(gettext("setreuid()/setregid() failed")); (void) priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_FILE_DAC_WRITE, NULL); } if (!(disabled || enabled || Dflg || Eflg || file || sflg || xflg)) (void) priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_SYS_ACCT, PRIV_SYS_DL_CONFIG, NULL); if (optind < argc) { if (typestr != NULL) { warn(gettext("illegal argument -- %s\n"), argv[optind]); usage(); } else { typestr = argv[optind]; } } } if (typestr != NULL) { if (strcmp(typestr, "process") == 0 || strcmp(typestr, "proc") == 0) type |= AC_PROC; else if (strcmp(typestr, "task") == 0) type |= AC_TASK; else if (strcmp(typestr, "flow") == 0) type |= AC_FLOW; else if (strcmp(typestr, "net") == 0) type |= AC_NET; else { warn(gettext("unknown accounting type -- %s\n"), typestr); usage(); } } else type = AC_PROC | AC_TASK | AC_FLOW | AC_NET; /* * Drop the DL config privilege if we are not working with * net. */ if ((type & AC_NET) == 0) { (void) priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_SYS_DL_CONFIG, NULL); } /* * check for invalid options */ if (optcnt > 1) usage(); /* * XXX For AC_NET, enabled/disabled should only be "basic" or * "extended" - need to check it here. */ if ((enabled || disabled) && (rflg || Dflg || sflg || xflg || Eflg)) usage(); if ((file || xflg || Dflg || Eflg || enabled || disabled) && !typestr) { warn(gettext("accounting type must be specified\n")); usage(); } if (rflg) { printgroups(type); return (E_SUCCESS); } /* * If no arguments have been passed then just print out the current * state and exit. */ if (!enabled && !disabled && !file && !Eflg && !rflg && !Dflg && !sflg && !xflg) { aconf_print(stdout, type); return (E_SUCCESS); } /* Open the libdladm handle */ if (dladm_open(&dld_handle) != DLADM_STATUS_OK) die(gettext("failed to open dladm handle\n")); /* * smf(7) start method. The FMRI to operate on is retrieved from the * SMF_FMRI environment variable that the restarter provides. */ if (sflg) { if ((fmri = getenv("SMF_FMRI")) != NULL) { int ret = aconf_setup(fmri); dladm_close(dld_handle); return (ret); } die(gettext("-s option should only be invoked by smf(7)\n")); } assert(type == AC_PROC || type == AC_TASK || type == AC_FLOW || type == AC_NET); if ((type == AC_FLOW || type == AC_NET) && getzoneid() != GLOBAL_ZONEID) die(gettext("%s accounting cannot be configured in " "non-global zones\n"), ac_type_name(type)); fmri = aconf_type2fmri(type); if (aconf_scf_init(fmri) == -1) die(gettext("cannot connect to repository for %s\n"), fmri); /* * Since the sys_acct the privilege allows use of acctctl() regardless * of the accounting type, we check the smf(7) authorizations granted * to the user to determine whether the user is allowed to change the * configuration for this particular accounting type. */ if (!aconf_have_smf_auths()) die(gettext("insufficient authorization to change %s extended " "accounting configuration\n"), ac_type_name(type)); if (xflg) { /* * Turn off the specified accounting and close its file */ /* * Stop net logging before turning it off so that the last * set of logs can be written. */ if (type & AC_NET) { (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_DL_CONFIG, NULL); err = dladm_stop_usagelog(dld_handle, DLADM_LOGTYPE_FLOW); (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_DL_CONFIG, NULL); if (err != DLADM_STATUS_OK) { die(gettext("failed to stop logging network " "information, error %d\n"), errno); } } state = AC_OFF; (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL); if (acctctl(type | AC_STATE_SET, &state, sizeof (int)) == -1) die(gettext("cannot disable %s accounting"), ac_type_name(type)); if (acctctl(type | AC_FILE_SET, NULL, 0) == -1) die(gettext("cannot close %s accounting file\n"), ac_type_name(type)); (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL); if (aconf_set_bool(AC_PROP_STATE, B_FALSE) == -1) die(gettext("cannot update %s property\n"), AC_PROP_STATE); if (aconf_set_string(AC_PROP_FILE, AC_STR_NONE) == -1) die(gettext("cannot update %s property\n"), AC_PROP_FILE); modified++; } if (enabled || disabled) { char *tracked, *untracked; ac_res_t *buf; /* * Enable/disable resources */ if ((buf = malloc(AC_BUFSIZE)) == NULL) die(gettext("not enough memory\n")); (void) memset(buf, 0, AC_BUFSIZE); if (acctctl(type | AC_RES_GET, buf, AC_BUFSIZE) == -1) { free(buf); die(gettext("cannot obtain list of resources\n")); } if (disabled) { /* * Stop net logging before turning it off so that the * last set of logs can be written. */ if (type & AC_NET) { (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_DL_CONFIG, NULL); err = dladm_stop_usagelog(dld_handle, strcmp(disabled, "basic") == 0 ? DLADM_LOGTYPE_LINK : DLADM_LOGTYPE_FLOW); (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_DL_CONFIG, NULL); if (err != DLADM_STATUS_OK) { die(gettext("failed to stop logging " "network information, error %d\n"), errno); } } str2buf(buf, disabled, AC_OFF, type); } else if (enabled) { str2buf(buf, enabled, AC_ON, type); } (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL); if (acctctl(type | AC_RES_SET, buf, AC_BUFSIZE) == -1) { free(buf); die(gettext("cannot enable/disable %s accounting " "resources\n"), ac_type_name(type)); } (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL); tracked = buf2str(buf, AC_BUFSIZE, AC_ON, type); untracked = buf2str(buf, AC_BUFSIZE, AC_OFF, type); if (aconf_set_string(AC_PROP_TRACKED, tracked) == -1) die(gettext("cannot update %s property\n"), AC_PROP_TRACKED); if (aconf_set_string(AC_PROP_UNTRACKED, untracked) == -1) die(gettext("cannot update %s property\n"), AC_PROP_UNTRACKED); free(tracked); free(untracked); free(buf); modified++; } if (file) { /* * Open new accounting file */ (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL); if (open_exacct_file(file, type) == -1) { dladm_close(dld_handle); exit(E_ERROR); } if (aconf_set_string(AC_PROP_FILE, file) == -1) die(gettext("cannot update %s property\n"), AC_PROP_FILE); state = AC_ON; if (acctctl(type | AC_STATE_SET, &state, sizeof (int)) == -1) die(gettext("cannot enable %s accounting"), ac_type_name(type)); (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL); if (aconf_set_bool(AC_PROP_STATE, B_TRUE) == -1) die(gettext("cannot update %s property\n"), AC_PROP_STATE); modified++; } /* * Let's get network logging started. We do this after turning on * accounting and opening the file so that we can start writing * immediately. */ if (enabled && (type & AC_NET)) { /* * Default logging interval for AC_NET is * ACCTADM_NET_LOG_INTERVAL. */ (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_DL_CONFIG, NULL); err = dladm_start_usagelog(dld_handle, strcmp(enabled, "basic") == 0 ? DLADM_LOGTYPE_LINK : DLADM_LOGTYPE_FLOW, ACCTADM_NET_LOG_INTERVAL); (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_DL_CONFIG, NULL); if (err != DLADM_STATUS_OK) { die(gettext("failed to start logging " "network information, error %d\n"), errno); } } if (Dflg) { /* * Disable accounting */ /* * Stop net logging before turning it off so that the last * set of logs can be written. */ if (type & AC_NET) { (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_DL_CONFIG, NULL); err = dladm_stop_usagelog(dld_handle, DLADM_LOGTYPE_FLOW); (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_DL_CONFIG, NULL); if (err != DLADM_STATUS_OK) { die(gettext("failed to stop logging " "network information, error %d\n"), errno); } } state = AC_OFF; (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL); if (acctctl(type | AC_STATE_SET, &state, sizeof (int)) == -1) die(gettext("cannot disable %s accounting"), ac_type_name(type)); (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL); if (aconf_set_bool(AC_PROP_STATE, B_FALSE) == -1) die(gettext("cannot update %s property\n"), AC_PROP_STATE); modified++; } if (Eflg) { /* * Enable accounting */ /* * Let's get network logging started. */ if (type & AC_NET) { /* * Default logging interval for AC_NET is * ACCTADM_NET_LOG_INTERVAL. */ (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_DL_CONFIG, NULL); err = dladm_start_usagelog(dld_handle, DLADM_LOGTYPE_FLOW, ACCTADM_NET_LOG_INTERVAL); (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_DL_CONFIG, NULL); if (err != DLADM_STATUS_OK) { die(gettext("failed to start logging " "network information, error %d\n"), errno); } } state = AC_ON; (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL); if (acctctl(type | AC_STATE_SET, &state, sizeof (int)) == -1) die(gettext("cannot enable %s accounting"), ac_type_name(type)); (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL); if (aconf_set_bool(AC_PROP_STATE, B_TRUE) == -1) die(gettext("cannot update %s property\n"), AC_PROP_STATE); modified++; } (void) priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_SYS_ACCT, NULL); if (modified) { char *smf_state; if (aconf_save() == -1) die(gettext("cannot save %s accounting " "configuration\n"), ac_type_name(type)); /* * Enable or disable the instance depending on the effective * configuration. If the effective configuration results in * extended accounting being 'on', the instance is enabled so * the configuration is applied at the next boot. */ smf_state = smf_get_state(fmri); aconf_init(&ac, type); if (ac.state == AC_ON || strcmp(ac.file, AC_STR_NONE) != 0 || strcmp(ac.tracked, AC_STR_NONE) != 0) { if (strcmp(smf_state, SCF_STATE_STRING_ONLINE) != 0) if (smf_enable_instance(fmri, 0) == -1) die(gettext("cannot enable %s\n"), fmri); } else { if (strcmp(smf_state, SCF_STATE_STRING_ONLINE) == 0) if (smf_disable_instance(fmri, 0) == -1) die(gettext("cannot disable %s\n"), fmri); } free(smf_state); } aconf_scf_fini(); dladm_close(dld_handle); return (E_SUCCESS); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include "utils.h" #include "aconf.h" #include "res.h" /* * resource names */ static ac_resname_t ac_names[] = { /* * Process accounting resources */ { AC_PROC, AC_PROC_PID, "pid" }, { AC_PROC, AC_PROC_UID, "uid" }, { AC_PROC, AC_PROC_GID, "gid" }, { AC_PROC, AC_PROC_PROJID, "projid" }, { AC_PROC, AC_PROC_TASKID, "taskid" }, { AC_PROC, AC_PROC_CPU, "cpu" }, { AC_PROC, AC_PROC_TIME, "time" }, { AC_PROC, AC_PROC_COMMAND, "command" }, { AC_PROC, AC_PROC_TTY, "tty" }, { AC_PROC, AC_PROC_HOSTNAME, "host" }, { AC_PROC, AC_PROC_MICROSTATE, "mstate" }, { AC_PROC, AC_PROC_FLAG, "flag" }, { AC_PROC, AC_PROC_ANCPID, "ancpid" }, { AC_PROC, AC_PROC_WAIT_STATUS, "wait-status" }, { AC_PROC, AC_PROC_ZONENAME, "zone" }, { AC_PROC, AC_PROC_MEM, "memory" }, /* * Task accounting resources */ { AC_TASK, AC_TASK_TASKID, "taskid" }, { AC_TASK, AC_TASK_PROJID, "projid" }, { AC_TASK, AC_TASK_CPU, "cpu" }, { AC_TASK, AC_TASK_TIME, "time" }, { AC_TASK, AC_TASK_HOSTNAME, "host" }, { AC_TASK, AC_TASK_MICROSTATE, "mstate" }, { AC_TASK, AC_TASK_ANCTASKID, "anctaskid" }, { AC_TASK, AC_TASK_ZONENAME, "zone" }, /* * Flow accounting resources */ { AC_FLOW, AC_FLOW_SADDR, "saddr" }, { AC_FLOW, AC_FLOW_DADDR, "daddr" }, { AC_FLOW, AC_FLOW_SPORT, "sport" }, { AC_FLOW, AC_FLOW_DPORT, "dport" }, { AC_FLOW, AC_FLOW_PROTOCOL, "proto" }, { AC_FLOW, AC_FLOW_DSFIELD, "dsfield" }, { AC_FLOW, AC_FLOW_NBYTES, "nbytes" }, { AC_FLOW, AC_FLOW_NPKTS, "npkts" }, { AC_FLOW, AC_FLOW_CTIME, "ctime" }, { AC_FLOW, AC_FLOW_LSEEN, "lseen" }, { AC_FLOW, AC_FLOW_PROJID, "projid" }, { AC_FLOW, AC_FLOW_UID, "uid" }, { AC_FLOW, AC_FLOW_ANAME, "action" }, /* * Net accounting resources */ { AC_NET, AC_NET_NAME, "name" }, { AC_NET, AC_NET_EHOST, "ehost" }, { AC_NET, AC_NET_EDEST, "edest" }, { AC_NET, AC_NET_VLAN_TPID, "vlan_pid" }, { AC_NET, AC_NET_VLAN_TCI, "vlan_tci" }, { AC_NET, AC_NET_SAP, "sap" }, { AC_NET, AC_NET_PRIORITY, "priority" }, { AC_NET, AC_NET_BWLIMIT, "bwlimit" }, { AC_NET, AC_NET_DEVNAME, "devname" }, { AC_NET, AC_NET_SADDR, "src_ip" }, { AC_NET, AC_NET_DADDR, "dst_ip" }, { AC_NET, AC_NET_SPORT, "src_port" }, { AC_NET, AC_NET_DPORT, "dst_port" }, { AC_NET, AC_NET_PROTOCOL, "protocol" }, { AC_NET, AC_NET_DSFIELD, "dsfield" }, { AC_NET, AC_NET_CURTIME, "curtime" }, { AC_NET, AC_NET_IBYTES, "ibytes" }, { AC_NET, AC_NET_OBYTES, "obytes" }, { AC_NET, AC_NET_IPKTS, "ipkts" }, { AC_NET, AC_NET_OPKTS, "opkts" }, { AC_NET, AC_NET_IERRPKTS, "ierrpkts" }, { AC_NET, AC_NET_OERRPKTS, "oerrpkts" }, /* * These are included for compatibility with old acctadm that * didn't have resource groups for individual accounting types. * It was possible to have resource "pid" enabled for task * accounting even though we couldn't actually track it. */ { AC_TASK, AC_NONE, "pid" }, { AC_TASK, AC_NONE, "uid" }, { AC_TASK, AC_NONE, "gid" }, { AC_TASK, AC_NONE, "command" }, { AC_TASK, AC_NONE, "tty" }, { AC_TASK, AC_NONE, "flag" }, { AC_NONE, AC_NONE, NULL } }; /* * resource groups */ static ac_group_t ac_groups[] = { { AC_PROC, "extended", { AC_PROC_PID, AC_PROC_UID, AC_PROC_GID, AC_PROC_CPU, AC_PROC_TIME, AC_PROC_COMMAND, AC_PROC_TTY, AC_PROC_PROJID, AC_PROC_TASKID, AC_PROC_ANCPID, AC_PROC_WAIT_STATUS, AC_PROC_ZONENAME, AC_PROC_FLAG, AC_PROC_MEM, AC_PROC_MICROSTATE, AC_NONE } }, { AC_PROC, "basic", { AC_PROC_PID, AC_PROC_UID, AC_PROC_GID, AC_PROC_CPU, AC_PROC_TIME, AC_PROC_COMMAND, AC_PROC_TTY, AC_PROC_FLAG, AC_NONE } }, { AC_TASK, "extended", { AC_TASK_TASKID, AC_TASK_PROJID, AC_TASK_CPU, AC_TASK_TIME, AC_TASK_HOSTNAME, AC_TASK_MICROSTATE, AC_TASK_ANCTASKID, AC_TASK_ZONENAME, AC_NONE } }, { AC_TASK, "basic", { AC_TASK_TASKID, AC_TASK_PROJID, AC_TASK_CPU, AC_TASK_TIME, AC_NONE } }, { AC_FLOW, "extended", { AC_FLOW_SADDR, AC_FLOW_DADDR, AC_FLOW_SPORT, AC_FLOW_DPORT, AC_FLOW_PROTOCOL, AC_FLOW_DSFIELD, AC_FLOW_NBYTES, AC_FLOW_NPKTS, AC_FLOW_ANAME, AC_FLOW_CTIME, AC_FLOW_LSEEN, AC_FLOW_PROJID, AC_FLOW_UID, AC_NONE } }, { AC_FLOW, "basic", { AC_FLOW_SADDR, AC_FLOW_DADDR, AC_FLOW_SPORT, AC_FLOW_DPORT, AC_FLOW_PROTOCOL, AC_FLOW_NBYTES, AC_FLOW_NPKTS, AC_FLOW_ANAME, AC_NONE } }, { AC_NET, "extended", { AC_NET_NAME, AC_NET_EHOST, AC_NET_EDEST, AC_NET_VLAN_TPID, AC_NET_VLAN_TCI, AC_NET_SAP, AC_NET_PRIORITY, AC_NET_BWLIMIT, AC_NET_DEVNAME, AC_NET_SADDR, AC_NET_DADDR, AC_NET_SPORT, AC_NET_DPORT, AC_NET_PROTOCOL, AC_NET_DSFIELD, AC_NET_CURTIME, AC_NET_IBYTES, AC_NET_OBYTES, AC_NET_IPKTS, AC_NET_OPKTS, AC_NET_IERRPKTS, AC_NET_OERRPKTS, AC_NONE } }, { AC_NET, "basic", { AC_NET_NAME, AC_NET_DEVNAME, AC_NET_EHOST, AC_NET_EDEST, AC_NET_VLAN_TPID, AC_NET_VLAN_TCI, AC_NET_SAP, AC_NET_PRIORITY, AC_NET_BWLIMIT, AC_NET_CURTIME, AC_NET_IBYTES, AC_NET_OBYTES, AC_NET_IPKTS, AC_NET_OPKTS, AC_NET_IERRPKTS, AC_NET_OERRPKTS, AC_NONE } }, { AC_NONE, NULL, { AC_NONE } } }; /* * this function returns the id of the named resource */ static int name2id(char *name, int type) { ac_resname_t *acname = ac_names; while (acname->ar_type != AC_NONE) { if (acname->ar_type == type && strcmp(acname->ar_name, name) == 0) { if (acname->ar_id == AC_NONE) /* * For compatibility with older versions. */ return (-1); else return (acname->ar_id); } acname++; } return (0); } /* * this function gives name of the resource by its id */ static char * id2name(int id, int type) { ac_resname_t *acname = ac_names; while (acname->ar_id != AC_NONE) { if (acname->ar_type == type && acname->ar_id == id) return (acname->ar_name); acname++; } return (NULL); } static void printgroup(int type) { int r, g, id; for (g = 0; ac_groups[g].ag_type != AC_NONE; g++) { if (ac_groups[g].ag_type != type) continue; (void) printf("%-9s", ac_groups[g].ag_name); (void) printf("%s", id2name(ac_groups[g].ag_mem[0], type)); for (r = 1; (id = ac_groups[g].ag_mem[r]) != AC_NONE; r++) (void) printf(",%s", id2name(id, type)); (void) printf("\n"); } } /* * this function prints the list of resource groups and their members */ void printgroups(int type) { int header = 0; if ((type & AC_PROC) && (type & AC_TASK) && (type & AC_FLOW) && (type & AC_NET)) { header = 1; } if (type & AC_PROC) { if (header == 1) (void) printf("process:\n"); printgroup(AC_PROC); } if (type & AC_TASK) { if (header == 1) (void) printf("task:\n"); printgroup(AC_TASK); } if (type & AC_FLOW) { if (header == 1) (void) printf("flow:\n"); printgroup(AC_FLOW); } if (type & AC_NET) { if (header == 1) (void) printf("net:\n"); printgroup(AC_NET); } } /* * this function sets the state of the particular resource */ static void resset(ac_res_t *res, int id, int state) { ac_res_t *resp; resp = (ac_res_t *)((uintptr_t)res + (sizeof (ac_res_t) * (id - 1))); resp->ar_state = state; resp->ar_id = id; } /* * this function gets the state of the particular resource */ static int resget(ac_res_t *res, int id) { ac_res_t *resp; resp = (ac_res_t *)((uintptr_t)res + (sizeof (ac_res_t) * (id - 1))); return (resp->ar_state); } /* * this function converts a string of resources into a buffer which then * can be used for acctctl() system call */ void str2buf(ac_res_t *buf, char *str, int state, int type) { int i, j, id, ok; char *p, *g, *copy; if (strcmp(str, AC_STR_NONE) == 0) return; /* * Take a lap through str, processing resources, modifying buf copy * as appropriate and making sure that all resource names are valid. */ if ((copy = malloc(strlen(str) + 1)) == NULL) die(gettext("not enough memory\n")); (void) memcpy(copy, str, strlen(str) + 1); p = strtok(copy, ", "); while (p != NULL) { /* * check if str contains any resource groups */ for (ok = 0, i = 0; (g = ac_groups[i].ag_name) != NULL; i++) { if (strcmp(p, g) == 0 && ac_groups[i].ag_type == type) { for (j = 0; (id = ac_groups[i].ag_mem[j]) != AC_NONE; j++) resset(buf, id, state); ok = 1; break; } } if (ok == 0) { id = name2id(p, type); if (id > 0) resset(buf, id, state); else if (id == 0) die(gettext("unknown %s resource: %s\n"), ac_type_name(type), p); } p = strtok(NULL, ", "); } free(copy); } /* * this function converts a buffer into a string of resource names. * state (on/off) for resources of interest is selected by the third argument. * accounting type is selected by the fourth argument. * it is caller's responsibility to free the allocated string buffer. */ char * buf2str(ac_res_t *buffer, size_t bufsz, int state, int type) { int i, j, ok, id; char *str, *g; ac_res_t *buf, *cur; if ((buf = malloc(bufsz)) == NULL || (str = malloc(MAXRESLEN)) == NULL) die(gettext("not enough memory\n")); (void) memset(str, 0, MAXRESLEN); (void) memcpy(buf, buffer, bufsz); /* * check if buf has any resource groups in it */ for (i = 0; (g = ac_groups[i].ag_name) != NULL; i++) { if (ac_groups[i].ag_type != type) continue; for (j = 0; (id = ac_groups[i].ag_mem[j]) != AC_NONE; j++) { ok = 1; if (resget(buf, id) != state) { ok = 0; break; } } if (ok) { /* buf contains this resource group */ if (strlen(str) != 0) (void) strcat(str, ","); (void) strcat(str, g); for (j = 0; (id = ac_groups[i].ag_mem[j]) != AC_NONE; j++) resset(buf, id, state == AC_ON ? AC_OFF : AC_ON); ok = 0; } } /* * browse through the rest of the buf for all remaining resources * that are not a part of any groups */ for (cur = buf; cur->ar_id != AC_NONE; cur++) { if (cur->ar_state == state) { if (strlen(str) != 0) (void) strcat(str, ","); if (id2name(cur->ar_id, type) == NULL) die(gettext("unknown %s resource id (%d)\n"), ac_type_name(type), cur->ar_id); (void) strcat(str, id2name(cur->ar_id, type)); } } if (strlen(str) == 0) (void) strcpy(str, AC_STR_NONE); free(buf); return (str); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 1999-2001 by Sun Microsystems, Inc. * All rights reserved. */ #ifndef _RES_H #define _RES_H #include #ifdef __cplusplus extern "C" { #endif typedef struct ac_resname { int ar_type; int ar_id; char *ar_name; } ac_resname_t; typedef struct ac_group { int ag_type; char *ag_name; int ag_mem[AC_MAX_RES + 1]; } ac_group_t; #define AC_BUFSIZE (sizeof (ac_res_t) * (AC_MAX_RES + 1)) extern void str2buf(ac_res_t *, char *, int, int); extern char *buf2str(ac_res_t *, size_t, int, int); extern void printgroups(int); #ifdef __cplusplus } #endif #endif /* _RES_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" static char PNAME_FMT[] = "%s: "; static char ERRNO_FMT[] = ": %s\n"; static char *pname; /*PRINTFLIKE1*/ void warn(const char *format, ...) { int err = errno; va_list alist; if (pname != NULL) (void) fprintf(stderr, gettext(PNAME_FMT), pname); va_start(alist, format); (void) vfprintf(stderr, format, alist); va_end(alist); if (strchr(format, '\n') == NULL) (void) fprintf(stderr, gettext(ERRNO_FMT), strerror(err)); } /*PRINTFLIKE1*/ void die(char *format, ...) { int err = errno; va_list alist; if (pname != NULL) (void) fprintf(stderr, gettext(PNAME_FMT), pname); va_start(alist, format); (void) vfprintf(stderr, format, alist); va_end(alist); if (strchr(format, '\n') == NULL) (void) fprintf(stderr, gettext(ERRNO_FMT), strerror(err)); /* close the libdladm handle if it was opened */ if (dld_handle != NULL) dladm_close(dld_handle); exit(E_ERROR); } char * setpname(char *arg0) { char *p = strrchr(arg0, '/'); if (p == NULL) p = arg0; else p++; pname = p; return (pname); } /* * Return the localized name of an accounting type. */ const char * ac_type_name(int type) { switch (type) { case AC_PROC: return (gettext("process")); case AC_FLOW: return (gettext("flow")); case AC_TASK: return (gettext("task")); case AC_NET: return (gettext("net")); default: die(gettext("invalid type %d\n"), type); } /* NOTREACHED */ return (NULL); } /* * Open an accounting file. The filename specified must be an absolute * pathname and the existing contents of the file (if any) must be of the * requested type. Needs euid 0 to open the root-owned accounting file. * file_dac_write is required to create a new file in a directory not owned * by root (/var/adm/exacct is owned by 'adm'). Assumes sys_acct privilege is * already asserted by caller. */ int open_exacct_file(const char *file, int type) { int rc; int err; if (file[0] != '/') { warn(gettext("%s is not an absolute pathname\n"), file); return (-1); } if (!verify_exacct_file(file, type)) { warn(gettext("%s is not a %s accounting file\n"), file, ac_type_name(type)); return (-1); } if (seteuid(0) == -1 || setegid(0) == -1) { warn(gettext("seteuid()/setegid() failed")); return (-1); } assert(priv_ineffect(PRIV_SYS_ACCT)); (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_FILE_DAC_WRITE, NULL); rc = acctctl(type | AC_FILE_SET, (void *) file, strlen(file) + 1); if (rc == -1 && (err = errno) == EBUSY) { char name[MAXPATHLEN]; struct stat cur; struct stat new; /* * The file is already open as an accounting file somewhere. * If the file we're trying to open is the same as we have * currently open then we're ok. */ if (acctctl(type | AC_FILE_GET, name, sizeof (name)) == 0 && stat(file, &new) != -1 && stat(name, &cur) != -1 && new.st_dev == cur.st_dev && new.st_ino == cur.st_ino) rc = 0; } /* * euid 0, egid 0 and the file_dac_write privilege are no longer * required; give them up permanently. */ (void) priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_FILE_DAC_WRITE, NULL); if (setreuid(getuid(), getuid()) == -1 || setregid(getgid(), getgid()) == -1) die(gettext("setreuid()/setregid() failed")); if (rc == 0) return (0); warn(gettext("cannot open %s accounting file %s: %s\n"), ac_type_name(type), file, strerror(err)); return (-1); } /* * Verify that the file contents (if any) are extended accounting records * of the desired type. */ boolean_t verify_exacct_file(const char *file, int type) { ea_file_t ef; ea_object_t eo; struct stat st; int err; if (stat(file, &st) != -1 && st.st_size != 0) { if (seteuid(0) == -1) return (B_FALSE); err = ea_open(&ef, file, "Hammerhead", EO_TAIL, O_RDONLY, 0); if (seteuid(getuid()) == 1) die(gettext("seteuid() failed")); if (err == -1) return (B_FALSE); bzero(&eo, sizeof (eo)); if (ea_previous_object(&ef, &eo) == EO_ERROR) { /* * EXR_EOF indicates there are no non-header objects * in the file. It can't be determined that this * file is or is not the proper type of extended * accounting file, which isn't necessarily an error. * Since it is a proper (albeit empty) extended * accounting file, it matches any desired type. * * if ea_previous_object() failed for any other reason * than EXR_EOF, the file must be corrupt. */ if (ea_error() != EXR_EOF) { (void) ea_close(&ef); return (B_FALSE); } } else { /* * A non-header object exists. Insist that it be * either a process, task, flow or net accounting * record, the same type as is desired. * xxx-venu:check 101 merge for EXD_GROUP_NET_* */ uint_t c = eo.eo_catalog & EXD_DATA_MASK; if (eo.eo_type != EO_GROUP || (eo.eo_catalog & EXC_CATALOG_MASK) != EXC_NONE || (!(c == EXD_GROUP_PROC && type == AC_PROC || c == EXD_GROUP_TASK && type == AC_TASK || c == EXD_GROUP_FLOW && type == AC_FLOW || (c == EXD_GROUP_NET_LINK_DESC || c == EXD_GROUP_NET_FLOW_DESC || c == EXD_GROUP_NET_LINK_STATS || c == EXD_GROUP_NET_FLOW_STATS) && type == AC_NET))) { (void) ea_close(&ef); return (B_FALSE); } } (void) ea_close(&ef); } return (B_TRUE); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _UTILS_H #define _UTILS_H #include #include #ifdef __cplusplus extern "C" { #endif #define E_SUCCESS 0 /* Exit status for success */ #define E_ERROR 1 /* Exit status for error */ #define E_USAGE 2 /* Exit status for usage error */ /* handle for libdladm calls */ extern dladm_handle_t dld_handle; extern void warn(const char *, ...); extern void die(char *, ...) __NORETURN; extern char *setpname(char *); extern const char *ac_type_name(int); extern int open_exacct_file(const char *, int); extern boolean_t verify_exacct_file(const char *, int); #ifdef __cplusplus } #endif #endif /* _UTILS_H */