Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 14 Oct 1998 07:25:14 -0700
From:      Wayne Scott <wscott@ichips.intel.com>
To:        current@FreeBSD.ORG
Subject:   simple popen() bug and solution (PATCH)
Message-ID:  <199810141425.HAA26802@pdxcs565.pdx.intel.com>

next in thread | raw e-mail | index | archive | help
This is a restatement of PR misc/7810.  Since that PR is making NO progress
I thought I would restate the problem in terms everyone can understand.

The Problem
-----------

The following program hangs on all current versions of FreeBSD, but
works on Linux, AIX, and HPUX.

    #include <stdio.h>
    #include <fcntl.h>

    main()
    {
	    FILE *file1;
	    FILE *file2;

	    file1 = popen("gzip > file1.gz", "w");
	    file2 = popen("gzip > file2.gz", "w");

	    fprintf(file1, "This is a test\n");
	    fprintf(file2, "This is also a test\n");

	    pclose(file1);
	    pclose(file2);
    }

The Cause
---------
    When popen exec's the second gzip process, that process inherits 
    the open filehandle 'file1'.  When 'pclose(file1)' is executed the
    first gzip does not exit because the second gzip is still holding
    an open handle for its input.

The Workaround
--------------
    If you reverse the order of the 'pclose()' calls then the program works
    correctly, but this is not possible in the big program I am really trying
    to get work.

The other Workaround
--------------------
    If you set the 'close_on_exec' flag on the 'file1' then the second gzip
    will not inheret open file handle.  
    ex.         fcntl(fileno(file1), F_SETFD, 1)

    This works, but is changes the semantics of popen().

What POSIX says
---------------
    /* Posix.2:  "popen() shall ensure that any streams from previous
       popen() calls that remain open in the parent process are closed
       in the new child process." */

The Solution
------------
    popen() already maintains a list of filehandles that have be previously 
    returned.  We just need to walk that list and close all the files.
    No need to deallocate the list because we are just about the exec()
    anyway.

Index: popen.c
===================================================================
RCS file: /home/ncvs/src/lib/libc/gen/popen.c,v
retrieving revision 1.9
diff -u -r1.9 popen.c
--- popen.c 1997/04/22 09:44:06 1.9
+++ popen.c 1998/09/02 18:47:49
@@ -62,6 +62,7 @@
  struct pid *cur;
  FILE *iop;
  int pdes[2], pid, twoway;
+ struct pid *p;

  /*
   * Lite2 introduced two-way popen() pipes using socketpair().
@@ -115,6 +116,9 @@
     (void)close(pdes[0]);
    }
    (void)close(pdes[1]);
+  }
+  for (p = pidlist; p; p = p->next) {
+   close(fileno(p->fp));
   }
   execl(_PATH_BSHELL, "sh", "-c", command, NULL);
        

I would love to have this fixed in the 3.0 release of FreeBSD.  Note I sent the
PR with the patch in plenty of time...

Wayne Scott		        MD6 Architecture - Intel Corp.
wscott@ichips.intel.com		Work #: (503) 613-5063
Disclaimer:  All views expressed are my own opinions, and not necessarily 
             those of Intel Corporation.

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-current" in the body of the message



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199810141425.HAA26802>