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/acctctl.h> 27 #include <assert.h> 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <unistd.h> 31 #include <string.h> 32 #include <errno.h> 33 #include <libintl.h> 34 #include <libdllink.h> 35 #include <locale.h> 36 #include <priv.h> 37 #include <libscf.h> 38 #include <zone.h> 39 40 #include "utils.h" 41 #include "aconf.h" 42 #include "res.h" 43 44 static const char USAGE[] = "\ 45 Usage:\n\ 46 acctadm [ {process | task | flow | net} ]\n\ 47 acctadm -s\n\ 48 acctadm -r [ {process | task | flow | net} ]\n\ 49 acctadm -x|-E|-D {process | task | flow | net}\n\ 50 acctadm -f filename {process | task | flow | net}\n\ 51 acctadm -e resources -d resources {process | task | flow | net}\n"; 52 53 static const char OPTS[] = "rsxf:e:d:ED"; 54 55 dladm_handle_t dld_handle = NULL; 56 57 static void 58 usage() 59 { 60 (void) fprintf(stderr, gettext(USAGE)); 61 exit(E_USAGE); 62 } 63 64 static void 65 setup_privs() 66 { 67 priv_set_t *privset; 68 69 if (seteuid(getuid()) == -1 || setegid(getgid()) == -1) 70 die(gettext("seteuid()/setegid() failed")); 71 72 /* 73 * Add our privileges and remove unneeded 'basic' privileges from the 74 * permitted set. 75 */ 76 if ((privset = priv_str_to_set("basic", ",", NULL)) == NULL) 77 die(gettext("cannot setup privileges")); 78 79 (void) priv_addset(privset, PRIV_SYS_ACCT); 80 (void) priv_addset(privset, PRIV_FILE_DAC_WRITE); 81 (void) priv_addset(privset, PRIV_SYS_DL_CONFIG); 82 (void) priv_delset(privset, PRIV_FILE_LINK_ANY); 83 (void) priv_delset(privset, PRIV_PROC_EXEC); 84 (void) priv_delset(privset, PRIV_PROC_FORK); 85 (void) priv_delset(privset, PRIV_PROC_INFO); 86 (void) priv_delset(privset, PRIV_PROC_SESSION); 87 priv_inverse(privset); 88 if (setppriv(PRIV_OFF, PRIV_PERMITTED, privset) == -1) 89 die(gettext("cannot setup privileges")); 90 priv_freeset(privset); 91 92 /* 93 * Clear the Inheritable and Limit sets. 94 */ 95 if ((privset = priv_allocset()) == NULL) 96 die(gettext("cannot setup privileges")); 97 priv_emptyset(privset); 98 if (setppriv(PRIV_SET, PRIV_INHERITABLE, privset) == -1 || 99 setppriv(PRIV_SET, PRIV_LIMIT, privset) == -1) 100 die(gettext("cannot setup privileges")); 101 102 /* 103 * Turn off the sys_acct, file_dac_write and dl_config privileges 104 * until needed. 105 */ 106 (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_FILE_DAC_WRITE, 107 PRIV_SYS_ACCT, PRIV_SYS_DL_CONFIG, NULL); 108 } 109 110 int 111 main(int argc, char *argv[]) 112 { 113 int c; /* options character */ 114 int type = 0; /* type of accounting */ 115 int modified = 0; /* have we modified any properties? */ 116 acctconf_t ac; /* current configuration */ 117 char *typestr = NULL; /* type of accounting argument string */ 118 char *enabled = NULL; /* enabled resources string */ 119 char *disabled = NULL; /* disabled resources string */ 120 char *file = NULL; 121 int Eflg = 0; 122 int Dflg = 0; 123 int rflg = 0; 124 int sflg = 0; 125 int xflg = 0; 126 int optcnt = 0; 127 int state; 128 const char *fmri; /* FMRI for this instance */ 129 130 setup_privs(); 131 132 (void) setlocale(LC_ALL, ""); 133 (void) textdomain(TEXT_DOMAIN); 134 (void) setprogname(argv[0]); 135 136 for (; optind < argc; optind++) { 137 while ((c = getopt(argc, argv, OPTS)) != (int)EOF) { 138 switch (c) { 139 case 'd': 140 disabled = optarg; 141 break; 142 case 'e': 143 enabled = optarg; 144 break; 145 case 'D': 146 Dflg = 1; 147 optcnt++; 148 break; 149 case 'E': 150 Eflg = 1; 151 optcnt++; 152 break; 153 case 'f': 154 file = optarg; 155 optcnt++; 156 break; 157 case 'r': 158 rflg = 1; 159 optcnt++; 160 break; 161 case 's': 162 sflg = 1; 163 optcnt++; 164 break; 165 case 'x': 166 xflg = 1; 167 optcnt++; 168 break; 169 case '?': 170 default: 171 usage(); 172 } 173 } 174 175 /* 176 * Permanently give up euid 0, egid 0 and privileges we 177 * don't need for the specified options. 178 */ 179 if (!(file || sflg)) { 180 if (setreuid(getuid(), getuid()) == -1 || 181 setregid(getgid(), getgid()) == -1) 182 die(gettext("setreuid()/setregid() failed")); 183 (void) priv_set(PRIV_OFF, PRIV_PERMITTED, 184 PRIV_FILE_DAC_WRITE, NULL); 185 } 186 if (!(disabled || enabled || Dflg || Eflg || file || sflg || 187 xflg)) 188 (void) priv_set(PRIV_OFF, PRIV_PERMITTED, 189 PRIV_SYS_ACCT, PRIV_SYS_DL_CONFIG, NULL); 190 191 if (optind < argc) { 192 if (typestr != NULL) { 193 warn(gettext("illegal argument -- %s\n"), 194 argv[optind]); 195 usage(); 196 } else { 197 typestr = argv[optind]; 198 } 199 } 200 } 201 if (typestr != NULL) { 202 if (strcmp(typestr, "process") == 0 || 203 strcmp(typestr, "proc") == 0) 204 type |= AC_PROC; 205 else if (strcmp(typestr, "task") == 0) 206 type |= AC_TASK; 207 else if (strcmp(typestr, "flow") == 0) 208 type |= AC_FLOW; 209 else if (strcmp(typestr, "net") == 0) 210 type |= AC_NET; 211 else { 212 warn(gettext("unknown accounting type -- %s\n"), 213 typestr); 214 usage(); 215 } 216 } else 217 type = AC_PROC | AC_TASK | AC_FLOW | AC_NET; 218 219 /* 220 * Drop the DL config privilege if we are not working with 221 * net. 222 */ 223 if ((type & AC_NET) == 0) { 224 (void) priv_set(PRIV_OFF, PRIV_PERMITTED, 225 PRIV_SYS_DL_CONFIG, NULL); 226 } 227 /* 228 * check for invalid options 229 */ 230 if (optcnt > 1) 231 usage(); 232 233 /* 234 * XXX For AC_NET, enabled/disabled should only be "basic" or 235 * "extended" - need to check it here. 236 */ 237 if ((enabled || disabled) && (rflg || Dflg || sflg || xflg || Eflg)) 238 usage(); 239 240 if ((file || xflg || Dflg || Eflg || enabled || disabled) && 241 !typestr) { 242 warn(gettext("accounting type must be specified\n")); 243 usage(); 244 } 245 246 if (rflg) { 247 printgroups(type); 248 return (E_SUCCESS); 249 } 250 251 /* 252 * If no arguments have been passed then just print out the current 253 * state and exit. 254 */ 255 if (!enabled && !disabled && !file && 256 !Eflg && !rflg && !Dflg && !sflg && !xflg) { 257 aconf_print(stdout, type); 258 return (E_SUCCESS); 259 } 260 261 /* Open the libdladm handle */ 262 if (dladm_open(&dld_handle) != DLADM_STATUS_OK) 263 die(gettext("failed to open dladm handle\n")); 264 265 /* 266 * smf(5) start method. The FMRI to operate on is retrieved from the 267 * SMF_FMRI environment variable that the restarter provides. 268 */ 269 if (sflg) { 270 if ((fmri = getenv("SMF_FMRI")) != NULL) { 271 int ret = aconf_setup(fmri); 272 dladm_close(dld_handle); 273 return (ret); 274 } 275 276 die(gettext("-s option should only be invoked by smf(5)\n")); 277 } 278 279 assert(type == AC_PROC || type == AC_TASK || type == AC_FLOW || 280 type == AC_NET); 281 282 if ((type == AC_FLOW || type == AC_NET) && getzoneid() != GLOBAL_ZONEID) 283 die(gettext("%s accounting cannot be configured in " 284 "non-global zones\n"), ac_type_name(type)); 285 286 fmri = aconf_type2fmri(type); 287 if (aconf_scf_init(fmri) == -1) 288 die(gettext("cannot connect to repository for %s\n"), fmri); 289 290 /* 291 * Since the sys_acct the privilege allows use of acctctl() regardless 292 * of the accounting type, we check the smf(5) authorizations granted 293 * to the user to determine whether the user is allowed to change the 294 * configuration for this particular accounting type. 295 */ 296 if (!aconf_have_smf_auths()) 297 die(gettext("insufficient authorization to change %s extended " 298 "accounting configuration\n"), ac_type_name(type)); 299 300 if (xflg) { 301 /* 302 * Turn off the specified accounting and close its file 303 */ 304 305 /* 306 * Stop net logging before turning it off so that the last 307 * set of logs can be written. 308 */ 309 if (type & AC_NET) { 310 (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, 311 PRIV_SYS_DL_CONFIG, NULL); 312 (void) dladm_stop_usagelog(dld_handle, 313 DLADM_LOGTYPE_FLOW); 314 (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, 315 PRIV_SYS_DL_CONFIG, NULL); 316 } 317 state = AC_OFF; 318 319 (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL); 320 if (acctctl(type | AC_STATE_SET, &state, sizeof (int)) == -1) 321 die(gettext("cannot disable %s accounting"), 322 ac_type_name(type)); 323 if (acctctl(type | AC_FILE_SET, NULL, 0) == -1) 324 die(gettext("cannot close %s accounting file\n"), 325 ac_type_name(type)); 326 (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL); 327 328 if (aconf_set_bool(AC_PROP_STATE, B_FALSE) == -1) 329 die(gettext("cannot update %s property\n"), 330 AC_PROP_STATE); 331 if (aconf_set_string(AC_PROP_FILE, AC_STR_NONE) == -1) 332 die(gettext("cannot update %s property\n"), 333 AC_PROP_FILE); 334 modified++; 335 } 336 337 if (enabled || disabled) { 338 char *tracked, *untracked; 339 ac_res_t *buf; 340 341 /* 342 * Enable/disable resources 343 */ 344 if ((buf = malloc(AC_BUFSIZE)) == NULL) 345 die(gettext("not enough memory\n")); 346 (void) memset(buf, 0, AC_BUFSIZE); 347 if (acctctl(type | AC_RES_GET, buf, AC_BUFSIZE) == -1) { 348 free(buf); 349 die(gettext("cannot obtain list of resources\n")); 350 } 351 if (disabled) { 352 /* 353 * Stop net logging before turning it off so that the 354 * last set of logs can be written. 355 */ 356 if (type & AC_NET) { 357 (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, 358 PRIV_SYS_DL_CONFIG, NULL); 359 (void) dladm_stop_usagelog(dld_handle, 360 strncmp(disabled, "basic", strlen("basic")) 361 == 0 ? DLADM_LOGTYPE_LINK : 362 DLADM_LOGTYPE_FLOW); 363 (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, 364 PRIV_SYS_DL_CONFIG, NULL); 365 } 366 str2buf(buf, disabled, AC_OFF, type); 367 } 368 if (enabled) 369 str2buf(buf, enabled, AC_ON, type); 370 371 (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL); 372 if (acctctl(type | AC_RES_SET, buf, AC_BUFSIZE) == -1) { 373 free(buf); 374 die(gettext("cannot enable/disable %s accounting " 375 "resources\n"), ac_type_name(type)); 376 } 377 (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL); 378 379 tracked = buf2str(buf, AC_BUFSIZE, AC_ON, type); 380 untracked = buf2str(buf, AC_BUFSIZE, AC_OFF, type); 381 if (aconf_set_string(AC_PROP_TRACKED, tracked) == -1) 382 die(gettext("cannot update %s property\n"), 383 AC_PROP_TRACKED); 384 if (aconf_set_string(AC_PROP_UNTRACKED, untracked) == -1) 385 die(gettext("cannot update %s property\n"), 386 AC_PROP_UNTRACKED); 387 /* 388 * We will enable net logging after turning it on so that 389 * it can immediately start writing log. 390 */ 391 if (type & AC_NET && enabled != NULL) { 392 /* 393 * Default logging interval for AC_NET is 20. 394 * XXX need to find the right place to 395 * configure it. 396 */ 397 (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, 398 PRIV_SYS_DL_CONFIG, NULL); 399 (void) dladm_start_usagelog(dld_handle, 400 strncmp(enabled, "basic", strlen("basic")) == 0 ? 401 DLADM_LOGTYPE_LINK : DLADM_LOGTYPE_FLOW, 20); 402 (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, 403 PRIV_SYS_DL_CONFIG, NULL); 404 } 405 free(tracked); 406 free(untracked); 407 free(buf); 408 modified++; 409 } 410 411 if (file) { 412 /* 413 * Open new accounting file 414 */ 415 (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL); 416 if (open_exacct_file(file, type) == -1) { 417 dladm_close(dld_handle); 418 exit(E_ERROR); 419 } 420 if (aconf_set_string(AC_PROP_FILE, file) == -1) 421 die(gettext("cannot update %s property\n"), 422 AC_PROP_FILE); 423 state = AC_ON; 424 425 if (acctctl(type | AC_STATE_SET, &state, sizeof (int)) == -1) 426 die(gettext("cannot enable %s accounting"), 427 ac_type_name(type)); 428 (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL); 429 430 if (aconf_set_bool(AC_PROP_STATE, B_TRUE) == -1) 431 die(gettext("cannot update %s property\n"), 432 AC_PROP_STATE); 433 modified++; 434 } 435 436 if (Dflg) { 437 /* 438 * Disable accounting 439 */ 440 441 /* 442 * Stop net logging before turning it off so that the last 443 * set of logs can be written. 444 */ 445 if (type & AC_NET) { 446 (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, 447 PRIV_SYS_DL_CONFIG, NULL); 448 (void) dladm_stop_usagelog(dld_handle, 449 DLADM_LOGTYPE_FLOW); 450 (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, 451 PRIV_SYS_DL_CONFIG, NULL); 452 } 453 state = AC_OFF; 454 455 (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL); 456 if (acctctl(type | AC_STATE_SET, &state, sizeof (int)) == -1) 457 die(gettext("cannot disable %s accounting"), 458 ac_type_name(type)); 459 (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL); 460 461 if (aconf_set_bool(AC_PROP_STATE, B_FALSE) == -1) 462 die(gettext("cannot update %s property\n"), 463 AC_PROP_STATE); 464 modified++; 465 } 466 467 if (Eflg) { 468 /* 469 * Enable accounting 470 */ 471 state = AC_ON; 472 473 (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL); 474 if (acctctl(type | AC_STATE_SET, &state, sizeof (int)) == -1) 475 die(gettext("cannot enable %s accounting"), 476 ac_type_name(type)); 477 (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL); 478 479 if (aconf_set_bool(AC_PROP_STATE, B_TRUE) == -1) 480 die(gettext("cannot update %s property\n"), 481 AC_PROP_STATE); 482 modified++; 483 if (type & AC_NET) { 484 /* 485 * Default logging interval for AC_NET is 20, 486 * XXX need to find the right place to configure it. 487 */ 488 (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, 489 PRIV_SYS_DL_CONFIG, NULL); 490 (void) dladm_start_usagelog(dld_handle, 491 DLADM_LOGTYPE_FLOW, 20); 492 (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, 493 PRIV_SYS_DL_CONFIG, NULL); 494 } 495 } 496 (void) priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_SYS_ACCT, NULL); 497 498 if (modified) { 499 char *smf_state; 500 501 if (aconf_save() == -1) 502 die(gettext("cannot save %s accounting " 503 "configuration\n"), ac_type_name(type)); 504 505 /* 506 * Enable or disable the instance depending on the effective 507 * configuration. If the effective configuration results in 508 * extended accounting being 'on', the instance is enabled so 509 * the configuration is applied at the next boot. 510 */ 511 smf_state = smf_get_state(fmri); 512 aconf_init(&ac, type); 513 514 if (ac.state == AC_ON || 515 strcmp(ac.file, AC_STR_NONE) != 0 || 516 strcmp(ac.tracked, AC_STR_NONE) != 0) { 517 if (strcmp(smf_state, SCF_STATE_STRING_ONLINE) != 0) 518 if (smf_enable_instance(fmri, 0) == -1) 519 die(gettext("cannot enable %s\n"), 520 fmri); 521 } else { 522 if (strcmp(smf_state, SCF_STATE_STRING_ONLINE) == 0) 523 if (smf_disable_instance(fmri, 0) == -1) 524 die(gettext("cannot disable %s\n"), 525 fmri); 526 } 527 free(smf_state); 528 } 529 aconf_scf_fini(); 530 dladm_close(dld_handle); 531 return (E_SUCCESS); 532 } 533