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