diff options
author | brian <brian@FreeBSD.org> | 2000-03-22 03:01:53 +0000 |
---|---|---|
committer | brian <brian@FreeBSD.org> | 2000-03-22 03:01:53 +0000 |
commit | 5bda7f13ae4f6baf62ee2b747c0dd16d990a5017 (patch) | |
tree | f9f933999acae31349cdb83976e8e52c2473719c | |
parent | b47896e80062927cee8f935e430194387da38913 (diff) | |
download | FreeBSD-src-5bda7f13ae4f6baf62ee2b747c0dd16d990a5017.zip FreeBSD-src-5bda7f13ae4f6baf62ee2b747c0dd16d990a5017.tar.gz |
Do some vfork() trickery so that the parent can determine
if the childs exec() has succeeded or failed by taking advantage
of the fact that both processes share the same memory.
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() ?
-rw-r--r-- | usr.sbin/ppp/exec.c | 44 |
1 files changed, 37 insertions, 7 deletions
diff --git a/usr.sbin/ppp/exec.c b/usr.sbin/ppp/exec.c index 5336dd3..e9baa9c 100644 --- a/usr.sbin/ppp/exec.c +++ b/usr.sbin/ppp/exec.c @@ -108,7 +108,8 @@ exec_Create(struct physical *p) log_Printf(LogPHASE, "Unable to create pipe for line exec: %s\n", strerror(errno)); else { - int stat, argc, i; + static int child_status; + int stat, argc, i, ret, wret; pid_t pid, realpid; char *argv[MAXARGS]; @@ -122,6 +123,7 @@ exec_Create(struct physical *p) case -1: log_Printf(LogPHASE, "Unable to create pipe for line exec: %s\n", strerror(errno)); + close(fids[1]); break; case 0: @@ -129,15 +131,20 @@ exec_Create(struct physical *p) timer_TermService(); setuid(ID0realuid()); - switch (fork()) { + child_status = 0; + switch (vfork()) { case 0: break; case -1: + ret = errno; log_Printf(LogPHASE, "Unable to fork to drop parent: %s\n", strerror(errno)); + _exit(ret); + break; + default: - _exit(127); + _exit(child_status); /* The error from exec() ! */ } log_Printf(LogDEBUG, "Exec'ing ``%s''\n", p->name.base); @@ -145,7 +152,7 @@ exec_Create(struct physical *p) if ((argc = MakeArgs(p->name.base, argv, VECSIZE(argv), PARSE_REDUCE|PARSE_NOHASH)) < 0) { log_Printf(LogWARN, "Syntax error in exec command\n"); - _exit(127); + _exit(ESRCH); } command_Expand(argv, argc, (char const *const *)argv, @@ -158,20 +165,43 @@ exec_Create(struct physical *p) fcntl(i, F_SETFD, 1); execvp(*argv, argv); - printf("execvp failed: %s: %s\r\n", *argv, strerror(errno)); - _exit(127); + child_status = errno; /* Only works for vfork() */ + printf("execvp failed: %s: %s\r\n", *argv, strerror(child_status)); + _exit(child_status); break; default: close(fids[1]); + while ((wret = waitpid(pid, &stat, 0)) == -1 && errno == EINTR) + ; + if (wret == -1) { + log_Printf(LogWARN, "Waiting for child process: %s\n", + strerror(errno)); + close(fids[0]); + break; + } else if (WIFSIGNALED(stat)) { + log_Printf(LogWARN, "Child process received sig %d !\n", + WTERMSIG(stat)); + close(fids[0]); + break; + } else if (WIFSTOPPED(stat)) { + log_Printf(LogWARN, "Child process received stop sig %d !\n", + WSTOPSIG(stat)); + /* I guess that's ok.... */ + } else if ((ret = WEXITSTATUS(stat))) { + log_Printf(LogWARN, "Cannot exec \"%s\": %s\n", p->name.base, + strerror(ret)); + close(fids[0]); + break; + } p->fd = fids[0]; - waitpid(pid, &stat, 0); log_Printf(LogDEBUG, "Using descriptor %d for child\n", p->fd); physical_SetupStack(p, execdevice.name, PHYSICAL_FORCE_ASYNC); if (p->cfg.cd.necessity != CD_DEFAULT) log_Printf(LogWARN, "Carrier settings ignored\n"); return &execdevice; } + close(fids[0]); } } |