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>