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