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