OpenSolaris_b135/lib/libnsl/saf/doconfig.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include "mt.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <ulimit.h>
#include <wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stropts.h>
#include <ctype.h>
#include <sys/conf.h>
#include <errno.h>
#include <signal.h>
#include "sac.h"

#define	COMMENT	'#'
#define	NOWAIT	0
#define	WAIT	1

static char	*eatwhite(char *);
static int	doassign(char *);
static int	dopush(int, char *);
static int	dopop(int, char *);
static int	dorun(char *, int);

/*
 * doconfig - the configuration script interpreter, if all is ok,
 *	      return 0.  If there is a "system" error, return -1.
 *	      If there is an error performing a command, or there
 *	      is a syntax error, return the line number in error.
 *
 *	args:	fd - file descriptor to push and pop from
 *		script - name of the configuration script
 *		rflag - restriction flag to determine what "commands"
 *			can be run
 */

int
doconfig(int fd, char *script, long rflag)
{
	int line;		/* line counter */
	struct stat statbuf;	/* place for stat */
	FILE *fp;		/* file pointer for config script */
	char buf[BUFSIZ + 1];	/* scratch buffer */
	char *bp;		/* scratch pointer */
	char *p;		/* scratch pointer */

	/* if the script does not exist, then there is nothing to do */
	if (stat(script, &statbuf) < 0)
		return (0);

	fp = fopen(script, "rF");
	if (fp == NULL)
		return (-1);

	line = 0;
	while (fgets(buf, BUFSIZ, fp)) {
		line++;
		p = strchr(buf, '\n');
		/* if no \n, then line is too long */
		if (p == NULL) {
			(void) fclose(fp);
			return (line);
		}
		*p = '\0';

		/* remove comments */
		p = strchr(buf, COMMENT);
		if (p)
			*p = '\0';

		/* remove leading whitespace */
		bp = eatwhite(buf);
		/* see if anything is left */
		if (*bp == '\0')
			continue;

		/* remove trailing whitespace */
		p = &buf[strlen(buf) - 1];
		while (*p && isspace(*p))
			*p-- = '\0';

		/* get the command */
		p = bp;
		while (*p && !isspace(*p))
			p++;
		if (*p)
			*p++ = '\0';
		/* skip any whitespace here too (between command and args) */
		p = eatwhite(p);

		if (strcmp(bp, "assign") == 0) {
			if ((rflag & NOASSIGN) || doassign(p)) {
				(void) fclose(fp);
				return (line);
			}
		} else if (strcmp(bp, "push") == 0) {
			if (dopush(fd, p)) {
				(void) fclose(fp);
				return (line);
			}
		} else if (strcmp(bp, "pop") == 0) {
			if (dopop(fd, p)) {
				(void) fclose(fp);
				return (line);
			}
		} else if (strcmp(bp, "run") == 0) {
			if ((rflag & NORUN) || dorun(p, NOWAIT)) {
				(void) fclose(fp);
				return (line);
			}
		} else if (strcmp(bp, "runwait") == 0) {
			if ((rflag & NORUN) || dorun(p, WAIT)) {
				(void) fclose(fp);
				return (line);
			}
		} else {
			/* unknown command */
			(void) fclose(fp);
			return (line);
		}
	}
	if (!feof(fp)) {
		(void) fclose(fp);
		return (-1);
	}
	(void) fclose(fp);
	return (0);
}


/*
 * doassign - handle an `assign' command
 *
 *	args:	p - assignment string
 */


static int
doassign(char *p)
{
	char *var;		/* environment variable to be assigned */
	char val[BUFSIZ];	/* and the value to be assigned to it */
	char scratch[BUFSIZ];	/* scratch buffer */
	char delim;		/* delimiter char seen (for quoted strings ) */
	char *tp;		/* scratch pointer */

	if (*p == '\0')
		return (-1);
	var = p;
	/* skip first token, but stop if we see a '=' */
	while (*p && !isspace(*p) && (*p != '='))
		p++;

	/* if we found end of string, it's an error */
	if (*p == '\0')
		return (-1);

	/* if we found a space, look for the '=', otherwise it's an error */
	if (isspace(*p)) {
		*p++ = '\0';
		while (*p && isspace(*p))
			p++;
		if (*p == '\0')
			return (-1);
		if (*p == '=')
			p++;
		else
			return (-1);
	} else {
		/* skip over '=' */
		*p = '\0';
		p++;
	}

	/* skip over any whitespace */
	p = eatwhite(p);
	if (*p == '\'' || *p == '"') {
		/* handle quoted values */
		delim = *p++;
		tp = val;
		for (;;) {
			if (*p == '\0') {
				return (-1);
			} else if (*p == delim) {
				if (*(p - 1) != '\\')
					break;
				else
					*(tp - 1) = *p++;
			} else
				*tp++ = *p++;
		}
		*tp = '\0';
		/*
		 * these assignments make the comment below true
		 * (values of tp and p
		 */
		tp = ++p;
		p = val;
	} else {
		tp = p;
		/* look for end of token */
		while (*tp && !isspace(*tp))
			tp++;
	}

/*
 * at this point, p points to the value, and tp points to the
 * end of the token.  check to make sure there is no garbage on
 * the end of the line
 */

	if (*tp)
		return (-1);
	(void) snprintf(scratch, sizeof (scratch), "%s=%s", var, p);
	/* note: need to malloc fresh space so putenv works */
	tp = malloc(strlen(scratch) + 1);
	if (tp == NULL)
		return (-1);
	(void) strcpy(tp, scratch);
	if (putenv(tp))
		return (-1);
	return (0);
}


/*
 * dopush - handle a `push' command
 *
 *	args:	fd - file descriptor to push on
 *		p - list of modules to push
 */


static int
dopush(int fd, char *p)
{
	char *tp;	/* scratch pointer */
	int i;		/* scratch variable */
	int npush;	/* count # of modules pushed */

	if (*p == '\0')
		return (-1);
	npush = 0;
	for (;;) {
		if (*p == '\0')		/* found end of line */
			return (0);
		p = eatwhite(p);
		if (*p == '\0')
			return (-1);
		tp = p;
		while (*tp && !isspace(*tp) && (*tp != ','))
			tp++;
		if (*tp)
			*tp++ = '\0';
		if (ioctl(fd, I_PUSH, p) < 0) {

/*
 * try to pop all that we've done, if pop fails it doesn't matter because
 * nothing can be done anyhow
 */

			for (i = 0; i < npush; ++i)
				(void) ioctl(fd, I_POP, 0);
			return (-1);
		}
		/* count the number of modules we've pushed */
		npush++;
		p = tp;
	}
}


/*
 * dopop - handle a `pop' command
 *
 *	args:	fd - file descriptor to pop from
 *		p - name of module to pop to or ALL (null means pop top only)
 */


static int
dopop(int fd, char *p)
{
	char *modp;		/* module name from argument to pop */
	char buf[FMNAMESZ + 1];	/* scratch buffer */

	if (*p == '\0') {
		/* just a pop with no args */
		if (ioctl(fd, I_POP, 0) < 0)
			return (-1);
		return (0);
	}

	/* skip any whitespace in between */
	p = eatwhite(p);
	modp = p;
	/* find end of module name */
	while (*p && !isspace(*p))
		p++;

	if (*p)		/* if not end of line, extra junk on line */
		return (-1);
	if (strcmp(modp, "ALL") == 0) {
		/* it's the magic name, pop them all */
		while (ioctl(fd, I_POP, 0) == 0)
			;
		/* After all popped, we'll get an EINVAL, which is expected */
		if (errno != EINVAL)
			return (-1);
		return (0);
	}
	/* check to see if the named module is on the stream */
	if (ioctl(fd, I_FIND, modp) != 1)
		return (-1);

	/* pop them until the right one is on top */
	for (;;) {
		if (ioctl(fd, I_LOOK, buf) < 0)
			return (-1);
		if (strcmp(modp, buf) == 0)
			/* we're done */
			return (0);
		if (ioctl(fd, I_POP, 0) < 0)
			return (-1);
	}
	/* NOTREACHED */
}


/*
 * dorun - handle a `run' command
 *
 *	args:	p - command line to run
 *		waitflag - flag indicating whether a wait should be done
 */


static int
dorun(char *p, int waitflg)
{
	char *tp;		/* scratch pointer */
	char *ep;		/* scratch pointer (end of token) */
	char savech;		/* hold area */
	int status;		/* return status from wait */
	pid_t pid;		/* pid of child proc */
	pid_t rpid;		/* returned pid from wait */
	void (*func)();		/* return from signal */

	if (*p == '\0')
		return (-1);

	/*
	 * get first token
	 */

	for (tp = p; *tp && !isspace(*tp); ++tp)
		;
	savech = '\0';
	if (*tp) {
		savech = *tp;
		*tp = '\0';
	}

	/*
	 * look for built-in's
	 */

	if (strcmp(p, "cd") == 0) {
		*tp = savech;
		tp = eatwhite(tp);
		if (*tp == '\0')
			/* if nothing there, try to cd to $HOME */
			tp = getenv("HOME");
		if (chdir(tp) < 0)
			return (-1);
	} else if (strcmp(p, "ulimit") == 0) {
		*tp = savech;
		tp = eatwhite(tp);
		/* must have an argument */
		if (*tp == '\0')
			return (-1);
		/* make sure nothing appears on line after arg */
		for (ep = tp; *ep && !isspace(*ep); ++ep)
			;
		ep = eatwhite(ep);
		if (*ep)
			return (-1);
		if (!isdigit(*tp))
			return (-1);

		if (ulimit(2, atoi(tp)) < 0)
			return (-1);
	} else if (strcmp(p, "umask") == 0) {
		*tp = savech;
		tp = eatwhite(tp);
		/* must have an argument */
		if (*tp == '\0')
			return (-1);
		/* make sure nothing appears on line after arg */
		for (ep = tp; *ep && !isspace(*ep); ++ep)
			;
		ep = eatwhite(ep);
		if (*ep)
			return (-1);
		if (!isdigit(*tp))
			return (-1);
		(void) umask(strtol(tp, NULL, 8));
	} else {
		/* not a built-in */
		*tp = savech;
		func = signal(SIGCLD, SIG_DFL);
		if ((pid = fork()) < 0) {
			(void) signal(SIGCLD, func);
			return (-1);
		}
		if (pid) {
			if (waitflg == WAIT) {
				status = 0;
				rpid = -1;
				while (rpid != pid)
					rpid = wait(&status);
				if (status) {
					/* child failed */
					(void) signal(SIGCLD, func);
					return (-1);
				}
			}
			(void) signal(SIGCLD, func);
		} else {
			/* set IFS for security */
			(void) putenv("IFS=\" \"");
			/*
			 * need to close all files to prevent unauthorized
			 * access in the children.  Setup stdin, stdout,
			 * and stderr to /dev/null.
			 */
			closefrom(0);
			/* stdin */
			if (open("/dev/null", O_RDWR) != 0)
				return (-1);
			/* stdout */
			if (dup(0) != 1)
				return (-1);
			/* stderr */
			if (dup(0) != 2)
				return (-1);
			(void) execl("/usr/bin/sh", "sh", "-c", p, NULL);
			/*
			 * if we get here, there is a problem - remember that
			 * this is the child
			 */
			exit(1);
		}
	}
	return (0);
}


/*
 * eatwhite - swallow any leading whitespace, return pointer to first
 *	      non-white space character or to terminating null character
 *	      if nothing else is there
 *
 *	args:	p - string to parse
 */

static char *
eatwhite(char *p)
{
	while (*p && isspace(*p))
		p++;
	return (p);
}