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 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <sys/types.h> 29 #include <sys/acctctl.h> 30 #include <unistd.h> 31 #include <string.h> 32 #include <stdlib.h> 33 #include <errno.h> 34 #include <limits.h> 35 #include <libscf.h> 36 #include <pwd.h> 37 #include <auth_attr.h> 38 #include <nss_dbdefs.h> 39 #include <secdb.h> 40 #include <priv.h> 41 #include <zone.h> 42 43 #include "aconf.h" 44 #include "utils.h" 45 #include "res.h" 46 47 #define FMRI_FLOW_ACCT "svc:/system/extended-accounting:flow" 48 #define FMRI_PROC_ACCT "svc:/system/extended-accounting:process" 49 #define FMRI_TASK_ACCT "svc:/system/extended-accounting:task" 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 * 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 && getzoneid() != GLOBAL_ZONEID) { 144 (void) smf_disable_instance(fmri, 0); 145 warn(gettext("%s accounting cannot be configured in " 146 "non-global zones\n"), ac_type_name(type)); 147 return (SMF_EXIT_OK); 148 } 149 150 if (aconf_scf_init(fmri) == -1) { 151 warn(gettext("cannot connect to repository\n")); 152 return (SMF_EXIT_ERR_FATAL); 153 } 154 if (aconf_get_string(AC_PGNAME, AC_PROP_TRACKED, tracked, 155 sizeof (tracked)) == -1) { 156 warn(gettext("cannot get %s property\n"), AC_PROP_TRACKED); 157 ret = SMF_EXIT_ERR_CONFIG; 158 goto out; 159 } 160 if (aconf_get_string(AC_PGNAME, AC_PROP_UNTRACKED, untracked, 161 sizeof (untracked)) == -1) { 162 warn(gettext("cannot get %s property\n"), AC_PROP_UNTRACKED); 163 ret = SMF_EXIT_ERR_CONFIG; 164 goto out; 165 } 166 if (aconf_get_string(AC_PGNAME, AC_PROP_FILE, file, 167 sizeof (file)) == -1) { 168 warn(gettext("cannot get %s property\n"), AC_PROP_FILE); 169 ret = SMF_EXIT_ERR_CONFIG; 170 goto out; 171 } 172 if (aconf_get_bool(AC_PGNAME, AC_PROP_STATE, &b) == -1) { 173 warn(gettext("cannot get %s property\n"), AC_PROP_STATE); 174 ret = SMF_EXIT_ERR_CONFIG; 175 goto out; 176 } 177 state = (b ? AC_ON : AC_OFF); 178 179 if ((buf = malloc(AC_BUFSIZE)) == NULL) { 180 warn(gettext("not enough memory\n")); 181 ret = SMF_EXIT_ERR_FATAL; 182 goto out; 183 } 184 (void) memset(buf, 0, AC_BUFSIZE); 185 str2buf(buf, untracked, AC_OFF, type); 186 str2buf(buf, tracked, AC_ON, type); 187 188 (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL); 189 if (acctctl(type | AC_RES_SET, buf, AC_BUFSIZE) == -1) { 190 warn(gettext("cannot enable/disable %s accounting resources"), 191 ac_type_name(type)); 192 ret = SMF_EXIT_ERR_FATAL; 193 } 194 free(buf); 195 196 if (strcmp(file, AC_STR_NONE) != 0) { 197 if (open_exacct_file(file, type) == -1) 198 ret = SMF_EXIT_ERR_FATAL; 199 } else { 200 if (acctctl(type | AC_FILE_SET, NULL, 0) == -1) { 201 warn(gettext("cannot close %s accounting file"), 202 ac_type_name(type)); 203 ret = SMF_EXIT_ERR_FATAL; 204 } 205 } 206 if (acctctl(type | AC_STATE_SET, &state, sizeof (state)) == -1) { 207 warn(gettext("cannot %s %s accounting"), 208 state == AC_ON ? gettext("enable") : gettext("disable"), 209 ac_type_name(type)); 210 ret = SMF_EXIT_ERR_FATAL; 211 } 212 (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL); 213 out: 214 aconf_scf_fini(); 215 return (ret); 216 } 217 218 void 219 aconf_print(FILE *fp, int types) 220 { 221 acctconf_t ac; 222 int print_order[] = { AC_TASK, AC_PROC, AC_FLOW }; 223 int i; 224 225 for (i = 0; i < NELEM(print_order); i++) { 226 if (types & print_order[i]) { 227 aconf_init(&ac, print_order[i]); 228 aconf_print_type(&ac, fp, print_order[i]); 229 } 230 } 231 } 232 233 static void 234 aconf_print_type(acctconf_t *acp, FILE *fp, int type) 235 { 236 switch (type) { 237 case AC_TASK: 238 (void) fprintf(fp, 239 gettext(" Task accounting: %s\n"), 240 acp->state == AC_ON ? 241 gettext("active") : gettext("inactive")); 242 (void) fprintf(fp, 243 gettext(" Task accounting file: %s\n"), 244 acp->file); 245 (void) fprintf(fp, 246 gettext(" Tracked task resources: %s\n"), 247 acp->tracked); 248 (void) fprintf(fp, 249 gettext(" Untracked task resources: %s\n"), 250 acp->untracked); 251 break; 252 case AC_PROC: 253 (void) fprintf(fp, 254 gettext(" Process accounting: %s\n"), 255 acp->state == AC_ON ? 256 gettext("active") : gettext("inactive")); 257 (void) fprintf(fp, 258 gettext(" Process accounting file: %s\n"), 259 acp->file); 260 (void) fprintf(fp, 261 gettext(" Tracked process resources: %s\n"), 262 acp->tracked); 263 (void) fprintf(fp, 264 gettext("Untracked process resources: %s\n"), 265 acp->untracked); 266 break; 267 case AC_FLOW: 268 (void) fprintf(fp, 269 gettext(" Flow accounting: %s\n"), 270 acp->state == AC_ON ? 271 gettext("active") : gettext("inactive")); 272 (void) fprintf(fp, 273 gettext(" Flow accounting file: %s\n"), 274 acp->file); 275 (void) fprintf(fp, 276 gettext(" Tracked flow resources: %s\n"), 277 acp->tracked); 278 (void) fprintf(fp, 279 gettext(" Untracked flow resources: %s\n"), 280 acp->untracked); 281 break; 282 } 283 } 284 285 /* 286 * Modified properties are put on the 'props' linked list by aconf_set_string() 287 * and aconf_set_bool(). Walk the list of modified properties and write them 288 * to the repository. The list is deleted on exit. 289 */ 290 int 291 aconf_save(void) 292 { 293 scf_propertygroup_t *pg; 294 scf_transaction_t *tx; 295 props_t *p; 296 props_t *q; 297 int tx_result; 298 299 if (props == NULL) 300 return (0); 301 302 if ((pg = scf_pg_create(handle)) == NULL || 303 scf_instance_get_pg(inst, AC_PGNAME, pg) == -1 || 304 (tx = scf_transaction_create(handle)) == NULL) 305 goto out; 306 307 do { 308 if (scf_pg_update(pg) == -1 || 309 scf_transaction_start(tx, pg) == -1) 310 goto out; 311 312 for (p = props; p != NULL; p = p->next) { 313 if (scf_transaction_property_change(tx, p->entry, 314 p->propname, p->proptype) == -1) 315 goto out; 316 (void) scf_entry_add_value(p->entry, p->value); 317 } 318 tx_result = scf_transaction_commit(tx); 319 scf_transaction_reset(tx); 320 } while (tx_result == 0); 321 322 out: 323 p = props; 324 while (p != NULL) { 325 scf_value_destroy(p->value); 326 scf_entry_destroy(p->entry); 327 free(p->propname); 328 q = p->next; 329 free(p); 330 p = q; 331 } 332 props = NULL; 333 scf_transaction_destroy(tx); 334 scf_pg_destroy(pg); 335 return ((tx_result == 1) ? 0 : -1); 336 } 337 338 boolean_t 339 aconf_have_smf_auths(void) 340 { 341 char auth[NSS_BUFLEN_AUTHATTR]; 342 struct passwd *pw; 343 344 if ((pw = getpwuid(getuid())) == NULL) 345 return (B_FALSE); 346 347 if (aconf_get_string("general", "action_authorization", auth, 348 sizeof (auth)) == -1 || chkauthattr(auth, pw->pw_name) == 0) 349 return (B_FALSE); 350 351 if (aconf_get_string("general", "value_authorization", auth, 352 sizeof (auth)) == -1 || chkauthattr(auth, pw->pw_name) == 0) 353 return (B_FALSE); 354 355 if (aconf_get_string("config", "value_authorization", auth, 356 sizeof (auth)) == -1 || chkauthattr(auth, pw->pw_name) == 0) 357 return (B_FALSE); 358 359 return (B_TRUE); 360 } 361 362 const char * 363 aconf_type2fmri(int type) 364 { 365 switch (type) { 366 case AC_PROC: 367 return (FMRI_PROC_ACCT); 368 case AC_TASK: 369 return (FMRI_TASK_ACCT); 370 case AC_FLOW: 371 return (FMRI_FLOW_ACCT); 372 default: 373 die(gettext("invalid type %d\n"), type); 374 } 375 /* NOTREACHED */ 376 return (NULL); 377 } 378 379 static int 380 aconf_fmri2type(const char *fmri) 381 { 382 if (strcmp(fmri, FMRI_PROC_ACCT) == 0) 383 return (AC_PROC); 384 else if (strcmp(fmri, FMRI_TASK_ACCT) == 0) 385 return (AC_TASK); 386 else if (strcmp(fmri, FMRI_FLOW_ACCT) == 0) 387 return (AC_FLOW); 388 else 389 return (-1); 390 } 391 392 int 393 aconf_scf_init(const char *fmri) 394 { 395 if ((handle = scf_handle_create(SCF_VERSION)) == NULL || 396 scf_handle_bind(handle) == -1 || 397 (inst = scf_instance_create(handle)) == NULL || 398 scf_handle_decode_fmri(handle, fmri, NULL, NULL, inst, NULL, NULL, 399 SCF_DECODE_FMRI_EXACT) == -1) { 400 aconf_scf_fini(); 401 return (-1); 402 } 403 return (0); 404 } 405 406 void 407 aconf_scf_fini(void) 408 { 409 scf_instance_destroy(inst); 410 (void) scf_handle_unbind(handle); 411 scf_handle_destroy(handle); 412 } 413 414 static int 415 aconf_get_string(const char *pgname, const char *propname, char *buf, 416 size_t len) 417 { 418 scf_propertygroup_t *pg; 419 scf_property_t *prop; 420 scf_value_t *value; 421 int ret = 0; 422 423 if ((pg = scf_pg_create(handle)) == NULL) 424 return (-1); 425 426 if (scf_instance_get_pg_composed(inst, NULL, pgname, pg) == -1) { 427 scf_pg_destroy(pg); 428 return (-1); 429 } 430 431 if ((prop = scf_property_create(handle)) == NULL || 432 (value = scf_value_create(handle)) == NULL || 433 scf_pg_get_property(pg, propname, prop) == -1 || 434 scf_property_get_value(prop, value) == -1 || 435 scf_value_get_astring(value, buf, len) == -1) 436 ret = -1; 437 438 scf_value_destroy(value); 439 scf_property_destroy(prop); 440 scf_pg_destroy(pg); 441 return (ret); 442 } 443 444 static int 445 aconf_get_bool(const char *pgname, const char *propname, uint8_t *rval) 446 { 447 scf_propertygroup_t *pg; 448 scf_property_t *prop; 449 scf_value_t *value; 450 int ret = 0; 451 452 if ((pg = scf_pg_create(handle)) == NULL) 453 return (-1); 454 455 if (scf_instance_get_pg_composed(inst, NULL, pgname, pg) == -1) { 456 scf_pg_destroy(pg); 457 return (-1); 458 } 459 460 if ((prop = scf_property_create(handle)) == NULL || 461 (value = scf_value_create(handle)) == NULL || 462 scf_pg_get_property(pg, propname, prop) == -1 || 463 scf_property_get_value(prop, value) == -1 || 464 scf_value_get_boolean(value, rval) == -1) 465 ret = -1; 466 467 scf_value_destroy(value); 468 scf_property_destroy(prop); 469 scf_pg_destroy(pg); 470 return (ret); 471 } 472 473 int 474 aconf_set_string(const char *propname, const char *value) 475 { 476 props_t *p; 477 478 if ((p = aconf_prop(propname, SCF_TYPE_ASTRING)) == NULL) 479 return (-1); 480 481 if (scf_value_set_astring(p->value, value) == -1) 482 return (-1); 483 return (0); 484 } 485 486 int 487 aconf_set_bool(const char *propname, boolean_t value) 488 { 489 props_t *p; 490 491 if ((p = aconf_prop(propname, SCF_TYPE_BOOLEAN)) == NULL) 492 return (-1); 493 494 scf_value_set_boolean(p->value, value); 495 return (0); 496 } 497 498 static props_t * 499 aconf_prop(const char *propname, int proptype) 500 { 501 props_t *p; 502 503 if ((p = malloc(sizeof (props_t))) != NULL) { 504 if ((p->propname = strdup(propname)) == NULL) { 505 free(p); 506 return (NULL); 507 } 508 if ((p->entry = scf_entry_create(handle)) == NULL) { 509 free(p->propname); 510 free(p); 511 return (NULL); 512 } 513 if ((p->value = scf_value_create(handle)) == NULL) { 514 scf_entry_destroy(p->entry); 515 free(p->propname); 516 free(p); 517 return (NULL); 518 } 519 p->proptype = proptype; 520 p->next = props; 521 props = p; 522 } 523 return (p); 524 } 525