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>
