str_list_prev: remove unused function
[oweals/opkg-lede.git] / libbb / gz_open.c
index f9ee8c10b53cdde04f2475316b17ae7872afe972..bf5efca31c8839b3127efb2205c0f37bb341b6be 100644 (file)
 #include <unistd.h>
 #include "libbb.h"
 
-extern FILE *gz_open(FILE *compressed_file, int *pid)
+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("%s: pipe", __FUNCTION__);
-               return(NULL);
+       if (pipe(unzip_pipe) != 0) {
+               perror_msg("pipe");
+               return (NULL);
        }
-       if ((*pid = fork()) == -1) {
-               perror_msg("%s: fork", __FUNCTION__);
-               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 (gz_use_vfork) {
+               *pid = vfork();
+       } else {
+               *pid = fork();
        }
-       if (*pid==0) {
+
+       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"));
+       return (fdopen(unzip_pipe[0], "r"));
 }
 
-extern void gz_close(int gunzip_pid)
+int gz_close(int gunzip_pid)
 {
-       if (waitpid(gunzip_pid, NULL, 0) == -1) {
-               perror_msg("%s wait", __FUNCTION__);
+       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));
+               return -1;
+       }
+
+       if (!WIFEXITED(status)) {
+               /* shouldn't happen */
+               error_msg
+                   ("Your system is broken: got status %d from waitpid.\n",
+                    status);
+               return -1;
+       }
+
+       if ((ret = WEXITSTATUS(status))) {
+               error_msg("Unzip process failed with return code %d.\n", ret);
+               return -1;
+       }
+
+       return 0;
 }