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