Detecting exec(2) failing after performing fork(2)?

Maarten Litmaath maart at nat.vu.nl
Tue Mar 12 08:20:53 AEST 1991


In article <shj.668447405 at dkuugin>,
	shj at 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 at nat.vu.nl
 * Date: 11 Mar 91
 */

#include	<stdio.h>

#ifdef	NO_FCNTL
#include	<sys/ioctl.h>
#else
#include	<fcntl.h>
#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	<signal.h>

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 */



More information about the Comp.unix.programmer mailing list