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