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