/* * Copyright 1989 by Rayan S. Zachariassen, all rights reserved. * This will be free software, but only when it is finished. */ /* * Trap script and signal management, and the builtin "trap" command. */ #include "hostenv.h" #include #include #include #include "zmsignal.h" #include "flags.h" #include "zmalloc.h" /* #include "listutils.h" */ #include "sh.h" #include "io.h" /* redefines stdio routines */ #include "shconfig.h" #include "libsh.h" extern const char *VersionNumb; #ifndef SIGCHLD #define SIGCHLD SIGCLD #endif /* SIGCHLD */ /* * The script to execute for a particular trap is stored as a string in * malloc()'ed storage, with a pointer to it in the traps[] array. */ const char *traps[NSIG]; /* * The effect of a signal is to increment a count of seen but unprocessed * (as in the trap script hasn't been run) signals in the spring[] array, * one counter per signal. They had better start out as 0. */ STATIC int spring[NSIG]; /* pending trap counts */ /* * As a cheap way of testing if there are pending unprocessed signals, the * sprung variable is used as a flag to that effect. It is a cheap test * elsewhere in the shell (in interpreter loop and input function). */ int sprung; /* there are pending traps */ /* * In order to interrupt builtin function execution (as opposed to builtin * functions), set a flag whenever we see an interrupt that doesn't have * a trap handler. This flag should be tested in the interpreter loop, and * anywhere else the shell might be spending a lot of time (e.g. the filename * expansion routines). */ int interrupted; /* XX: we saw an interrupt */ /* * To maintain sh semantics, we need to know what the original signal handler * values are. These are retrieved once at startup time (from main) and * stored in the orig_handler[] array. */ RETSIGTYPE (*orig_handler[NSIG]) __((int)); /* * Indeed, that is what the trapsnap() function does. */ void trapsnap() { int i; for (i = 1; i < NSIG; ++i) SIGNAL_HANDLESAVE(i, SIG_DFL, orig_handler[i]); /* is there a vulnerability here due to SIG_DFL instead of SIG_IGN ? */ for (i = 1; i < NSIG; ++i) if (orig_handler[i] != SIG_DFL && i != SIGCHLD) SIGNAL_HANDLE(i, orig_handler[i]); } /* * This is the generic signal handler that is set whenever a trap is laid. */ void trap_handler(sig) int sig; { if (sig > 0 && sig < NSIG) { SIGNAL_HANDLE(sig, trap_handler); spring[sig] += 1; sprung = 1; } if (sig == SIGINT && traps[sig] == NULL) interrupted = 1; } /* * Evaluate the contents of the buffer and invoke the interpreter. * This function really should not be here! */ #define CFSUFFIX ".cf" #define FCSUFFIX ".fc" #define FCSUBDIR "fc" STATIC char * makefc __((const char *, char *)); STATIC char * makefc(path, buf) const char *path; char *buf; { register char *cp; struct stat stbuf; u_int plen = strlen(path); if (plen <= sizeof CFSUFFIX) return NULL; if (strcmp(path+plen-(sizeof FCSUFFIX - 1), FCSUFFIX) == 0) { strcpy(buf, path); return buf; } if (strcmp(path+plen-(sizeof CFSUFFIX - 1), CFSUFFIX) != 0) return NULL; strcpy(buf, path); if ((cp = strrchr(buf, '/')) != NULL) ++cp; else cp = buf; strcpy(cp, FCSUBDIR); if (stat(buf, &stbuf) == 0 && (stbuf.st_mode & S_IFDIR)) { sprintf(buf + strlen(buf), "/%s", path+(cp-buf)); strcpy(buf + strlen(buf) - (sizeof CFSUFFIX - 1), FCSUFFIX); } else { strcpy(buf, path); strcpy(buf+plen-(sizeof CFSUFFIX - 1), FCSUFFIX); } return buf; } int eval(script, scriptname, savefile, srcstbufp) const char *script, *scriptname, *savefile; const struct stat *srcstbufp; { int status; void *table, *eotable; FILE *fp; char *fcfile; char *buf = NULL; #ifdef USE_ALLOCA if (savefile != NULL) buf = alloca(strlen(savefile)+9); #else if (savefile != NULL) buf = emalloc(strlen(savefile)+9); #endif commandline = s_pushstack(commandline, script); table = SslWalker(scriptname, stdout, &eotable); status = 0; if (table != NULL) { if (isset('n')) { free((char *)table); return 0; } fcfile = NULL; if (isset('O')) { extern void *optimize __((int, void *, void **)); table = optimize(0, table, &eotable); if (isset('V')) table = optimize(1, table, &eotable); if (savefile != NULL && (fcfile = makefc(savefile,buf)) != NULL && (fp = fopen(fcfile, "w")) != NULL) { /* magic1, magic2, st_dev, st_ino, st_size, st_mtime, st_ctime */ fprintf(fp,"#!zmsh -l%s,%d,%ld,%ld,%ld,%ld,%ld,%ld\n", VersionNumb, magic_number, (long)bin_magic, (long)srcstbufp->st_dev, (long)srcstbufp->st_ino, (long)srcstbufp->st_size, (long)srcstbufp->st_mtime, (long)srcstbufp->st_ctime); std_fwrite(table, 1, (char*)eotable - (char*)table, fp); if (fclose(fp) == EOF) { fprintf(stderr, "%s: write to %s failed\n", progname, fcfile); unlink(fcfile); } } } interpret(table, eotable, (u_char *)NULL, globalcaller == NULL ? &avcmd : globalcaller, &status, (struct codedesc *)NULL); } #ifndef USE_ALLOCA free(buf); #endif return status; } int loadeval(fcfd, path, srcstbufp) int fcfd; const char *path; struct stat *srcstbufp; { int status; char *fcfile; #ifdef USE_ALLOCA char *buf = alloca(strlen(path)+9); #else char *buf = emalloc(strlen(path)+9); #endif fcfile = makefc(path,buf); if (fcfile == NULL) { #ifndef USE_ALLOCA free(buf); #endif return -1; } status = leaux(fcfd, fcfile, srcstbufp); #ifndef USE_ALLOCA free(buf); #endif return status; } int leaux(fcfd, path, srcstbufp) int fcfd; const char *path; struct stat *srcstbufp; { FILE *fp; int status, len; void *table; struct stat objstbuf; char buf[200]; char sbuf[200]; /* magic1, magic2, st_dev, st_ino, st_size, st_mtime, st_ctime */ sprintf(sbuf,"#!zmsh -l%s,%d,%ld,%ld,%ld,%ld,%d,%d\n", VersionNumb, magic_number, (long)bin_magic, (long)srcstbufp->st_dev, (long)srcstbufp->st_ino, (long)srcstbufp->st_size, (int)srcstbufp->st_mtime, (int)srcstbufp->st_ctime); fp = fopen(path, "r"); if (fp == NULL) return -1; if (std_fgets(buf, sizeof buf, fp) == NULL) { fclose(fp); fprintf(stderr, "%s: cannot get first line of %s\n", progname, path); return -1; } if (fstat(FILENO(fp), &objstbuf) < 0) { fclose(fp); fprintf(stderr, "%s: fstat failed on %s\n", progname, path); return -1; } if (strcmp(buf,sbuf) != 0) { fclose(fp); unlink(path); return -1; } if (srcstbufp != NULL && srcstbufp->st_mtime > objstbuf.st_mtime) { fclose(fp); unlink(path); return -1; } len = (int)(objstbuf.st_size - ftell(fp)); table = (void *)emalloc(len); if (std_fread(table, 1, len, fp) != len) { fclose(fp); fprintf(stderr, "%s: read of %d failed on %s\n", progname, len, path); return -1; } fclose(fp); if (fcfd >= 0) close(fcfd); status = 0; interpret(table, (char*)table + len, NULL, globalcaller == NULL ? &avcmd : globalcaller, &status, (struct codedesc *)NULL); return status; } /* * If unprocessed signals are pending (and the sprung flag set), we call * this function to do the processing. It will deal with pending signals * in numerical as opposed to chronological order. */ void trapped() { int i; static int intrap = 0; if (!sprung) return; /* * We must reset the sprung flag before calling aval(), or we will * almost certainly get a recursive invocation of this routine from * the interpreter. */ sprung = 0; /* * What about this scenario: interpreter calls trapped. trapped calls * eval which calls interpreter. a signal is delivered and sprung * gets set. the interpreter calls trapped again. some traps will * then maybe get run twice. Ergo we need some semaphore here. */ if (intrap) { /* more signals have arrived, be sure not to miss them */ ++intrap; return; } for (++intrap; intrap > 0; --intrap) { for (i = 1; i < NSIG; ++i) while (spring[i] > 0) { if (traps[i] != NULL) eval(traps[i], "trap", NULL, NULL); --spring[i]; } } } /* * This is the exit routine used when one wants a "trap 0" to be honoured. */ void trapexit(n) int n; { if (traps[0] != NULL) { const char *cmd = traps[0]; traps[0] = NULL; eval(cmd, "exit trap", NULL, NULL); } /* Lets clean these up, malloc tracers complain less.. */ /* s_free_tree(envarlist); */ envarlist = NULL; #ifdef MALLOC_TRACE mal_dumpleaktrace(stderr); /* mal_heapdump(&_iob[2]); */ #endif /* MALLOC_TRACE */ exit(n); /* NOTREACHED */ } /* * The builtin "trap" function is implemented here. */ int sh_trap(argc, argv) int argc; const char *argv[]; { int i; const char *av0, *script; if (argc == 1) { /* just print the known traps */ for (i = 0; i < NSIG; ++i) { if (traps[i] != NULL) printf("%d: %s\n", i, traps[i]); } return 0; } av0 = argv[0]; --argc, ++argv; if (**argv == '\0') { /* ignore the specified signals */ script = *argv; --argc, ++argv; } else if (isascii((unsigned)(**argv)) && isdigit((unsigned)(**argv))){ /* reset the signal handlers to original value */ script = NULL; } else { /* stash the script away for later execution by a trap */ script = *argv; --argc, ++argv; /* don't bother guarding argc > 0, sh doesn't */ } while (argc-- > 0) { if (!isascii(**argv) || !isdigit(**argv)) { fprintf(stderr, "%s: bad number: '%s'\n", av0, *argv); ++argv; continue; } i = atoi(*argv++); if (i < 0 || i >= NSIG) { fprintf(stderr, "%s: %s: %s\n", av0, BAD_TRAP, *(argv-1)); continue; } if (traps[i] != NULL) free((void*)traps[i]); if (script != NULL && *script != '\0') { char *tp = (char *)emalloc(strlen(script)+1); strcpy(tp, script); traps[i] = tp; /* enable that signal */ if (i > 0 && orig_handler[i] != SIG_IGN) SIGNAL_HANDLE(i, trap_handler); } else if (script != NULL) { traps[i] = NULL; /* disable that signal */ if (i > 0) SIGNAL_IGNORE(i); } else { traps[i] = NULL; if (i > 0) SIGNAL_HANDLE(i, orig_handler[i]); } } return 0; }