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 2007 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/rctl_impl.h> 29 #include <sys/types.h> 30 #include <sys/stat.h> 31 32 #include <errno.h> 33 #include <libintl.h> 34 #include <locale.h> 35 #include <rctl.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <syslog.h> 40 #include <unistd.h> 41 #include <fcntl.h> 42 43 #include "utils.h" 44 45 #define ENABLE 1 46 #define DISABLE 0 47 48 #define CONFIGPATH "/etc/rctladm.conf" 49 #define CONFIGOWNER 0 /* uid 0 (root) */ 50 #define CONFIGGROUP 1 /* gid 1 (other) */ 51 #define CONFIGPERM (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) /* 0644 */ 52 53 /* 54 * Macros to produce a quoted string containing the value of a 55 * preprocessor macro. For example, if SIZE is defined to be 256, 56 * VAL2STR(SIZE) is "256". This is used to construct format 57 * strings for scanf-family functions below. 58 */ 59 #define QUOTE(x) #x 60 #define VAL2STR(x) QUOTE(x) 61 62 static const char USAGE[] = 63 "Usage:\trctladm -l\n" 64 "\trctladm -u\n" 65 "\trctladm -e actions -d actions rctl_name\n"; 66 67 static const char OPTS[] = "d:e:lu"; 68 static int dflg, eflg, lflg, uflg; 69 70 static uint_t op_failures; 71 72 static void rctladm_enable(const char *, char *); 73 74 #define BUFSIZE 256 75 76 static void 77 usage() 78 { 79 (void) fprintf(stderr, gettext(USAGE)); 80 exit(E_USAGE); 81 } 82 83 #define LOG_HIGHEST LOG_DEBUG 84 static const char *syslog_priorities[] = { 85 "emerg", /* LOG_EMERG */ 86 "alert", /* LOG_ALERT */ 87 "crit", /* LOG_CRIT */ 88 "err", /* LOG_ERR */ 89 "warning", /* LOG_WARNING */ 90 "notice", /* LOG_NOTICE */ 91 "info", /* LOG_INFO */ 92 "debug" /* LOG_DEBUG */ 93 }; 94 95 static int 96 rctladm_syslog_prio(const char *priority) 97 { 98 uint_t i; 99 100 for (i = 0; i < LOG_HIGHEST + 1; i++) { 101 if ((strcasecmp(priority, syslog_priorities[i]) == 0)) 102 return (i); 103 } 104 105 die(gettext("unknown syslog priority \"%s\"\n"), priority); 106 107 /*NOTREACHED*/ 108 } 109 110 /*ARGSUSED*/ 111 static int 112 rctl_save_walk_cb(const char *rctl_name, void *file) 113 { 114 FILE *fp = file; 115 rctlblk_t *gblk; 116 uint_t action; 117 rctl_opaque_t *gopq; 118 119 if ((gblk = malloc(rctlblk_size())) == NULL) 120 die(gettext("unable to allocate control block")); 121 122 123 if (rctlctl(rctl_name, gblk, RCTLCTL_GET) == -1) { 124 warn(gettext("unable to obtain control block contents for %s"), 125 rctl_name); 126 } else { 127 action = rctlblk_get_global_action(gblk); 128 gopq = (rctl_opaque_t *)gblk; 129 130 (void) fprintf(fp, "%s=", rctl_name); 131 if (action & RCTL_GLOBAL_SYSLOG) 132 (void) fprintf(fp, "syslog=%s\n", 133 syslog_priorities[gopq->rcq_global_syslog_level]); 134 else 135 (void) fprintf(fp, "none\n"); 136 } 137 138 free(gblk); 139 140 return (0); 141 } 142 143 static void 144 rctladm_save_config() 145 { 146 int fd; 147 FILE *fp; 148 149 /* 150 * Non-root users shouldn't update the configuration file. 151 */ 152 if (geteuid() != 0) 153 return; 154 155 if ((fd = open(CONFIGPATH, O_WRONLY|O_CREAT|O_TRUNC, CONFIGPERM)) == -1) 156 die(gettext("failed to open %s"), CONFIGPATH); 157 158 if ((fp = fdopen(fd, "w")) == NULL) 159 die(gettext("failed to open stream for %s"), CONFIGPATH); 160 161 (void) fputs( 162 "#\n" 163 "# rctladm.conf\n" 164 "#\n" 165 "# Parameters for resource controls configuration.\n" 166 "# Do NOT edit this file by hand -- use rctladm(1m) instead.\n" 167 "#\n", 168 fp); 169 170 (void) rctl_walk(rctl_save_walk_cb, fp); 171 172 (void) fflush(fp); 173 (void) fsync(fd); 174 (void) fchmod(fd, CONFIGPERM); 175 (void) fchown(fd, CONFIGOWNER, CONFIGGROUP); 176 (void) fclose(fp); 177 } 178 179 static void 180 rctladm_setup_action(char *name, char *action, int line) 181 { 182 if (action[0] == '\0') { 183 warn(gettext("\"%s\", line %d, syntax error\n"), CONFIGPATH, 184 line); 185 return; 186 } 187 rctladm_enable(name, action); 188 } 189 190 static void 191 rctladm_read_config() 192 { 193 int fd; 194 FILE *fp; 195 char buf[BUFSIZE]; 196 char name[BUFSIZE+1], actions[BUFSIZE+1]; 197 char *action; 198 int line, len, n; 199 rctl_opaque_t *gblk; 200 201 /* 202 * Non-root users shouldn't do this. 203 */ 204 if (geteuid() != 0) 205 die(gettext("you must be root to use this option\n")); 206 207 if ((fd = open(CONFIGPATH, O_RDONLY, CONFIGPERM)) == -1) 208 die(gettext("failed to open %s"), CONFIGPATH); 209 210 if ((fp = fdopen(fd, "r")) == NULL) 211 die(gettext("failed to open stream for %s"), CONFIGPATH); 212 213 if ((gblk = malloc(rctlblk_size())) == NULL) 214 die(gettext("unable to allocate control block")); 215 216 for (line = 1; fgets(buf, BUFSIZE, fp) != NULL; line++) { 217 /* 218 * Skip comment lines and empty lines. 219 */ 220 if (buf[0] == '#' || buf[0] == '\n') 221 continue; 222 223 /* 224 * Look for "rctl_name=action;action;...;action, with 225 * optional whitespace on either side, terminated by a newline, 226 * and consuming the whole line. 227 */ 228 n = sscanf(buf, 229 " %" VAL2STR(BUFSIZE) "[^=]=%" VAL2STR(BUFSIZE) "s \n%n", 230 name, actions, &len); 231 if (n >= 1 && name[0] != '\0' && 232 (n == 1 || len == strlen(buf))) { 233 if (n == 1) { 234 warn(gettext("\"%s\", line %d, syntax error\n"), 235 CONFIGPATH, line); 236 continue; 237 } 238 if (rctlctl(name, (rctlblk_t *)gblk, 239 RCTLCTL_GET) == -1) { 240 warn(gettext("\"%s\", line %d, unknown resource" 241 " control: %s\n"), CONFIGPATH, line, name); 242 continue; 243 } 244 if (actions[0] == ';') { 245 warn(gettext("\"%s\", line %d, syntax error\n"), 246 CONFIGPATH, line); 247 continue; 248 } 249 action = strtok(actions, ";"); 250 rctladm_setup_action(name, action, line); 251 while (action = strtok(NULL, ";")) 252 rctladm_setup_action(name, action, line); 253 } 254 } 255 256 if (line == 1) 257 die(gettext("failed to read rctl configuration from \"%s\""), 258 CONFIGPATH); 259 free(gblk); 260 (void) fclose(fp); 261 } 262 263 static void 264 rctladm_modify_action(const char *rctl_name, uint_t enable, uint_t action, 265 int log_level) 266 { 267 rctl_opaque_t *gblk; 268 269 if ((gblk = malloc(rctlblk_size())) == NULL) 270 die(gettext("unable to allocate control block")); 271 272 if (rctlctl(rctl_name, (rctlblk_t *)gblk, RCTLCTL_GET) == -1) 273 die(gettext("unable to obtain resource control block")); 274 275 if ((gblk->rcq_global_flagaction & RCTL_GLOBAL_SYSLOG_NEVER) && 276 (action == RCTL_GLOBAL_SYSLOG)) { 277 warn(gettext("\"syslog\" action not valid for %s\n"), 278 rctl_name); 279 op_failures++; 280 free(gblk); 281 return; 282 } 283 284 if (enable) { 285 gblk->rcq_global_flagaction |= (action & 286 ~RCTL_GLOBAL_ACTION_MASK); 287 gblk->rcq_global_syslog_level = log_level; 288 } else { 289 gblk->rcq_global_flagaction &= ~(action & 290 ~RCTL_GLOBAL_ACTION_MASK); 291 gblk->rcq_global_syslog_level = LOG_NOTICE; 292 } 293 294 if (rctlctl(rctl_name, (rctlblk_t *)gblk, RCTLCTL_SET) == -1) { 295 warn(gettext("unable to update control block contents")); 296 op_failures++; 297 } 298 299 free(gblk); 300 } 301 302 static int 303 rctladm_get_log_level(char *action) 304 { 305 char *log_lvl_str; 306 307 /* 308 * Our syslog priority defaults to LOG_NOTICE. 309 */ 310 if (strcmp("syslog", action) == 0) 311 return (LOG_NOTICE); 312 313 if (strncmp("syslog=", action, strlen("syslog=")) != 0) 314 die(gettext("unknown action \"%s\"\n"), action); 315 316 log_lvl_str = action + strlen("syslog="); 317 318 return (rctladm_syslog_prio(log_lvl_str)); 319 } 320 321 322 static void 323 rctladm_enable(const char *rctl_name, char *action) 324 { 325 /* 326 * Two valid values: "none" and "syslog[=level]". 327 */ 328 if (strcmp("none", action) == 0) { 329 rctladm_modify_action(rctl_name, DISABLE, 330 ~RCTL_GLOBAL_ACTION_MASK, 0); 331 return; 332 } 333 334 rctladm_modify_action(rctl_name, ENABLE, RCTL_GLOBAL_SYSLOG, 335 rctladm_get_log_level(action)); 336 } 337 338 static void 339 rctladm_disable(const char *rctl_name, char *action) 340 { 341 /* 342 * Two valid values: "all" and "syslog". 343 */ 344 if (strcmp("all", action) == 0) { 345 rctladm_modify_action(rctl_name, DISABLE, 346 ~RCTL_GLOBAL_ACTION_MASK, 0); 347 return; 348 } else if (strcmp("syslog", action) == 0) { 349 rctladm_modify_action(rctl_name, DISABLE, RCTL_GLOBAL_SYSLOG, 350 0); 351 return; 352 } 353 354 die(gettext("unknown action \"%s\"\n"), action); 355 } 356 357 static void 358 rctlblk_display(FILE *f, rctlblk_t *gblk) 359 { 360 uint_t action = rctlblk_get_global_action(gblk); 361 uint_t flags = rctlblk_get_global_flags(gblk); 362 rctl_opaque_t *gopq = (rctl_opaque_t *)gblk; 363 364 if (flags & RCTL_GLOBAL_SYSLOG_NEVER) 365 (void) fprintf(f, "syslog=n/a "); 366 else if (action & RCTL_GLOBAL_SYSLOG) 367 (void) fprintf(f, "syslog=%-7s", 368 syslog_priorities[gopq->rcq_global_syslog_level]); 369 else 370 (void) fprintf(f, "syslog=off "); 371 372 if (flags & RCTL_GLOBAL_ACTION_MASK) 373 (void) fprintf(f, " ["); 374 375 if (flags & RCTL_GLOBAL_NOBASIC) 376 (void) fprintf(f, " no-basic"); 377 if (flags & RCTL_GLOBAL_LOWERABLE) 378 (void) fprintf(f, " lowerable"); 379 if (flags & RCTL_GLOBAL_DENY_ALWAYS) 380 (void) fprintf(f, " deny"); 381 if (flags & RCTL_GLOBAL_DENY_NEVER) 382 (void) fprintf(f, " no-deny"); 383 if (flags & RCTL_GLOBAL_CPU_TIME) 384 (void) fprintf(f, " cpu-time"); 385 if (flags & RCTL_GLOBAL_FILE_SIZE) 386 (void) fprintf(f, " file-size"); 387 if (flags & RCTL_GLOBAL_SIGNAL_NEVER) 388 (void) fprintf(f, " no-signal"); 389 if (flags & RCTL_GLOBAL_UNOBSERVABLE) 390 (void) fprintf(f, " no-obs"); 391 if (flags & RCTL_GLOBAL_INFINITE) 392 (void) fprintf(f, " inf"); 393 if (flags & RCTL_GLOBAL_SYSLOG_NEVER) 394 (void) fprintf(f, " no-syslog"); 395 if (flags & RCTL_GLOBAL_SECONDS) 396 (void) fprintf(f, " seconds"); 397 if (flags & RCTL_GLOBAL_BYTES) 398 (void) fprintf(f, " bytes"); 399 if (flags & RCTL_GLOBAL_COUNT) 400 (void) fprintf(f, " count"); 401 if (flags & RCTL_GLOBAL_ACTION_MASK) 402 (void) fprintf(f, " ]"); 403 404 (void) fprintf(f, "\n"); 405 } 406 407 /*ARGSUSED*/ 408 static int 409 rctl_walk_cb(const char *rctl_name, void *pvt) 410 { 411 rctlblk_t *gblk; 412 413 if ((gblk = malloc(rctlblk_size())) == NULL) 414 die(gettext("unable to allocate control block")); 415 416 if (rctlctl(rctl_name, gblk, RCTLCTL_GET) == -1) { 417 if (errno == ESRCH) 418 warn(gettext("unknown resource control: %s\n"), 419 rctl_name); 420 else 421 warn(gettext("unable to obtain %s properties"), 422 rctl_name); 423 op_failures++; 424 } else { 425 (void) printf("%-27s ", rctl_name); 426 rctlblk_display(stdout, gblk); 427 } 428 429 free(gblk); 430 431 return (0); 432 } 433 434 static void 435 rctladm_list_rctls(int optind, int argc, char *argv[]) 436 { 437 if (optind >= argc) { 438 (void) rctl_walk(rctl_walk_cb, NULL); 439 return; 440 } 441 442 for (; optind < argc; optind++) 443 (void) rctl_walk_cb(argv[optind], NULL); 444 } 445 446 int 447 main(int argc, char *argv[]) 448 { 449 int c; /* options character */ 450 char *action; 451 char *rctl; 452 453 (void) setlocale(LC_ALL, ""); 454 (void) textdomain(TEXT_DOMAIN); 455 (void) setprogname(argv[0]); 456 457 while ((c = getopt(argc, argv, OPTS)) != EOF) { 458 switch (c) { 459 case 'd': 460 dflg++; 461 action = optarg; 462 break; 463 case 'e': 464 eflg++; 465 action = optarg; 466 break; 467 case 'l': 468 lflg = 1; 469 break; 470 case 'u': 471 uflg = 1; 472 break; 473 case '?': 474 default: 475 usage(); 476 } 477 } 478 479 if (uflg) { 480 rctladm_read_config(); 481 return (E_SUCCESS); 482 } 483 484 if (lflg && (dflg || eflg)) { 485 warn(gettext("-l, -d, and -e flags are exclusive\n")); 486 usage(); 487 } 488 489 if (dflg && eflg) { 490 warn(gettext("-d and -e flags are exclusive\n")); 491 usage(); 492 } 493 494 if (dflg > 1 || eflg > 1) { 495 warn(gettext("only one -d or -e flag per line\n")); 496 usage(); 497 } 498 499 if (lflg || !(dflg || eflg)) { 500 rctladm_list_rctls(optind, argc, argv); 501 rctladm_save_config(); 502 503 return (op_failures ? E_ERROR : E_SUCCESS); 504 } 505 506 if (optind >= argc) { 507 warn(gettext("must specify one or more " 508 "resource control names\n")); 509 usage(); 510 } 511 512 for (; optind < argc; optind++) { 513 rctl = argv[optind]; 514 515 if (eflg) { 516 rctladm_enable(rctl, action); 517 rctladm_save_config(); 518 } else if (dflg) { 519 rctladm_disable(rctl, action); 520 rctladm_save_config(); 521 } else { 522 usage(); 523 } 524 } 525 526 return (op_failures ? E_ERROR : E_SUCCESS); 527 } 528