Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 22 Mar 2000 10:13:56 +0000
From:      Brian Somers <brian@Awfulhak.org>
To:        Brian Fundakowski Feldman <green@FreeBSD.org>
Cc:        Brian Somers <brian@FreeBSD.org>, cvs-committers@FreeBSD.org, cvs-all@FreeBSD.org
Subject:   Re: Ultimate Re: cvs commit: src/usr.sbin/ppp exec.c 
Message-ID:  <200003221013.KAA01119@hak.lan.Awfulhak.org>
In-Reply-To: Message from Brian Fundakowski Feldman <green@FreeBSD.org>  of "Wed, 22 Mar 2000 01:03:46 EST." <Pine.BSF.4.21.0003220101370.22960-100000@green.dyndns.org> 

next in thread | previous in thread | raw e-mail | index | archive | help
> > > On Tue, 21 Mar 2000, Brian Somers wrote:
> > > 
> > > >   FWIW:
> > > >     I tried to implement this by doing a pipe(), setting the
> > > >     write desciptors close-on-exec flag in the child and writing
> > > >     errno to the descriptor if the exec() fails.  The parent can
> > > >     then ``if (read()) got errno else exec worked''.
> > > >   
> > > >     This didn't work though - the child could write() to fd[1] on
> > > >     exec failure, but the parent got 0 trying to read() from fd[0] !
> > > >     Is this a bug in execve() ?
> 
> Finally!  I have what I think is exactly what you want.  Here's the
> test output:
[.....]

Not quite, but now that you've got me thinking (you're program is 
pretty close to what I was trying to do).  This doesn't make sense !

This works - try

  ./foo true
  ./foo alskjd
  ./foo sleep 100

#include <sys/types.h>
#include <sys/time.h>
#include <sys/wait.h>

#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

int
main(int argc, char **argv)
{
	int pipes[2], error, status, ret;
	pid_t pid;

	if (argc <= 1)
		errx(1, "Tell me what to exec");

	switch ((pid = fork())) {
	default:
		while ((error = wait(&status)) == -1 && errno == EINTR)
			;
		if (error == -1)
			err(1, "parent wait");
		else {
			fputs("Parent: ", stdout);
			if (WIFEXITED(status))
				printf("child exited with status %d.\n",
				    WEXITSTATUS(status));
			else if (WIFSIGNALED(status))
				printf("child exited on signal %d.\n",
				    WTERMSIG(status));
			else
				printf("child stoped on signal %d.\n",
				    WSTOPSIG(status));
		}
		exit(0);

	case -1:
		err(1, "fork");

	case 0:
		error = pipe(pipes);
		if (error != 0)
			err(1, "intermediate pipe");

		switch (fork()) {	/* Don't want defunct processes */
		case 0:
			close(pipes[0]);
			break;
		case -1:
			ret = errno;
			close(pipes[0]);
			close(pipes[1]);
			_exit(ret);
		default:
			/*
			 * Intermediate - find out what the child can
			 * do and report to parent
			 */
			close(pipes[1]);
			error = read(pipes[0], &ret, sizeof ret);
			switch (error) {
			case -1:
				ret = errno;
				break;
			case 0:
				printf("Intermediate: 0 from read (good!)\n");
				ret = 0;
				break;

			case sizeof ret:
				break;

			default:
				ret = EIO;
				break;
			}
			_exit(ret);
		}

		/*
		 * Child.  Exec the process, reporting to the parent via
		 * pipes[1]
		 */
		if (fcntl(pipes[1], F_SETFD, 1) != 0)
			err(1, "Child: fcntl");
		printf("Child: execing %s\n", argv[1]);
		execvp(argv[1], argv + 1);
		ret = errno;
		if (write(pipes[1], &ret, sizeof ret) != sizeof ret)
			err(1, "Child write");
		printf("Child: sent status %d after failure\n", ret);
		_exit(ret);
	}
}


*BUT*, now try the attached patches to ppp.  You want to have an empty 
default: section and then this in ppp.conf:

exec:
 set ifaddr 127.0.0.2 127.0.0.3
 set device "!/bin/pwx" "!/bin/pwy"
 set redial 2 2

Then run ``ppp -b exec''.  With the current code, it correctly fails 
to execute pwx and tries pwy and gets things right.

With the patched code (which *should* work) it craps out at the first 
hurdle 'cos it things the exec succeeded - see the diagnostics !

So go figure :*P

> --
>  Brian Fundakowski Feldman           \  FreeBSD: The Power to Serve!  /
>  green@FreeBSD.org                    `------------------------------'

Index: exec.c
===================================================================
RCS file: /home/ncvs/src/usr.sbin/ppp/exec.c,v
retrieving revision 1.21
diff -u -r1.21 exec.c
--- exec.c	2000/03/22 03:01:53	1.21
+++ exec.c	2000/03/22 09:07:43
@@ -109,7 +109,7 @@
 	         strerror(errno));
     else {
       static int child_status;
-      int stat, argc, i, ret, wret;
+      int stat, argc, i, ret, wret, fd[2];
       pid_t pid, realpid;
       char *argv[MAXARGS];
 
@@ -131,19 +131,42 @@
           timer_TermService();
           setuid(ID0realuid());
 
-          child_status = 0;
-          switch (vfork()) {
+          if (pipe(fd) == -1)
+              _exit(errno);
+
+          switch (fork()) {
             case 0:
+              close(fd[0]);
               break;
 
             case -1:
               ret = errno;
+              close(fd[0]);
+              close(fd[1]);
               log_Printf(LogPHASE, "Unable to fork to drop parent: %s\n",
 	                 strerror(errno));
               _exit(ret);
               break;
 
             default:
+              close(fd[1]);
+              switch ((ret = read(fd[0], &child_status, sizeof child_status))) {
+                case -1:
+                  child_status = errno;
+                  break;
+
+                case 0:
+                  log_Printf(LogPHASE, "Cool !  Got zero from read !\n");
+                  child_status = 0;
+                  break;
+
+                case sizeof wret:
+                  break;
+
+                default:
+                  child_status = EIO;
+                  break;
+              }
               _exit(child_status);	/* The error from exec() ! */
           }
 
@@ -163,10 +186,17 @@
           dup2(fids[1], STDERR_FILENO);
           for (i = getdtablesize(); i > STDERR_FILENO; i--)
             fcntl(i, F_SETFD, 1);
+          fcntl(fd[1], F_SETFD, 1);
 
           execvp(*argv, argv);
-          child_status = errno;		/* Only works for vfork() */
+          child_status = errno;
           printf("execvp failed: %s: %s\r\n", *argv, strerror(child_status));
+          wret = write(fd[1], &child_status, sizeof child_status);
+          if (wret == sizeof child_status)
+            log_Printf(LogPHASE, "Reported exec failure %d\n", child_status);
+          else
+            log_Printf(LogPHASE, "Didn't report exec failure %d !\n",
+                       child_status);
           _exit(child_status);
           break;
 

-- 
Brian <brian@Awfulhak.org>                        <brian@[uk.]FreeBSD.org>
      <http://www.Awfulhak.org>;                   <brian@[uk.]OpenBSD.org>
Don't _EVER_ lose your sense of humour !





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




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