Mailing-List: contact cygwin-help AT cygwin DOT com; run by ezmlm List-Subscribe: List-Archive: List-Post: List-Help: , 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 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 Content-Type: multipart/mixed; boundary="----_=_NextPart_000_01C32F59.9424A932" 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 =0A= #include =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 =0A= #include =0A= #include =0A= #include =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--