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