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