Allow vfork()ing an external gunzip binary instead of using fork().
[oweals/opkg-lede.git] / libbb / gz_open.c
index 3997e72..bdc7564 100644 (file)
 #include <unistd.h>
 #include "libbb.h"
 
+static int gz_use_vfork;
+
 FILE *
 gz_open(FILE *compressed_file, int *pid)
 {
        int unzip_pipe[2];
+       off_t floc;
+       int cfile = -1;
+
+       gz_use_vfork = (getenv("OPKG_USE_VFORK") != NULL);
+
+       if (gz_use_vfork) {
+               /* Create a new file descriptor for the input stream
+                * (it *must* be associated with a file), and lseek()
+                * to the same position in that fd as the stream.
+                */
+               cfile = dup(fileno(compressed_file));
+               floc = ftello(compressed_file);
+               lseek(cfile, floc, SEEK_SET);
+               setenv("GZIP", "--quiet", 0);
+       }
 
        if (pipe(unzip_pipe)!=0) {
                perror_msg("pipe");
                return(NULL);
        }
 
-    /* If we don't flush, we end up with two copies of anything pending,
-       one from the parent, one from the child */
-    fflush(stdout);
-    fflush(stderr);
+       /* If we don't flush, we end up with two copies of anything pending,
+          one from the parent, one from the child */
+       fflush(stdout);
+       fflush(stderr);
 
-       if ((*pid = fork()) == -1) {
+       if (gz_use_vfork) {
+               *pid = vfork();
+       } else {
+               *pid = fork();
+       }
+
+       if (*pid<0) {
                perror_msg("fork");
                return(NULL);
        }
+
        if (*pid==0) {
                /* child process */
                close(unzip_pipe[0]);
-               unzip(compressed_file, fdopen(unzip_pipe[1], "w"));
-               fflush(NULL);
-               fclose(compressed_file);
-               close(unzip_pipe[1]);
-               _exit(EXIT_SUCCESS);
+               if (gz_use_vfork) {
+                       dup2(unzip_pipe[1], 1);
+                       dup2(cfile, 0);
+                       execlp("gunzip","gunzip",NULL);
+                       /* If we get here, we had a failure */
+                       _exit(EXIT_FAILURE);
+               } else {
+                       unzip(compressed_file, fdopen(unzip_pipe[1], "w"));
+                       fflush(NULL);
+                       fclose(compressed_file);
+                       close(unzip_pipe[1]);
+                       _exit(EXIT_SUCCESS);
+               }
+       }
+       /* Parent process is executing here */
+       if (gz_use_vfork) {
+               close(cfile);
        }
        close(unzip_pipe[1]);
        return(fdopen(unzip_pipe[0], "r"));
@@ -67,11 +103,29 @@ gz_close(int gunzip_pid)
        int status;
        int ret;
 
+       if (gz_use_vfork) {
+               /* The gunzip process remains running in the background if we
+                * used the vfork()/exec() technique - so we have to kill it
+                * forcibly.  There might be a better way to do this, but that
+                * affect a lot of other parts of opkg, and this works fine.
+                */
+               if (kill(gunzip_pid, SIGTERM) == -1) {
+                       perror_msg("gz_close(): unable to kill gunzip pid.");
+                       return -1;
+               }
+       }
+
+
        if (waitpid(gunzip_pid, &status, 0) == -1) {
                perror_msg("waitpid");
                return -1;
        }
 
+       if (gz_use_vfork) {
+               /* Bail out here if we used the vfork()/exec() technique. */
+               return 0;
+       }
+
        if (WIFSIGNALED(status)) {
                error_msg("Unzip process killed by signal %d.\n",
                        WTERMSIG(status));