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