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