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