/*
* 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 <stdio.h>
#include <ctype.h>
#include <sys/stat.h>
#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;
}
syntax highlighted by Code2HTML, v. 0.9.1