xref: /illumos-gate/usr/src/cmd/dispadmin/dispadmin.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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