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