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