script: preserve child exit status
The patch also removes unnecessary detection of child process existence (by kill()). This code was replaces with SIGCHLD hold/release around fork(). Based on the patch from therealneworld@gmail.com. Signed-off-by: Karel Zak <kzak@redhat.com>
This commit is contained in:
parent
9f1d967639
commit
f1014a4f33
|
@ -41,6 +41,7 @@
|
|||
.Nm script
|
||||
.Op Fl a
|
||||
.Op Fl c Ar COMMAND
|
||||
.Op Fl e
|
||||
.Op Fl f
|
||||
.Op Fl q
|
||||
.Op Fl t
|
||||
|
@ -74,6 +75,9 @@ retaining the prior contents.
|
|||
Run the COMMAND rather than an interactive shell.
|
||||
This makes it easy for a script to capture the output of a program that
|
||||
behaves differently when its stdout is not a tty.
|
||||
.It Fl e
|
||||
Return the exit code of the child process. Uses the same format as bash
|
||||
termination on signal termination exit code is 128+n.
|
||||
.It Fl f
|
||||
Flush output after each write. This is nice for telecooperation:
|
||||
One person does `mkfifo foo; script -f foo' and another can
|
||||
|
|
|
@ -81,6 +81,7 @@ int master;
|
|||
int slave;
|
||||
int child;
|
||||
int subchild;
|
||||
int childstatus;
|
||||
char *fname;
|
||||
|
||||
struct termios tt;
|
||||
|
@ -92,6 +93,7 @@ char line[] = "/dev/ptyXX";
|
|||
#endif
|
||||
int aflg = 0;
|
||||
char *cflg = NULL;
|
||||
int eflg = 0;
|
||||
int fflg = 0;
|
||||
int qflg = 0;
|
||||
int tflg = 0;
|
||||
|
@ -127,6 +129,7 @@ die_if_link(char *fn) {
|
|||
|
||||
int
|
||||
main(int argc, char **argv) {
|
||||
sigset_t block_mask, unblock_mask;
|
||||
struct sigaction sa;
|
||||
extern int optind;
|
||||
char *p;
|
||||
|
@ -150,7 +153,7 @@ main(int argc, char **argv) {
|
|||
}
|
||||
}
|
||||
|
||||
while ((ch = getopt(argc, argv, "ac:fqt")) != -1)
|
||||
while ((ch = getopt(argc, argv, "ac:efqt")) != -1)
|
||||
switch((char)ch) {
|
||||
case 'a':
|
||||
aflg++;
|
||||
|
@ -158,6 +161,9 @@ main(int argc, char **argv) {
|
|||
case 'c':
|
||||
cflg = optarg;
|
||||
break;
|
||||
case 'e':
|
||||
eflg++;
|
||||
break;
|
||||
case 'f':
|
||||
fflg++;
|
||||
break;
|
||||
|
@ -170,7 +176,7 @@ main(int argc, char **argv) {
|
|||
case '?':
|
||||
default:
|
||||
fprintf(stderr,
|
||||
_("usage: script [-a] [-f] [-q] [-t] [file]\n"));
|
||||
_("usage: script [-a] [-e] [-f] [-q] [-t] [file]\n"));
|
||||
exit(1);
|
||||
}
|
||||
argc -= optind;
|
||||
|
@ -196,19 +202,30 @@ main(int argc, char **argv) {
|
|||
printf(_("Script started, file is %s\n"), fname);
|
||||
fixtty();
|
||||
|
||||
/* setup SIGCHLD handler */
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
|
||||
sa.sa_handler = finish;
|
||||
sigaction(SIGCHLD, &sa, NULL);
|
||||
|
||||
/* init mask for SIGCHLD */
|
||||
sigprocmask(SIG_SETMASK, NULL, &block_mask);
|
||||
sigaddset(&block_mask, SIGCHLD);
|
||||
|
||||
sigprocmask(SIG_SETMASK, &block_mask, &unblock_mask);
|
||||
child = fork();
|
||||
sigprocmask(SIG_SETMASK, &unblock_mask, NULL);
|
||||
|
||||
if (child < 0) {
|
||||
perror("fork");
|
||||
fail();
|
||||
}
|
||||
if (child == 0) {
|
||||
|
||||
sigprocmask(SIG_SETMASK, &block_mask, NULL);
|
||||
subchild = child = fork();
|
||||
sigprocmask(SIG_SETMASK, &unblock_mask, NULL);
|
||||
|
||||
if (child < 0) {
|
||||
perror("fork");
|
||||
fail();
|
||||
|
@ -233,9 +250,6 @@ doinput() {
|
|||
|
||||
(void) fclose(fscript);
|
||||
|
||||
if (die == 0 && child && kill(child, 0) == -1 && errno == ESRCH)
|
||||
die = 1;
|
||||
|
||||
while (die == 0) {
|
||||
if ((cc = read(0, ibuf, BUFSIZ)) > 0) {
|
||||
ssize_t wrt = write(master, ibuf, cc);
|
||||
|
@ -263,8 +277,10 @@ finish(int dummy) {
|
|||
register int pid;
|
||||
|
||||
while ((pid = wait3(&status, WNOHANG, 0)) > 0)
|
||||
if (pid == child)
|
||||
if (pid == child) {
|
||||
childstatus = status;
|
||||
die = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -303,17 +319,6 @@ dooutput() {
|
|||
my_strftime(obuf, sizeof obuf, "%c\n", localtime(&tvec));
|
||||
fprintf(fscript, _("Script started on %s"), obuf);
|
||||
|
||||
if (die == 0 && child && kill(child, 0) == -1 && errno == ESRCH)
|
||||
/*
|
||||
* the SIGCHLD handler could be executed when the "child"
|
||||
* variable is not set yet. It means that the "die" is zero
|
||||
* althought the child process is already done. We have to
|
||||
* check this thing now. Now we have the "child" variable
|
||||
* already initialized. For more details see main() and
|
||||
* finish(). --kzak 07-Aug-2007
|
||||
*/
|
||||
die = 1;
|
||||
|
||||
do {
|
||||
if (die && flgs == 0) {
|
||||
/* ..child is dead, but it doesn't mean that there is
|
||||
|
@ -436,6 +441,13 @@ done() {
|
|||
if (!qflg)
|
||||
printf(_("Script done, file is %s\n"), fname);
|
||||
}
|
||||
|
||||
if(eflg) {
|
||||
if (WIFSIGNALED(childstatus))
|
||||
exit(WTERMSIG(childstatus) + 0x80);
|
||||
else
|
||||
exit(WEXITSTATUS(childstatus));
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue