bsa@ncoast.UUCP (Brandon Allbery) (03/01/85)
I wrote this as a simple way of stopping programs temporarily to trap signals or get a shell quickly, get a core image, or push it into the background. If anyone ever figures out how to get ``suspend'' out of an unmodified V7 kernel, please let me know. Following are a man page and a C program for the "setjctl()" function. Read the manual page for details. # This is a shell archive. # Remove everything above and including the cut line. # Then run the rest of the file through sh. -----cut here-----cut here-----cut here-----cut here----- #!/bin/sh # shar: Shell Archiver # Run the following text with /bin/sh to create: # src/jobs.c # jobs.3 # This archive created: Fri Mar 1 10:34:12 1985 cat << \SHAR_EOF > src/jobs.c /* * jobs.c -- simple job control routines * * These subroutines are Copyright (C) 1985 by Brandon Allbery. * Permission is hereby granted to copy and distribute this code, provided * that this notice is not removed. */ #include <stdio.h> #include <signal.h> #ifndef SYS3 # include <sgtty.h> struct sgttyb __origtty; #define Index(s, c) index(s, c) char *index(); #else # include <termio.h> struct termio __origtty; #define Index(s, c) strchr(s, c) char *strchr(); #endif int (*_oldsig)(), __sig, abort(); _jobctrl() { char line[256]; FILE *tfd; short flg, retval = 0; #ifndef SYS3 struct sgttyb sgbuf, oldbuf; #else struct termio tbuf, oldtbuf; #endif signal(__sig, _jobctrl); if (isatty(0)) tfd = stdin; else tfd = fopen("/dev/tty", "r"); #ifndef SYS3 ioctl(fileno(tfd), TIOCGETP, &sgbuf); oldbuf = sgbuf; sgbuf.sg_flags &= ~(ECHO|RAW|CBREAK); ioctl(fileno(tfd), TIOCSETN, &sgbuf); #else ioctl(fileno(tfd), TCGETA, &tbuf); oldtbuf = tbuf; tbuf.lflags &= ~ECHO; ioctl(fileno(tfd), TCSETAW, &tbuf); #endif fgets(line, 256, tfd); #ifndef SYS3 ioctl(fileno(tfd), TIOCSETN, &oldbuf); #else ioctl(fileno(tfd), TCSETAW, &oldtbuf); #endif if (tfd != stdin) fclose(tfd); line[strlen(line) - 1] = '\0'; if (strlen(line) >= 2) { if (strncmp(line, "ab", 2) == 0) { _resetty(); exit(100); } else if (strncmp(line, "ci", 2) == 0) { _resetty(); abort(); } else if (strncmp(line, "cn", 2) == 0) ; else if (strncmp(line, "bg", 2) == 0) { switch(fork()) { case -1: break; case 0: if (_oldsig == SIG_DFL) signal(__sig, SIG_IGN); else signal(__sig, _oldsig); if (strlen(line) == 2 && isatty(1)) { fflush(stdout); freopen("./background.out", "a", stdout); } else if (line[2] != ',') { char *cp; if ((cp = Index(line, ',')) != NULL) *cp = '\0'; freopen(&line[2], "a", stdout); if (cp != NULL) *cp = ','; } if (Index(line, ',') == NULL && isatty(0)) freopen("/dev/null", "r", stdin); else if (Index(line, ',') != NULL) freopen(Index(line, ','), "r", stdin); break; default: _resetty(); exit(101); } } else if (strncmp(line, "ig", 2) == 0) if (strlen(line) > 2) signal(atoi(&line[2]), SIG_IGN); else signal(__sig, SIG_IGN); else if (strncmp(line, "dc", 2) == 0) if (strlen(line) > 2) signal(atoi(&line[2]), abort); else signal(__sig, abort); else if (strncmp(line, "df", 2) == 0) if (strlen(line) > 2) signal(atoi(&line[2]), SIG_DFL); else signal(__sig, SIG_DFL); else if (strncmp(line, "kp", 2) == 0) if (strlen(line) > 2) kill(getpid(), atoi(&line[2])); else kill(getpid(), 9); else if (strncmp(line, "sh", 2) == 0) _doshell(); else { signal(__sig, _oldsig); kill(getpid(), __sig); } } } _resetty() { FILE *f; if (isatty(0)) f = stdin; else f = fopen("/dev/tty", "w"); #ifndef SYS3 ioctl(fileno(f), TIOCSETN, &__origtty); #else ioctl(fileno(f), TCSETAN, &__origtty); #endif if (f != stdin) fclose(f); } /* this is the only externally visible function */ setjctl(sig) int sig; { FILE *f; __sig = (sig? sig: SIGINT); if (isatty(stdin)) f = stdin; else f = fopen("/dev/tty", "w"); #ifndef SYS3 ioctl(fileno(f), TIOCGETP, &__origtty); #else ioctl(fileno(f), TCGETA, &__origtty); #endif if (f != stdin) fclose(f); _oldsig = signal((sig? sig: SIGINT), _jobctrl); } _doshell() { FILE *fp; #if defined(SIGCHLD) || defined(SIGCLD) int (*oldsig()); #endif #ifndef SYS3 struct sgttyb curtty; #else struct termio curtty; #endif switch(fork()) { case -1: break; case 0: freopen("/dev/tty", "r", stdin); freopen("/dev/tty", "w", stdout); execl(getenv(SHELL)? getenv(SHELL): "/bin/sh", "sh", (char *) 0); exit(-100); default: if (isatty(0)) fp = stdin; else fp = fopen("/dev/tty", "w"); #ifndef SYS3 ioctl(fileno(fp), TIOCGETP, &curtty); ioctl(fileno(fp), TIOCSETN, &__origtty); #else ioctl(fileno(fp), TCGETA, &curtty); ioctl(fileno(fp), TCSETAW, &__origtty); #endif #ifdef SIGCLD oldsig = signal(SIGCLD, SIG_IGN); #endif #ifdef SIGCHLD oldsig = signal(SIGCHLD, SIG_IGN); #endif wait((int *) 0); #ifdef SIGCLD signal(SIGCLD, oldsig); #endif #ifdef SIGCHLD signal(SIGCHLD, oldsig); #endif #ifndef SYS3 ioctl(fileno(fp), TIOCSETN, &curtty); #else ioctl(fileno(fp), TCSETAW, &curtty); #endif if (fp != stdin) fclose(fp); } } SHAR_EOF cat << \SHAR_EOF > jobs.3 .TH JOBS 3 .SH NAME jobs \- job control routines for non\-Berkeley systems .SH SYNTAX .RB cc prog.c ... \-l\c .I jobs .SH DESCRIPTION The .I jobs library is a set of routines to allow programs to be interrupted, signals to be trapped or ignored, and restarted, aborted on any signal, or a core image to be generated. .PP In a program using this library, a call to .I setjctl(signo) should be made immediately upon program entry, in order to set signal traps, save terminal values for a possible program abort, etc. The call can be made as .I setjctl(0) which uses the SIGINT signal, thus making unnecessary the inclusion of the <signal.h> header file. No other changes need be made to the program. .PP When the specified signal is received, the trap routine waits for input without prompting or echoing, so as not to disturb the screen display. The commands supported are: .RS .HP "ab \-" abort execution of the program, taking the default action for the signal used to invoke the job control routines .HP "ci \-" abort execution, generating a core image via abort(3) .HP "cn \-" continue program execution as if job control had not been invoked .HP "bg \-" continue execution in the background, see BACKGROUND EXECUTION below .HP "ig \-" ignore the specified signal (default is the job control signal) .HP "dc \-" dump core on receipt of signal (as ig above) .HP "df \-" take default action on receipt of signal, as above .HP "kp \-" send process specified signal \- default is signal 9 (SIGKILL) .HP "sh \-" fork a subshell ($SHELL or /bin/sh); on exit, return to the program .RE .SH ARGUMENTS When job control is invoked, a line of input is read from the terminal without prompting or echoing. The first two characters of the line are taken as a command; a null command defaults to `ab'. The rest of the line will be ignored by some commands or taken as arguments by others. The most common argument is a .I signo taken by the .I kp, ig, dc, and .I df commands to specify the signal to use; it must be a decimal number. .SH BACKGROUND EXECUTION The .I bg command, without any arguments, redirects stdio if it is coming from / going to a terminal, directing stdin to /dev/null and output to be appended to the file `background.out' in the current directory. With a single argument, output is sent to the given file (or left as it is, overriding the default, if the argument is `\-'). With two comma- separated arguments, input is redirected as per the second argument, with `\-' meaning to leave the input alone. Input can be specified without changing the output action. Note that extraneous spaces will probably end up in a filename somewhere. .PP After this file swapping is performed, the process forks, and the main path resets the tty to the values stored at the time of the call to .I setjctl. The background path returns to program execution. .SH USE WITH SCREEN HANDLERS The easiest way to use this is to trap SIGUSR1 or some other normally unused signal with .I setjctl, then trap the job control signal to a user-written subroutine to kill itself with the .I setjctl signal, then rebuild the screen (in case the `sh' option was used). If your signal handler gets its signals crossed when you try this, insert a call to the primitive .I _jobctrl() which will also return non-zero if the screen needs an update because the \&`sh' command was used. .SH FILES /usr/lib/libjobs.a /dev/null ./background.out .SH SEE ALSO signal(2), kill(2), fork(2), exec(2), getenv(3), freopen(3) .SH AUTHOR Brandon Allbery, North Coast Xenix (decvax!cwruecmp!ncoast!bsa) .SH BUGS \&`sh' does not check to see if it is setuid; therefore, it is definitely dangerous to use this package in a setuid program. SHAR_EOF # End of shell archive exit 0 -- Brandon Allbery, decvax!cwruecmp!ncoast!bsa, ncoast!bsa@case.csnet (etc.) 6504 Chestnut Road Independence, Ohio 44131 +1 216 524 1416 -- CIS 74106,1032 -=> Does the Doctor make house calls? <=-