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 2006 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 warn(gettext("\"syslog\" action not valid for %s\n"), 277 rctl_name); 278 op_failures++; 279 free(gblk); 280 return; 281 } 282 283 if (enable) { 284 gblk->rcq_global_flagaction |= (action & 285 ~RCTL_GLOBAL_ACTION_MASK); 286 gblk->rcq_global_syslog_level = log_level; 287 } else { 288 gblk->rcq_global_flagaction &= ~(action & 289 ~RCTL_GLOBAL_ACTION_MASK); 290 gblk->rcq_global_syslog_level = LOG_NOTICE; 291 } 292 293 if (rctlctl(rctl_name, (rctlblk_t *)gblk, RCTLCTL_SET) == -1) { 294 warn(gettext("unable to update control block contents")); 295 op_failures++; 296 } 297 298 free(gblk); 299 } 300 301 static int 302 rctladm_get_log_level(char *action) 303 { 304 char *log_lvl_str; 305 306 /* 307 * Our syslog priority defaults to LOG_NOTICE. 308 */ 309 if (strcmp("syslog", action) == 0) 310 return (LOG_NOTICE); 311 312 if (strncmp("syslog=", action, strlen("syslog=")) != 0) 313 die(gettext("unknown action \"%s\"\n"), action); 314 315 log_lvl_str = action + strlen("syslog="); 316 317 return (rctladm_syslog_prio(log_lvl_str)); 318 } 319 320 321 static void 322 rctladm_enable(const char *rctl_name, char *action) 323 { 324 /* 325 * Two valid values: "none" and "syslog[=level]". 326 */ 327 if (strcmp("none", action) == 0) { 328 rctladm_modify_action(rctl_name, DISABLE, 329 ~RCTL_GLOBAL_ACTION_MASK, 0); 330 return; 331 } 332 333 rctladm_modify_action(rctl_name, ENABLE, RCTL_GLOBAL_SYSLOG, 334 rctladm_get_log_level(action)); 335 } 336 337 static void 338 rctladm_disable(const char *rctl_name, char *action) 339 { 340 /* 341 * Two valid values: "all" and "syslog". 342 */ 343 if (strcmp("all", action) == 0) { 344 rctladm_modify_action(rctl_name, DISABLE, 345 ~RCTL_GLOBAL_ACTION_MASK, 0); 346 return; 347 } else if (strcmp("syslog", action) == 0) { 348 rctladm_modify_action(rctl_name, DISABLE, RCTL_GLOBAL_SYSLOG, 349 0); 350 return; 351 } 352 353 die(gettext("unknown action \"%s\"\n"), action); 354 } 355 356 static void 357 rctlblk_display(FILE *f, rctlblk_t *gblk) 358 { 359 uint_t action = rctlblk_get_global_action(gblk); 360 uint_t flags = rctlblk_get_global_flags(gblk); 361 rctl_opaque_t *gopq = (rctl_opaque_t *)gblk; 362 363 if (flags & RCTL_GLOBAL_SYSLOG_NEVER) 364 (void) fprintf(f, "syslog=n/a "); 365 else if (action & RCTL_GLOBAL_SYSLOG) 366 (void) fprintf(f, "syslog=%-7s", 367 syslog_priorities[gopq->rcq_global_syslog_level]); 368 else 369 (void) fprintf(f, "syslog=off "); 370 371 if (flags & RCTL_GLOBAL_ACTION_MASK) 372 (void) fprintf(f, " ["); 373 374 if (flags & RCTL_GLOBAL_NOBASIC) 375 (void) fprintf(f, " no-basic"); 376 if (flags & RCTL_GLOBAL_LOWERABLE) 377 (void) fprintf(f, " lowerable"); 378 if (flags & RCTL_GLOBAL_DENY_ALWAYS) 379 (void) fprintf(f, " deny"); 380 if (flags & RCTL_GLOBAL_DENY_NEVER) 381 (void) fprintf(f, " no-deny"); 382 if (flags & RCTL_GLOBAL_CPU_TIME) 383 (void) fprintf(f, " cpu-time"); 384 if (flags & RCTL_GLOBAL_FILE_SIZE) 385 (void) fprintf(f, " file-size"); 386 if (flags & RCTL_GLOBAL_SIGNAL_NEVER) 387 (void) fprintf(f, " no-signal"); 388 if (flags & RCTL_GLOBAL_UNOBSERVABLE) 389 (void) fprintf(f, " no-obs"); 390 if (flags & RCTL_GLOBAL_INFINITE) 391 (void) fprintf(f, " inf"); 392 if (flags & RCTL_GLOBAL_SYSLOG_NEVER) 393 (void) fprintf(f, " no-syslog"); 394 if (flags & RCTL_GLOBAL_SECONDS) 395 (void) fprintf(f, " seconds"); 396 if (flags & RCTL_GLOBAL_BYTES) 397 (void) fprintf(f, " bytes"); 398 if (flags & RCTL_GLOBAL_COUNT) 399 (void) fprintf(f, " count"); 400 if (flags & RCTL_GLOBAL_ACTION_MASK) 401 (void) fprintf(f, " ]"); 402 403 (void) fprintf(f, "\n"); 404 } 405 406 /*ARGSUSED*/ 407 static int 408 rctl_walk_cb(const char *rctl_name, void *pvt) 409 { 410 rctlblk_t *gblk; 411 412 if ((gblk = malloc(rctlblk_size())) == NULL) 413 die(gettext("unable to allocate control block")); 414 415 if (rctlctl(rctl_name, gblk, RCTLCTL_GET) == -1) { 416 if (errno == ESRCH) 417 warn(gettext("unknown resource control: %s\n"), 418 rctl_name); 419 else 420 warn(gettext("unable to obtain %s properties"), 421 rctl_name); 422 op_failures++; 423 } else { 424 (void) printf("%-27s ", rctl_name); 425 rctlblk_display(stdout, gblk); 426 } 427 428 free(gblk); 429 430 return (0); 431 } 432 433 static void 434 rctladm_list_rctls(int optind, int argc, char *argv[]) 435 { 436 if (optind >= argc) { 437 (void) rctl_walk(rctl_walk_cb, NULL); 438 return; 439 } 440 441 for (; optind < argc; optind++) 442 (void) rctl_walk_cb(argv[optind], NULL); 443 } 444 445 int 446 main(int argc, char *argv[]) 447 { 448 int c; /* options character */ 449 char *action; 450 char *rctl; 451 452 (void) setlocale(LC_ALL, ""); 453 (void) textdomain(TEXT_DOMAIN); 454 (void) setprogname(argv[0]); 455 456 while ((c = getopt(argc, argv, OPTS)) != EOF) { 457 switch (c) { 458 case 'd': 459 dflg++; 460 action = optarg; 461 break; 462 case 'e': 463 eflg++; 464 action = optarg; 465 break; 466 case 'l': 467 lflg = 1; 468 break; 469 case 'u': 470 uflg = 1; 471 break; 472 case '?': 473 default: 474 usage(); 475 } 476 } 477 478 if (uflg) { 479 rctladm_read_config(); 480 return (E_SUCCESS); 481 } 482 483 if (lflg && (dflg || eflg)) { 484 warn(gettext("-l, -d, and -e flags are exclusive\n")); 485 usage(); 486 } 487 488 if (dflg && eflg) { 489 warn(gettext("-d and -e flags are exclusive\n")); 490 usage(); 491 } 492 493 if (dflg > 1 || eflg > 1) { 494 warn(gettext("only one -d or -e flag per line\n")); 495 usage(); 496 } 497 498 if (lflg || !(dflg || eflg)) { 499 rctladm_list_rctls(optind, argc, argv); 500 rctladm_save_config(); 501 502 return (op_failures ? E_ERROR : E_SUCCESS); 503 } 504 505 if (optind >= argc) { 506 warn(gettext("must specify one or more " 507 "resource control names\n")); 508 usage(); 509 } 510 511 for (; optind < argc; optind++) { 512 rctl = argv[optind]; 513 514 if (eflg) { 515 rctladm_enable(rctl, action); 516 rctladm_save_config(); 517 } else if (dflg) { 518 rctladm_disable(rctl, action); 519 rctladm_save_config(); 520 } else { 521 usage(); 522 } 523 } 524 525 return (op_failures ? E_ERROR : E_SUCCESS); 526 } 527