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