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 (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 23 /* All Rights Reserved */ 24 25 /* 26 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 27 * Use is subject to license terms. 28 */ 29 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <unistd.h> 34 #include <fcntl.h> 35 #include <errno.h> 36 #include <string.h> 37 #include <limits.h> 38 #include <wait.h> 39 #include <zone.h> 40 #include <sys/types.h> 41 #include <sys/stat.h> 42 #include <sys/priocntl.h> 43 44 #include "dispadmin.h" 45 46 /* 47 * This file contains the code implementing the class independent part 48 * of the dispadmin command. Most of the functionality of the dispadmin 49 * command is provided by the class specific sub-commands, the code for 50 * which is elsewhere. The class independent part of the command is 51 * responsible for switching out to the appropriate class specific 52 * sub-command based on the user supplied class argument. 53 * Code in this file should never assume any knowledge of any specific 54 * scheduler class (other than the SYS class). 55 */ 56 57 #define BASENMSZ 16 58 #define BUFSZ (PATH_MAX + 80) 59 #define CLASSPATH "/usr/lib/class" 60 #define CONFIGPATH "/etc/dispadmin.conf" 61 #define CONFIGOWNER 0 /* uid 0 (root) */ 62 #define CONFIGGROUP 1 /* gid 1 (other) */ 63 #define CONFIGPERM (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) /* 0644 */ 64 #define TOKENNAME "DEFAULT_SCHEDULER" 65 66 extern char *basename(); 67 68 static char usage[] = 69 "usage: dispadmin -l\n\ 70 dispadmin -c class [class-specific options]\n\ 71 dispadmin -d [class]\n"; 72 73 static char basenm[BASENMSZ]; 74 static char cmdpath[PATH_MAX]; 75 76 static void print_classlist(); 77 static void exec_cscmd(char *, char **); 78 static void set_scheduler(char *); 79 static void class_info(pcinfo_t *); 80 static void set_default_class(); 81 82 int 83 main(int argc, char **argv) 84 { 85 extern char *optarg; 86 extern int optind, opterr; 87 88 int c; 89 int uflag, cflag, dflag, lflag, csoptsflag; 90 char *clname; 91 92 (void) strncpy(cmdpath, argv[0], PATH_MAX); 93 (void) strncpy(basenm, basename(argv[0]), BASENMSZ); 94 cflag = dflag = lflag = uflag = csoptsflag = 0; 95 opterr = 0; 96 while ((c = getopt(argc, argv, "c:dlu")) != -1) { 97 switch (c) { 98 99 case 'c': 100 cflag++; 101 clname = optarg; 102 break; 103 104 case 'd': 105 dflag++; 106 clname = argv[optind]; 107 break; 108 109 case 'l': 110 lflag++; 111 break; 112 113 case 'u': 114 uflag++; 115 break; 116 117 118 case '?': 119 /* 120 * We assume for now that any option that 121 * getopt() doesn't recognize is intended for a 122 * class specific subcommand. 123 */ 124 csoptsflag++; 125 if (argv[optind] && argv[optind][0] != '-') { 126 127 128 /* 129 * Class specific option takes an 130 * argument which we skip over for now. 131 */ 132 optind++; 133 } 134 break; 135 136 default: 137 break; 138 } 139 } 140 141 if (lflag) { 142 if (uflag || cflag || dflag || csoptsflag) 143 fatalerr(usage); 144 145 print_classlist(); 146 exit(0); 147 148 } else if (uflag) { 149 if (lflag || dflag || csoptsflag) 150 fatalerr(usage); 151 152 set_default_class(); 153 } else if (cflag) { 154 if (lflag || dflag) 155 fatalerr(usage); 156 157 exec_cscmd(clname, argv); 158 159 } else if (dflag) { 160 if (cflag || lflag || csoptsflag) 161 fatalerr(usage); 162 set_scheduler(clname); 163 exit(0); 164 165 } else { 166 fatalerr(usage); 167 } 168 return (1); 169 } 170 171 172 /* 173 * Print the heading for the class list and execute the 174 * class specific sub-command with the -l option for each 175 * configured class. 176 */ 177 static void 178 print_classlist() 179 { 180 id_t cid; 181 int nclass; 182 pcinfo_t pcinfo; 183 184 if ((nclass = priocntl(0, 0, PC_GETCLINFO, NULL)) == -1) 185 fatalerr("%s: Can't get number of configured classes\n", 186 cmdpath); 187 188 (void) printf("CONFIGURED CLASSES\n==================\n\n"); 189 (void) printf("SYS\t(System Class)\n"); 190 (void) fflush(stdout); 191 for (cid = 1; cid < nclass; cid++) { 192 pcinfo.pc_cid = cid; 193 if (priocntl(0, 0, PC_GETCLINFO, (caddr_t)&pcinfo) == -1) 194 fatalerr("%s: Can't get class name (class ID = %d)\n", 195 cmdpath, cid); 196 class_info(&pcinfo); 197 } 198 } 199 200 201 /* 202 * Execute the appropriate class specific sub-command for the class 203 * specified by clname, passing it the arguments in subcmdargv. 204 */ 205 static void 206 exec_cscmd(char *clname, char **subcmdargv) 207 { 208 pcinfo_t pcinfo; 209 char subcmdpath[PATH_MAX]; 210 211 /* 212 * Do a quick check to make sure clname is valid. 213 * We could just wait and see if the exec below 214 * succeeds but we wouldn't know much about the reason. 215 * This way we can give the user a more meaningful error 216 * message. 217 */ 218 (void) strncpy(pcinfo.pc_clname, clname, PC_CLNMSZ); 219 if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1) 220 fatalerr("%s: Invalid or unconfigured class %s\n", cmdpath, 221 clname); 222 223 (void) snprintf(subcmdpath, PATH_MAX, "%s/%s/%s%s", CLASSPATH, 224 clname, clname, basenm); 225 subcmdargv[0] = subcmdpath; 226 227 (void) execv(subcmdpath, subcmdargv); 228 fatalerr("%s: Can't execute %s sub-command\n", cmdpath, clname); 229 } 230 231 static void 232 class_info(pcinfo_t *pcinfo) 233 { 234 int pid; 235 char subcmdpath[PATH_MAX]; 236 237 (void) snprintf(subcmdpath, PATH_MAX, "%s/%s/%s%s", CLASSPATH, 238 pcinfo->pc_clname, pcinfo->pc_clname, basenm); 239 if ((pid = fork()) == 0) { 240 (void) execl(subcmdpath, subcmdpath, "-l", (char *)0); 241 fatalerr("%s\n\tCan't execute %s specific subcommand\n", 242 pcinfo->pc_clname, pcinfo->pc_clname); 243 } else if (pid == (pid_t)-1) { 244 (void) fprintf(stderr, 245 "%s\nCan't execute %s specific subcommand)\n", 246 pcinfo->pc_clname, pcinfo->pc_clname); 247 } else { 248 (void) wait(NULL); 249 } 250 } 251 252 /* 253 * Return the current default scheduling class as specified in 254 * /etc/dispadmin.conf. 255 */ 256 static char * 257 read_default_file(FILE *fp) 258 { 259 char buf[BUFSZ]; 260 int line; 261 262 for (line = 1; fgets(buf, BUFSZ, fp) != NULL; line++) { 263 char name[BUFSZ], value[BUFSZ]; 264 int len; 265 266 if (buf[0] == '#' || buf[0] == '\n') 267 continue; 268 /* LINTED - unbounded string specifier */ 269 if (sscanf(buf, " %[^=]=%s \n%n", name, value, &len) == 2 && 270 name[0] != '\0' && value[0] != '\0' && len == strlen(buf)) { 271 272 if (strcmp(name, TOKENNAME) != 0) 273 fatalerr("\"%s\", line %d: invalid " 274 "token: %s\n", CONFIGPATH, line, name); 275 276 (void) fclose(fp); 277 return (strdup(value)); 278 } else { 279 fatalerr("\"%s\", line %d: syntax error\n", CONFIGPATH, 280 line); 281 (void) fclose(fp); 282 } 283 } 284 if (line == 1) 285 fatalerr("%s: %s is empty\n", cmdpath, CONFIGPATH); 286 return (NULL); 287 } 288 289 /* 290 * Set the default scheduling class for the system. 291 * Update /etc/dispadmin.conf if necessary. 292 */ 293 static void 294 set_scheduler(char *clname) 295 { 296 pcinfo_t pcinfo; 297 FILE *fp; 298 int fd; 299 300 if (getzoneid() != GLOBAL_ZONEID) 301 fatalerr("%s: Operation not supported in non-global zones\n", 302 cmdpath); 303 304 if (clname == NULL) { 305 if ((fd = open(CONFIGPATH, O_RDONLY, CONFIGPERM)) == -1) { 306 if (errno == ENOENT) 307 fatalerr("%s: Default scheduling class " 308 "is not set\n", cmdpath); 309 else 310 fatalerr("%s: Failed to open %s (%s)\n", 311 cmdpath, CONFIGPATH, strerror(errno)); 312 } 313 314 if ((fp = fdopen(fd, "r")) == NULL) 315 fatalerr("%s: Failed to open stream for %s (%s)\n", 316 cmdpath, CONFIGPATH, strerror(errno)); 317 clname = read_default_file(fp); 318 (void) strncpy(pcinfo.pc_clname, clname, PC_CLNMSZ); 319 320 if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1) 321 fatalerr("\"%s\", scheduling class %s is not " 322 "available\n", CONFIGPATH, clname); 323 else 324 class_info(&pcinfo); 325 return; 326 } 327 328 /* 329 * Do a quick check to make sure clname is valid class name. 330 */ 331 (void) strncpy(pcinfo.pc_clname, clname, PC_CLNMSZ); 332 if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1) 333 fatalerr("%s: Invalid or unconfigured class %s\n", cmdpath, 334 clname); 335 if ((fd = open(CONFIGPATH, O_RDWR | O_CREAT, CONFIGPERM)) == -1) 336 fatalerr("%s: Failed to open %s (%s)\n", cmdpath, CONFIGPATH, 337 strerror(errno)); 338 if ((fp = fdopen(fd, "w")) == NULL) 339 fatalerr("%s: Failed to open stream for %s\n", CONFIGPATH); 340 if (ftruncate(fd, (off_t)0) == -1) 341 fatalerr("%s: Failed to truncate %s\n", cmdpath, CONFIGPATH); 342 (void) fputs("#\n# /etc/dispadmin.conf\n#\n" 343 "# Do NOT edit this file by hand -- use dispadmin(1m) instead.\n" 344 "#\n", fp); 345 if ((fprintf(fp, "%s=%s\n", TOKENNAME, clname)) == -1) 346 fatalerr("%s: Failed to write to %s\n", cmdpath, CONFIGPATH); 347 if (fflush(fp) != 0) 348 (void) fprintf(stderr, 349 "%s: warning: failed to flush config file\n", 350 cmdpath); 351 if (fsync(fd) == -1) 352 (void) fprintf(stderr, 353 "%s: warning: failed to sync config file to disk\n", 354 cmdpath); 355 if (fchmod(fd, CONFIGPERM) == -1) 356 (void) fprintf(stderr, 357 "%s: warning: failed to reset config file mode\n", 358 cmdpath); 359 if (fchown(fd, CONFIGOWNER, CONFIGGROUP) == -1) 360 (void) fprintf(stderr, 361 "%s: warning: failed to reset config file owner\n", 362 cmdpath); 363 (void) fclose(fp); 364 365 if (priocntl(0, 0, PC_SETDFLCL, clname) == -1) 366 fatalerr("%s: failed to set default class %s in kernel: %s\n", 367 cmdpath, clname, strerror(errno)); 368 } 369 370 static void 371 set_default_class() 372 { 373 char *clname; 374 FILE *fp; 375 int fd; 376 377 if ((fd = open(CONFIGPATH, O_RDONLY, CONFIGPERM)) == -1) { 378 /* silently succeed, there is nothing to do */ 379 if (errno == ENOENT) 380 return; 381 else 382 fatalerr("%s: Failed to open %s (%s)\n", 383 cmdpath, CONFIGPATH, strerror(errno)); 384 } 385 386 if ((fp = fdopen(fd, "r")) == NULL) 387 fatalerr("%s: Failed to open stream for %s (%s)\n", 388 cmdpath, CONFIGPATH, strerror(errno)); 389 390 if ((clname = read_default_file(fp)) != NULL) { 391 if (priocntl(0, 0, PC_SETDFLCL, clname) == -1) 392 fatalerr("%s: failed to set default class %s in " 393 "kernel: %s\n", cmdpath, clname, strerror(errno)); 394 } 395 } 396