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
doconfig(int fd,char * script,long rflag)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
doassign(char * p)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
dopush(int fd,char * p)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
dopop(int fd,char * p)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
dorun(char * p,int waitflg)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 *
eatwhite(char * p)502 eatwhite(char *p)
503 {
504 while (*p && isspace(*p))
505 p++;
506 return (p);
507 }
508