Mail Archives: cygwin/2003/06/10/10:08:17
------_=_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 -