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 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 30 /* All Rights Reserved */ 31 32 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 <rpc/trace.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(); 68 static int doassign(); 69 static int dopush(); 70 static int dopop(); 71 static int dorun(); 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 trace3(TR_doconfig, 0, fd, rflag); 97 if (_STAT(script, &statbuf) < 0) { 98 trace1(TR_doconfig, 1); 99 return (0); 100 } 101 102 fp = fopen(script, "r"); 103 if (fp == NULL) { 104 trace1(TR_doconfig, 1); 105 return (-1); 106 } 107 108 line = 0; 109 while (fgets(buf, BUFSIZ, fp)) { 110 line++; 111 p = strchr(buf, '\n'); 112 /* if no \n, then line is too long */ 113 if (p == NULL) { 114 (void) fclose(fp); 115 trace1(TR_doconfig, 1); 116 return (line); 117 } 118 *p = '\0'; 119 120 /* remove comments */ 121 p = strchr(buf, COMMENT); 122 if (p) 123 *p = '\0'; 124 125 /* remove leading whitespace */ 126 bp = eatwhite(buf); 127 /* see if anything is left */ 128 if (*bp == '\0') 129 continue; 130 131 /* remove trailing whitespace */ 132 p = &buf[strlen(buf) - 1]; 133 while (*p && isspace(*p)) 134 *p-- = '\0'; 135 136 /* get the command */ 137 p = bp; 138 while (*p && !isspace(*p)) 139 p++; 140 if (*p) 141 *p++ = '\0'; 142 /* skip any whitespace here too (between command and args) */ 143 p = eatwhite(p); 144 145 if (!strcmp(bp, "assign")) { 146 if ((rflag & NOASSIGN) || doassign(p)) { 147 (void) fclose(fp); 148 trace1(TR_doconfig, 1); 149 return (line); 150 } 151 } else if (!strcmp(bp, "push")) { 152 if (dopush(fd, p)) { 153 (void) fclose(fp); 154 trace1(TR_doconfig, 1); 155 return (line); 156 } 157 } else if (!strcmp(bp, "pop")) { 158 if (dopop(fd, p)) { 159 (void) fclose(fp); 160 trace1(TR_doconfig, 1); 161 return (line); 162 } 163 } else if (!strcmp(bp, "run")) { 164 if ((rflag & NORUN) || dorun(p, NOWAIT)) { 165 (void) fclose(fp); 166 trace1(TR_doconfig, 1); 167 return (line); 168 } 169 } else if (!strcmp(bp, "runwait")) { 170 if ((rflag & NORUN) || dorun(p, WAIT)) { 171 (void) fclose(fp); 172 trace1(TR_doconfig, 1); 173 return (line); 174 } 175 } else { 176 /* unknown command */ 177 (void) fclose(fp); 178 trace1(TR_doconfig, 1); 179 return (line); 180 } 181 } 182 if (!feof(fp)) { 183 (void) fclose(fp); 184 trace1(TR_doconfig, 1); 185 return (-1); 186 } else { 187 (void) fclose(fp); 188 trace1(TR_doconfig, 1); 189 return (0); 190 } 191 } 192 193 194 /* 195 * doassign - handle an `assign' command 196 * 197 * args: p - assignment string 198 */ 199 200 201 static int 202 doassign(char *p) 203 { 204 char *var; /* environment variable to be assigned */ 205 char val[BUFSIZ]; /* and the value to be assigned to it */ 206 char scratch[BUFSIZ]; /* scratch buffer */ 207 char delim; /* delimiter char seen (for quoted strings ) */ 208 char *tp; /* scratch pointer */ 209 210 trace1(TR_doassign, 0); 211 if (*p == '\0') { 212 trace1(TR_doassign, 1); 213 return (-1); 214 } 215 var = p; 216 /* skip first token, but stop if we see a '=' */ 217 while (*p && !isspace(*p) && (*p != '=')) 218 p++; 219 220 /* if we found end of string, it's an error */ 221 if (*p == '\0') { 222 trace1(TR_doassign, 1); 223 return (-1); 224 } 225 226 /* if we found a space, look for the '=', otherwise it's an error */ 227 if (isspace(*p)) { 228 *p++ = '\0'; 229 while (*p && isspace(*p)) 230 p++; 231 if (*p == '\0') { 232 trace1(TR_doassign, 1); 233 return (-1); 234 } 235 if (*p == '=') 236 p++; 237 else { 238 trace1(TR_doassign, 1); 239 return (-1); 240 } 241 } else { 242 /* skip over '=' */ 243 *p = '\0'; 244 p++; 245 } 246 247 /* skip over any whitespace */ 248 p = eatwhite(p); 249 if (*p == '\'' || *p == '"') { 250 /* handle quoted values */ 251 delim = *p++; 252 tp = val; 253 for (;;) { 254 if (*p == '\0') { 255 trace1(TR_doassign, 1); 256 return (-1); 257 } else if (*p == delim) { 258 if (*(p - 1) != '\\') 259 break; 260 else 261 *(tp - 1) = *p++; 262 } else 263 *tp++ = *p++; 264 } 265 *tp = '\0'; 266 /* these assignments make the comment below true (values of tp and p */ 267 tp = ++p; 268 p = val; 269 } else { 270 tp = p; 271 /* look for end of token */ 272 while (*tp && !isspace(*tp)) 273 tp++; 274 } 275 276 /* 277 * at this point, p points to the value, and tp points to the 278 * end of the token. check to make sure there is no garbage on 279 * the end of the line 280 */ 281 282 if (*tp) { 283 trace1(TR_doassign, 1); 284 return (-1); 285 } 286 sprintf(scratch, "%s=%s", var, p); 287 /* note: need to malloc fresh space so putenv works */ 288 tp = malloc(strlen(scratch) + 1); 289 if (tp == NULL) { 290 trace1(TR_doassign, 1); 291 return (-1); 292 } 293 strcpy(tp, scratch); 294 if (putenv(tp)) { 295 trace1(TR_doassign, 1); 296 return (-1); 297 } else { 298 trace1(TR_doassign, 1); 299 return (0); 300 } 301 } 302 303 304 /* 305 * dopush - handle a `push' command 306 * 307 * args: fd - file descriptor to push on 308 * p - list of modules to push 309 */ 310 311 312 static int 313 dopush(int fd, char *p) 314 { 315 char *tp; /* scratch pointer */ 316 int i; /* scratch variable */ 317 int npush; /* count # of modules pushed */ 318 319 trace2(TR_dopush, 0, fd); 320 if (*p == '\0') { 321 trace1(TR_dopush, 1); 322 return (-1); 323 } 324 npush = 0; 325 for (;;) { 326 if (*p == '\0') { /* found end of line */ 327 trace1(TR_dopush, 1); 328 return (0); 329 } 330 p = eatwhite(p); 331 if (*p == '\0') { 332 trace1(TR_dopush, 1); 333 return (-1); 334 } 335 tp = p; 336 while (*tp && !isspace(*tp) && (*tp != ',')) 337 tp++; 338 if (*tp) 339 *tp++ = '\0'; 340 if (ioctl(fd, I_PUSH, p) < 0) { 341 342 /* 343 * try to pop all that we've done, if pop fails it doesn't matter because 344 * nothing can be done anyhow 345 */ 346 347 for (i = 0; i < npush; ++i) 348 ioctl(fd, I_POP, 0); 349 trace1(TR_dopush, 1); 350 return (-1); 351 } else { 352 /* count the number of modules we've pushed */ 353 npush++; 354 p = tp; 355 } 356 } 357 } 358 359 360 /* 361 * dopop - handle a `pop' command 362 * 363 * args: fd - file descriptor to pop from 364 * p - name of module to pop to or ALL (null means pop top only) 365 */ 366 367 368 static int 369 dopop(int fd, char *p) 370 { 371 char *modp; /* module name from argument to pop */ 372 char buf[FMNAMESZ + 1]; /* scratch buffer */ 373 374 trace2(TR_dopop, 0, fd); 375 if (*p == '\0') { 376 /* just a pop with no args */ 377 if (ioctl(fd, I_POP, 0) < 0) { 378 trace1(TR_dopop, 1); 379 return (-1); 380 } else { 381 trace1(TR_dopop, 1); 382 return (0); 383 } 384 } 385 386 /* skip any whitespace in between */ 387 p = eatwhite(p); 388 modp = p; 389 /* find end of module name */ 390 while (*p && !isspace(*p)) 391 p++; 392 393 if (*p) { /* if not end of line, extra junk on line */ 394 trace1(TR_dopop, 1); 395 return (-1); 396 } 397 if (!strcmp(modp, "ALL")) { 398 /* it's the magic name, pop them all */ 399 while (ioctl(fd, I_POP, 0) == 0) 400 ; 401 /* After all popped, we'll get an EINVAL, which is expected */ 402 if (errno != EINVAL) { 403 trace1(TR_dopop, 1); 404 return (-1); 405 } else { 406 trace1(TR_dopop, 1); 407 return (0); 408 } 409 } else { 410 /* check to see if the named module is on the stream */ 411 if (ioctl(fd, I_FIND, modp) != 1) { 412 trace1(TR_dopop, 1); 413 return (-1); 414 } 415 416 /* pop them until the right one is on top */ 417 for (;;) { 418 if (ioctl(fd, I_LOOK, buf) < 0) { 419 trace1(TR_dopop, 1); 420 return (-1); 421 } 422 if (!strcmp(modp, buf)) { 423 trace1(TR_dopop, 1); 424 /* we're done */ 425 return (0); 426 } 427 if (ioctl(fd, I_POP, 0) < 0) { 428 trace1(TR_dopop, 1); 429 return (-1); 430 } 431 } 432 } 433 } 434 435 436 /* 437 * dorun - handle a `run' command 438 * 439 * args: p - command line to run 440 * waitflag - flag indicating whether a wait should be done 441 */ 442 443 444 static int 445 dorun(char *p, int waitflg) 446 { 447 char *tp; /* scratch pointer */ 448 char *ep; /* scratch pointer (end of token) */ 449 int nfiles; /* # of possibly open files */ 450 int i; /* scratch variable */ 451 char savech; /* hold area */ 452 int status; /* return status from wait */ 453 pid_t pid; /* pid of child proc */ 454 pid_t rpid; /* returned pid from wait */ 455 void (*func)(); /* return from signal */ 456 457 trace2(TR_dorun, 0, waitflg); 458 if (*p == '\0') { 459 trace1(TR_dorun, 1); 460 return (-1); 461 } 462 463 /* 464 * get first token 465 */ 466 467 for (tp = p; *tp && !isspace(*tp); ++tp) 468 ; 469 savech = '\0'; 470 if (*tp) { 471 savech = *tp; 472 *tp = '\0'; 473 } 474 475 /* 476 * look for built-in's 477 */ 478 479 if (!strcmp(p, "cd")) { 480 *tp = savech; 481 tp = eatwhite(tp); 482 if (*tp == '\0') 483 /* if nothing there, try to cd to $HOME */ 484 tp = getenv("HOME"); 485 if (chdir(tp) < 0) { 486 trace1(TR_dorun, 1); 487 return (-1); 488 } 489 } else if (!strcmp(p, "ulimit")) { 490 *tp = savech; 491 tp = eatwhite(tp); 492 /* must have an argument */ 493 if (*tp == '\0') { 494 trace1(TR_dorun, 1); 495 return (-1); 496 } 497 /* make sure nothing appears on line after arg */ 498 for (ep = tp; *ep && !isspace(*ep); ++ep) 499 ; 500 ep = eatwhite(ep); 501 if (*ep) { 502 trace1(TR_dorun, 1); 503 return (-1); 504 } 505 if (!isdigit(*tp)) { 506 trace1(TR_dorun, 1); 507 return (-1); 508 } 509 510 if (ulimit(2, atoi(tp)) < 0) { 511 trace1(TR_dorun, 1); 512 return (-1); 513 } 514 } else if (!strcmp(p, "umask")) { 515 *tp = savech; 516 tp = eatwhite(tp); 517 /* must have an argument */ 518 if (*tp == '\0') { 519 trace1(TR_dorun, 1); 520 return (-1); 521 } 522 /* make sure nothing appears on line after arg */ 523 for (ep = tp; *ep && !isspace(*ep); ++ep) 524 ; 525 ep = eatwhite(ep); 526 if (*ep) { 527 trace1(TR_dorun, 1); 528 return (-1); 529 } 530 if (!isdigit(*tp)) { 531 trace1(TR_dorun, 1); 532 return (-1); 533 } 534 (void) umask(strtol(tp, NULL, 8)); 535 } else { 536 /* not a built-in */ 537 *tp = savech; 538 func = signal(SIGCLD, SIG_DFL); 539 if ((pid = fork()) < 0) { 540 signal(SIGCLD, func); 541 trace1(TR_dorun, 1); 542 return (-1); 543 } else if (pid) { 544 if (waitflg == WAIT) { 545 status = 0; 546 rpid = -1; 547 while (rpid != pid) 548 rpid = wait(&status); 549 if (status) { 550 /* child failed */ 551 signal(SIGCLD, func); 552 trace1(TR_dorun, 1); 553 return (-1); 554 } 555 } 556 signal(SIGCLD, func); 557 } else { 558 /* set IFS for security */ 559 (void) putenv("IFS=\" \""); 560 /* 561 * need to close all files to prevent unauthorized 562 * access in the children. Setup stdin, stdout, 563 * and stderr to /dev/null. 564 */ 565 nfiles = ulimit(4, 0); 566 closefrom(0); 567 /* stdin */ 568 if (open("/dev/null", O_RDWR) != 0) { 569 trace1(TR_dorun, 1); 570 return (-1); 571 } 572 /* stdout */ 573 if (dup(0) != 1) { 574 trace1(TR_dorun, 1); 575 return (-1); 576 } 577 /* stderr */ 578 if (dup(0) != 2) { 579 trace1(TR_dorun, 1); 580 return (-1); 581 } 582 execle("/usr/bin/sh", "sh", "-c", p, 0, _environ); 583 /* if we get here, there is a problem - remember that 584 this is the child */ 585 trace1(TR_dorun, 1); 586 exit(1); 587 } 588 } 589 trace1(TR_dorun, 1); 590 return (0); 591 } 592 593 594 /* 595 * eatwhite - swallow any leading whitespace, return pointer to first 596 * non-white space character or to terminating null character 597 * if nothing else is there 598 * 599 * args: p - string to parse 600 */ 601 602 static char * 603 eatwhite(char *p) 604 { 605 trace1(TR_eatwhite, 0); 606 while (*p && isspace(*p)) 607 p++; 608 trace1(TR_eatwhite, 1); 609 return (p); 610 } 611