delorie.com/archives/browse.cgi   search  
Mail Archives: cygwin/2003/06/10/10:08:17

Mailing-List: contact cygwin-help AT cygwin DOT com; run by ezmlm
List-Subscribe: <mailto:cygwin-subscribe AT cygwin DOT com>
List-Archive: <http://sources.redhat.com/ml/cygwin/>
List-Post: <mailto:cygwin AT cygwin DOT com>
List-Help: <mailto:cygwin-help AT cygwin DOT com>, <http://sources.redhat.com/ml/#faqs>
Sender: cygwin-owner AT cygwin DOT com
Mail-Followup-To: cygwin AT cygwin DOT com
Delivered-To: mailing list cygwin AT cygwin DOT com
Message-ID: <337472B99242D4119EAF00805F312AE702CE2119@zcz04exm01.prag.ecid.cig.mot.com>
From: Hanak Michal-r50233 <Michal DOT Hanak AT motorola DOT com>
To: cygwin AT cygwin DOT com
Subject: 1.3.22: popen() fails without /bin/sh
Date: Tue, 10 Jun 2003 16:07:08 +0200
MIME-Version: 1.0
Note-from-DJ: This may be spam

------_=_NextPart_000_01C32F59.9424A932
Content-Type: text/plain;
	charset="iso-8859-2"

Hello,

it seems to me that the popen() does not work correctly in applications running on hosts without "/bin/sh" available. 

The Cygwin's popen() should normally work in a way that after vfork(), the execl("/bin/sh"...) is tried first and if it fails, the execlp("sh"...) is tried to find "sh" on PATH. (see source in newlib/libc/posix.c/popen.c, or code lines bellow).

A problem is that the execlp() gets never called because unsuccessful execl() causes vfork-ed child process to exit. Workaround is to use fork() instead of vfork() but this is perhaps too expensive. 

Does anybody face similar problem ? 
Is there any workaround if I want/need to use the built-in popen()?

Many Thanks,
Michal



Just for clarification, this is the sequence in newlib/libc/posix.c/popen.c:

....
execl(_PATH_BSHELL, "sh", "-c", program, NULL);
#ifdef __CYGWIN__
/* On cygwin32, we may not have /bin/sh.  In that
        case, try to find sh on PATH.  */
execlp("sh", "sh", "-c", program, NULL);
#endif
....


Attached there are two example files. The file "fails.c" shows application which fails to spawn simple "pwd" when /bin/sh is not on path. The file "works.c" shows the private popen2() workaround using fork().




------_=_NextPart_000_01C32F59.9424A932
Content-Type: application/octet-stream;
	name="fails.c"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filename="fails.c"

#include <stdio.h>=0A=
#include <stdlib.h>=0A=
=0A=
/* this shows how the popen fails to spawn "pwd" if /bin/sh is not on =
path */=0A=
=0A=
int main()=0A=
{=0A=
	char buff[41];=0A=
	FILE* f;=0A=
=0A=
	f =3D popen("pwd", "r");=0A=
=0A=
	if(!f)=0A=
	{=0A=
		printf("popen error\n");=0A=
		return 1;=0A=
	}=0A=
=0A=
	while(fgets(buff, 40, f))=0A=
		printf("read: %s\n", buff);=0A=
=0A=
	printf("pclose returned: %d\n", pclose(f));=0A=
	return 0;=0A=
}=0A=
=0A=

------_=_NextPart_000_01C32F59.9424A932
Content-Type: application/octet-stream;
	name="works.c"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filename="works.c"

#include <errno.h>=0A=
#include <unistd.h>=0A=
#include <stdio.h>=0A=
#include <stdlib.h>=0A=
=0A=
/* this shows how a private fork-based popen2() can be used instead of =
built-in popen */=0A=
=0A=
FILE * popen2(const char *program, const char *type);=0A=
int pclose2(FILE *iop);=0A=
=0A=
int main()=0A=
{=0A=
	char buff[41];=0A=
	FILE* f;=0A=
=0A=
	f =3D popen2("pwd", "r");=0A=
=0A=
	if(!f)=0A=
	{=0A=
		printf("popen2 error\n");=0A=
		return 1;=0A=
	}=0A=
=0A=
	while(fgets(buff, 40, f))=0A=
		printf("read: %s\n", buff);=0A=
=0A=
	printf("pclose2 returned: %d\n", pclose2(f));=0A=
	return 0;=0A=
}=0A=
=0A=
=0A=
=0A=
////////////////////////////////////////////////////////=0A=
// private popen2() - in-fact this is exact copy of=0A=
// newlib/libc/posix.c/popen.c with fork() instead of vfork()=0A=
=0A=
static struct pid {=0A=
	struct pid *next;=0A=
	FILE *fp;=0A=
	pid_t pid;=0A=
} *pidlist; =0A=
	=0A=
FILE * popen2(const char *program, const char *type)=0A=
{=0A=
	struct pid *cur;=0A=
	FILE *iop;=0A=
	int pdes[2], pid;=0A=
=0A=
       if ((*type !=3D 'r' && *type !=3D 'w')=0A=
	   || (type[1]=0A=
	       && (type[2] || (type[1] !=3D 'b' && type[1] !=3D 't'))=0A=
			       )) {=0A=
		errno =3D EINVAL;=0A=
		return (NULL);=0A=
	}=0A=
=0A=
	if ((cur =3D malloc(sizeof(struct pid))) =3D=3D NULL)=0A=
		return (NULL);=0A=
=0A=
	if (pipe(pdes) < 0) {=0A=
		free(cur);=0A=
		return (NULL);=0A=
	}=0A=
=0A=
	switch (pid =3D fork()) {=0A=
	case -1:			/* Error. */=0A=
		(void)close(pdes[0]);=0A=
		(void)close(pdes[1]);=0A=
		free(cur);=0A=
		return (NULL);=0A=
		/* NOTREACHED */=0A=
	case 0:				/* Child. */=0A=
		if (*type =3D=3D 'r') {=0A=
			if (pdes[1] !=3D STDOUT_FILENO) {=0A=
				(void)dup2(pdes[1], STDOUT_FILENO);=0A=
				(void)close(pdes[1]);=0A=
			}=0A=
			(void) close(pdes[0]);=0A=
		} else {=0A=
			if (pdes[0] !=3D STDIN_FILENO) {=0A=
				(void)dup2(pdes[0], STDIN_FILENO);=0A=
				(void)close(pdes[0]);=0A=
			}=0A=
			(void)close(pdes[1]);=0A=
		}=0A=
		execl("/bin/sh", "sh", "-c", program, NULL);=0A=
		/* On cygwin32, we may not have /bin/sh.  In that=0A=
                   case, try to find sh on PATH.  */=0A=
		execlp("sh", "sh", "-c", program, NULL);=0A=
		_exit(127);=0A=
		/* NOTREACHED */=0A=
	}=0A=
=0A=
	/* Parent; assume fdopen can't fail. */=0A=
	if (*type =3D=3D 'r') {=0A=
		iop =3D fdopen(pdes[0], type);=0A=
		(void)close(pdes[1]);=0A=
	} else {=0A=
		iop =3D fdopen(pdes[1], type);=0A=
		(void)close(pdes[0]);=0A=
	}=0A=
=0A=
	/* Link into list of file descriptors. */=0A=
	cur->fp =3D iop;=0A=
	cur->pid =3D  pid;=0A=
	cur->next =3D pidlist;=0A=
	pidlist =3D cur;=0A=
=0A=
	return (iop);=0A=
}=0A=
=0A=
/*=0A=
 * pclose --=0A=
 *	Pclose returns -1 if stream is not associated with a `popened' =
command,=0A=
 *	if already `pclosed', or waitpid returns an error.=0A=
 */=0A=
=0A=
int pclose2(FILE *iop)=0A=
{=0A=
	register struct pid *cur, *last;=0A=
	int pstat;=0A=
	pid_t pid;=0A=
=0A=
	(void)fclose(iop);=0A=
=0A=
	/* Find the appropriate file pointer. */=0A=
	for (last =3D NULL, cur =3D pidlist; cur; last =3D cur, cur =3D =
cur->next)=0A=
		if (cur->fp =3D=3D iop)=0A=
			break;=0A=
	if (cur =3D=3D NULL)=0A=
		return (-1);=0A=
=0A=
	do {=0A=
		pid =3D waitpid(cur->pid, &pstat, 0);=0A=
	} while (pid =3D=3D -1 && errno =3D=3D EINTR);=0A=
=0A=
	/* Remove the entry from the linked list. */=0A=
	if (last =3D=3D NULL)=0A=
		pidlist =3D cur->next;=0A=
	else=0A=
		last->next =3D cur->next;=0A=
	free(cur);=0A=
		=0A=
	return (pid =3D=3D -1 ? -1 : pstat);=0A=
}=0A=
=0A=


------_=_NextPart_000_01C32F59.9424A932
Content-Type: text/plain; charset=us-ascii

--
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple
Problem reports:       http://cygwin.com/problems.html
Documentation:         http://cygwin.com/docs.html
FAQ:                   http://cygwin.com/faq/
------_=_NextPart_000_01C32F59.9424A932--

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019