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