/* * 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 */ #include "mt.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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); }