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
66extern char *basename();
67
68static char usage[] =
69"usage:	dispadmin -l\n\
70	dispadmin -c class [class-specific options]\n\
71	dispadmin -d [class]\n";
72
73static char basenm[BASENMSZ];
74static char cmdpath[PATH_MAX];
75
76static void print_classlist();
77static void exec_cscmd(char *, char **);
78static void set_scheduler(char *);
79static void class_info(pcinfo_t *);
80static void set_default_class();
81
82int
83main(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 */
177static void
178print_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 */
205static void
206exec_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
231static void
232class_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 */
256static char *
257read_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 */
293static void
294set_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
370static void
371set_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