doconfig.c revision 1219:f89f56c2d9ac
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/*
24 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
25 * Use is subject to license terms.
26 */
27
28#pragma ident	"%Z%%M%	%I%	%E% SMI"
29
30/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
31/*	  All Rights Reserved  	*/
32
33#include "mt.h"
34#include <stdio.h>
35#include <string.h>
36#include <stdlib.h>
37#include <unistd.h>
38#include <fcntl.h>
39#include <ulimit.h>
40#include <wait.h>
41#include <sys/types.h>
42#include <sys/stat.h>
43#include <stropts.h>
44#include <ctype.h>
45#include <sys/conf.h>
46#include <errno.h>
47#include <signal.h>
48#include "sac.h"
49
50#define	COMMENT	'#'
51#define	NOWAIT	0
52#define	WAIT	1
53
54extern char	**_environ;
55
56static char	*eatwhite(char *);
57static int	doassign(char *);
58static int	dopush(int, char *);
59static int	dopop(int, char *);
60static int	dorun(char *, int);
61
62/*
63 * doconfig - the configuration script interpreter, if all is ok,
64 *	      return 0.  If there is a "system" error, return -1.
65 *	      If there is an error performing a command, or there
66 *	      is a syntax error, return the line number in error.
67 *
68 *	args:	fd - file descriptor to push and pop from
69 *		script - name of the configuration script
70 *		rflag - restriction flag to determine what "commands"
71 *			can be run
72 */
73
74int
75doconfig(int fd, char *script, long rflag)
76{
77	int line;		/* line counter */
78	struct stat statbuf;	/* place for stat */
79	FILE *fp;		/* file pointer for config script */
80	char buf[BUFSIZ + 1];	/* scratch buffer */
81	char *bp;		/* scratch pointer */
82	char *p;		/* scratch pointer */
83
84	/* if the script does not exist, then there is nothing to do */
85	if (stat(script, &statbuf) < 0)
86		return (0);
87
88	fp = fopen(script, "r");
89	if (fp == NULL)
90		return (-1);
91
92	line = 0;
93	while (fgets(buf, BUFSIZ, fp)) {
94		line++;
95		p = strchr(buf, '\n');
96		/* if no \n, then line is too long */
97		if (p == NULL) {
98			(void) fclose(fp);
99			return (line);
100		}
101		*p = '\0';
102
103		/* remove comments */
104		p = strchr(buf, COMMENT);
105		if (p)
106			*p = '\0';
107
108		/* remove leading whitespace */
109		bp = eatwhite(buf);
110		/* see if anything is left */
111		if (*bp == '\0')
112			continue;
113
114		/* remove trailing whitespace */
115		p = &buf[strlen(buf) - 1];
116		while (*p && isspace(*p))
117			*p-- = '\0';
118
119		/* get the command */
120		p = bp;
121		while (*p && !isspace(*p))
122			p++;
123		if (*p)
124			*p++ = '\0';
125		/* skip any whitespace here too (between command and args) */
126		p = eatwhite(p);
127
128		if (strcmp(bp, "assign") == 0) {
129			if ((rflag & NOASSIGN) || doassign(p)) {
130				(void) fclose(fp);
131				return (line);
132			}
133		} else if (strcmp(bp, "push") == 0) {
134			if (dopush(fd, p)) {
135				(void) fclose(fp);
136				return (line);
137			}
138		} else if (strcmp(bp, "pop") == 0) {
139			if (dopop(fd, p)) {
140				(void) fclose(fp);
141				return (line);
142			}
143		} else if (strcmp(bp, "run") == 0) {
144			if ((rflag & NORUN) || dorun(p, NOWAIT)) {
145				(void) fclose(fp);
146				return (line);
147			}
148		} else if (strcmp(bp, "runwait") == 0) {
149			if ((rflag & NORUN) || dorun(p, WAIT)) {
150				(void) fclose(fp);
151				return (line);
152			}
153		} else {
154			/* unknown command */
155			(void) fclose(fp);
156			return (line);
157		}
158	}
159	if (!feof(fp)) {
160		(void) fclose(fp);
161		return (-1);
162	}
163	(void) fclose(fp);
164	return (0);
165}
166
167
168/*
169 * doassign - handle an `assign' command
170 *
171 *	args:	p - assignment string
172 */
173
174
175static int
176doassign(char *p)
177{
178	char *var;		/* environment variable to be assigned */
179	char val[BUFSIZ];	/* and the value to be assigned to it */
180	char scratch[BUFSIZ];	/* scratch buffer */
181	char delim;		/* delimiter char seen (for quoted strings ) */
182	char *tp;		/* scratch pointer */
183
184	if (*p == '\0')
185		return (-1);
186	var = p;
187	/* skip first token, but stop if we see a '=' */
188	while (*p && !isspace(*p) && (*p != '='))
189		p++;
190
191	/* if we found end of string, it's an error */
192	if (*p == '\0')
193		return (-1);
194
195	/* if we found a space, look for the '=', otherwise it's an error */
196	if (isspace(*p)) {
197		*p++ = '\0';
198		while (*p && isspace(*p))
199			p++;
200		if (*p == '\0')
201			return (-1);
202		if (*p == '=')
203			p++;
204		else
205			return (-1);
206	} else {
207		/* skip over '=' */
208		*p = '\0';
209		p++;
210	}
211
212	/* skip over any whitespace */
213	p = eatwhite(p);
214	if (*p == '\'' || *p == '"') {
215		/* handle quoted values */
216		delim = *p++;
217		tp = val;
218		for (;;) {
219			if (*p == '\0') {
220				return (-1);
221			} else if (*p == delim) {
222				if (*(p - 1) != '\\')
223					break;
224				else
225					*(tp - 1) = *p++;
226			} else
227				*tp++ = *p++;
228		}
229		*tp = '\0';
230		/*
231		 * these assignments make the comment below true
232		 * (values of tp and p
233		 */
234		tp = ++p;
235		p = val;
236	} else {
237		tp = p;
238		/* look for end of token */
239		while (*tp && !isspace(*tp))
240			tp++;
241	}
242
243/*
244 * at this point, p points to the value, and tp points to the
245 * end of the token.  check to make sure there is no garbage on
246 * the end of the line
247 */
248
249	if (*tp)
250		return (-1);
251	(void) snprintf(scratch, sizeof (scratch), "%s=%s", var, p);
252	/* note: need to malloc fresh space so putenv works */
253	tp = malloc(strlen(scratch) + 1);
254	if (tp == NULL)
255		return (-1);
256	(void) strcpy(tp, scratch);
257	if (putenv(tp))
258		return (-1);
259	return (0);
260}
261
262
263/*
264 * dopush - handle a `push' command
265 *
266 *	args:	fd - file descriptor to push on
267 *		p - list of modules to push
268 */
269
270
271static int
272dopush(int fd, char *p)
273{
274	char *tp;	/* scratch pointer */
275	int i;		/* scratch variable */
276	int npush;	/* count # of modules pushed */
277
278	if (*p == '\0')
279		return (-1);
280	npush = 0;
281	for (;;) {
282		if (*p == '\0')		/* found end of line */
283			return (0);
284		p = eatwhite(p);
285		if (*p == '\0')
286			return (-1);
287		tp = p;
288		while (*tp && !isspace(*tp) && (*tp != ','))
289			tp++;
290		if (*tp)
291			*tp++ = '\0';
292		if (ioctl(fd, I_PUSH, p) < 0) {
293
294/*
295 * try to pop all that we've done, if pop fails it doesn't matter because
296 * nothing can be done anyhow
297 */
298
299			for (i = 0; i < npush; ++i)
300				(void) ioctl(fd, I_POP, 0);
301			return (-1);
302		}
303		/* count the number of modules we've pushed */
304		npush++;
305		p = tp;
306	}
307}
308
309
310/*
311 * dopop - handle a `pop' command
312 *
313 *	args:	fd - file descriptor to pop from
314 *		p - name of module to pop to or ALL (null means pop top only)
315 */
316
317
318static int
319dopop(int fd, char *p)
320{
321	char *modp;		/* module name from argument to pop */
322	char buf[FMNAMESZ + 1];	/* scratch buffer */
323
324	if (*p == '\0') {
325		/* just a pop with no args */
326		if (ioctl(fd, I_POP, 0) < 0)
327			return (-1);
328		return (0);
329	}
330
331	/* skip any whitespace in between */
332	p = eatwhite(p);
333	modp = p;
334	/* find end of module name */
335	while (*p && !isspace(*p))
336		p++;
337
338	if (*p)		/* if not end of line, extra junk on line */
339		return (-1);
340	if (strcmp(modp, "ALL") == 0) {
341		/* it's the magic name, pop them all */
342		while (ioctl(fd, I_POP, 0) == 0)
343			;
344		/* After all popped, we'll get an EINVAL, which is expected */
345		if (errno != EINVAL)
346			return (-1);
347		return (0);
348	}
349	/* check to see if the named module is on the stream */
350	if (ioctl(fd, I_FIND, modp) != 1)
351		return (-1);
352
353	/* pop them until the right one is on top */
354	for (;;) {
355		if (ioctl(fd, I_LOOK, buf) < 0)
356			return (-1);
357		if (strcmp(modp, buf) == 0)
358			/* we're done */
359			return (0);
360		if (ioctl(fd, I_POP, 0) < 0)
361			return (-1);
362	}
363	/* NOTREACHED */
364}
365
366
367/*
368 * dorun - handle a `run' command
369 *
370 *	args:	p - command line to run
371 *		waitflag - flag indicating whether a wait should be done
372 */
373
374
375static int
376dorun(char *p, int waitflg)
377{
378	char *tp;		/* scratch pointer */
379	char *ep;		/* scratch pointer (end of token) */
380	char savech;		/* hold area */
381	int status;		/* return status from wait */
382	pid_t pid;		/* pid of child proc */
383	pid_t rpid;		/* returned pid from wait */
384	void (*func)();		/* return from signal */
385
386	if (*p == '\0')
387		return (-1);
388
389	/*
390	 * get first token
391	 */
392
393	for (tp = p; *tp && !isspace(*tp); ++tp)
394		;
395	savech = '\0';
396	if (*tp) {
397		savech = *tp;
398		*tp = '\0';
399	}
400
401	/*
402	 * look for built-in's
403	 */
404
405	if (strcmp(p, "cd") == 0) {
406		*tp = savech;
407		tp = eatwhite(tp);
408		if (*tp == '\0')
409			/* if nothing there, try to cd to $HOME */
410			tp = getenv("HOME");
411		if (chdir(tp) < 0)
412			return (-1);
413	} else if (strcmp(p, "ulimit") == 0) {
414		*tp = savech;
415		tp = eatwhite(tp);
416		/* must have an argument */
417		if (*tp == '\0')
418			return (-1);
419		/* make sure nothing appears on line after arg */
420		for (ep = tp; *ep && !isspace(*ep); ++ep)
421			;
422		ep = eatwhite(ep);
423		if (*ep)
424			return (-1);
425		if (!isdigit(*tp))
426			return (-1);
427
428		if (ulimit(2, atoi(tp)) < 0)
429			return (-1);
430	} else if (strcmp(p, "umask") == 0) {
431		*tp = savech;
432		tp = eatwhite(tp);
433		/* must have an argument */
434		if (*tp == '\0')
435			return (-1);
436		/* make sure nothing appears on line after arg */
437		for (ep = tp; *ep && !isspace(*ep); ++ep)
438			;
439		ep = eatwhite(ep);
440		if (*ep)
441			return (-1);
442		if (!isdigit(*tp))
443			return (-1);
444		(void) umask(strtol(tp, NULL, 8));
445	} else {
446		/* not a built-in */
447		*tp = savech;
448		func = signal(SIGCLD, SIG_DFL);
449		if ((pid = fork()) < 0) {
450			(void) signal(SIGCLD, func);
451			return (-1);
452		}
453		if (pid) {
454			if (waitflg == WAIT) {
455				status = 0;
456				rpid = -1;
457				while (rpid != pid)
458					rpid = wait(&status);
459				if (status) {
460					/* child failed */
461					(void) signal(SIGCLD, func);
462					return (-1);
463				}
464			}
465			(void) signal(SIGCLD, func);
466		} else {
467			/* set IFS for security */
468			(void) putenv("IFS=\" \"");
469			/*
470			 * need to close all files to prevent unauthorized
471			 * access in the children.  Setup stdin, stdout,
472			 * and stderr to /dev/null.
473			 */
474			closefrom(0);
475			/* stdin */
476			if (open("/dev/null", O_RDWR) != 0)
477				return (-1);
478			/* stdout */
479			if (dup(0) != 1)
480				return (-1);
481			/* stderr */
482			if (dup(0) != 2)
483				return (-1);
484			(void) execle("/usr/bin/sh", "sh", "-c",
485							p, 0, _environ);
486			/*
487			 * if we get here, there is a problem - remember that
488			 * this is the child
489			 */
490			exit(1);
491		}
492	}
493	return (0);
494}
495
496
497/*
498 * eatwhite - swallow any leading whitespace, return pointer to first
499 *	      non-white space character or to terminating null character
500 *	      if nothing else is there
501 *
502 *	args:	p - string to parse
503 */
504
505static char *
506eatwhite(char *p)
507{
508	while (*p && isspace(*p))
509		p++;
510	return (p);
511}
512