]> err.no Git - util-linux/commitdiff
script: preserve child exit status
authorKarel Zak <kzak@redhat.com>
Tue, 27 Apr 2010 10:11:31 +0000 (12:11 +0200)
committerKarel Zak <kzak@redhat.com>
Tue, 27 Apr 2010 10:11:31 +0000 (12:11 +0200)
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>
misc-utils/script.1
misc-utils/script.c

index e017b63bcc55ca43216ecb55fccc41a62e51503e..24c6b5723029462c733ff229a73608a0c519a1ad 100644 (file)
@@ -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
index b877c31298a296759614e40c4039961e1283ced5..e3ccb1a18526e17073527bd804ade7c24da5dbfb 100644 (file)
@@ -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);
 }