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