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(strncmp(tracked, "basic", 222 strlen("basic")) == 0 ? DLADM_LOGTYPE_LINK : 223 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