Path: utzoo!news-server.csri.toronto.edu!rutgers!cs.utexas.edu!uunet!mcsun!hp4nl!star.cs.vu.nl!nat.vu.nl!maart From: maart@nat.vu.nl (Maarten Litmaath) Newsgroups: comp.unix.programmer Subject: Re: Detecting exec(2) failing after performing fork(2)? Keywords: spawn(), C source Message-ID: <1991Mar11.232053.8812@nat.vu.nl> Date: 11 Mar 91 22:20:53 GMT References: <1991Mar1.205944.13198@crc.skl.dnd.ca> <1991Mar4.231540.17644@nat.vu.nl> <1991Mar7.222033.4711@nat.vu.nl> Organization: Dept. of Physics, Vrije University, Amsterdam, The Netherlands Lines: 208 In article , shj@login.dkuug.dk (Stig Jacobsen) writes: >[...] >Since I didn't want to wait() for anything, I just performed >another fork() in the spawn() function and then let the first >child exit() imediately, so that the second child would get init >(process 1) as parent. This way I avoid a zombie process. > >Rather wastefull, yes, but I couldn't find another way to >disassociate my child process from the parent. This is quite a normal (portable!) way to dispose of child processes. Right, I've composed a general spawn() example, included below. Note that the calling syntax and semantics have changed. Enjoy! --------------------cut here-------------------- /* * spawn.c * * Usage: * * char *command, *argv[]; * int status, *pidp, *errp, (*preamble)(); * * status = spawn(command, argv, pidp, errp, preamble); * * Spawn `command' with arguments `argv' (argv[0] is the name); * the `argv' array ends with a NULL pointer. * * If `preamble' is not a NULL pointer, the addressed function will be * called in the child (before the execvp(), of course) with arguments * `command' and `argv'. This function may set some signals to be ignored, * reset the process group, redirect input, etc. Only if it returns 0 the * call to execvp() will be made; else spawn() will return -3 and if `errp' * is not a NULL pointer the return value of `preamble' will be copied into * `*errp'. * * The process ID of the child is copied into `*pidp', unless `pidp' is a * NULL pointer. * * The return value of spawn() itself is 0 on success, -1 if it could not * create the pipe that it needs, -2 if it could not fork(), -3 if `preamble' * was nonnull and did not return 0, and -4 if the execvp() failed. * * In the last case the errno value indicating why the execvp() failed * is copied into `*errp', unless `errp' is a NULL pointer. * * Compile with `-DNO_FCNTL' if your UNIX variant doesn't have fcntl(2). * Compile with `-DNO_STRERROR' if it doesn't have strerror(3). * * Author: Maarten Litmaath @ Dept. of Physics, Vrije University Amsterdam * Email: maart@nat.vu.nl * Date: 11 Mar 91 */ #include #ifdef NO_FCNTL #include #else #include #endif /* NO_FCNTL */ main(argc, argv) int argc; char **argv; { int status, spawn(), pid, error, preamble(); char *strerror(); if (argc == 1) { fprintf(stderr, "Usage: %s command args\n", argv[0]); exit(1); } /* start background job */ status = spawn(argv[1], &argv[1], &pid, &error, preamble); switch (status) { case 0: printf("The execvp() in the child succeeded.\n"); printf("The child has process ID %d.\n", pid); break; case -1: perror("pipe"); break; case -2: perror("fork"); break; case -3: printf("The preamble function returned %d.\n", error); break; case -4: printf("The execvp() in the child failed.\n"); printf("The reason was: %s.\n", strerror(error)); break; default: printf("Values of %d will give rise to dom!\n", status); /* pseudo V6 */ break; } return status; } int spawn(cmd, argv, pidp, errp, preamble) char *cmd, **argv; int *pidp, *errp, (*preamble)(); { extern int errno; int pp[2], n, pid; struct error { int status, errno; } err; if (pipe(pp) < 0) return -1; switch (pid = fork()) { case -1: close(pp[0]); close(pp[1]); return -2; case 0: close(pp[0]); /* set close-on-exec flag */ #ifdef NO_FCNTL ioctl(pp[1], FIOCLEX, (int *) 0); #else fcntl(pp[1], F_SETFD, 1); #endif /* NO_FCNTL */ if (preamble && (err.errno = (*preamble)(cmd, argv)) != 0) err.status = -3; else { execvp(cmd, argv); err.errno = errno; err.status = -4; } /* send a message indicating the failure */ write(pp[1], (char *) &err, sizeof err); _exit(1); } if (pidp) *pidp = pid; close(pp[1]); n = read(pp[0], (char *) &err, sizeof err); close(pp[0]); if (n != 0 && errp) *errp = err.errno; return n == 0 ? 0 : err.status; } #include static int Signals[] = { SIGHUP, /* ignore SIGHUP (on logout or hangup) */ SIGINT, /* ignore interrupts */ SIGQUIT, /* ignore quits */ #ifdef SIGTSTP SIGTSTP, /* ignore keyboard stop signals */ #endif /* SIGTSTP */ 0 }; /* ARGSUSED */ int preamble(cmd, argv) char *cmd, **argv; { int *sigp; for (sigp = Signals; *sigp; sigp++) signal(*sigp, SIG_IGN); return 0; } #ifdef NO_STRERROR char *strerror(n) int n; { extern int errno, sys_nerr; extern char *sys_errlist[]; static char buf[32]; if ((unsigned) n < sys_nerr) return sys_errlist[n]; sprintf(buf, "Unknown error %d", n); return buf; } #endif /* NO_STRERROR */