[BACK]Return to cmd_exec.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / make

File: [local] / src / usr.bin / make / cmd_exec.c (download)

Revision 1.11, Thu Jan 16 16:07:18 2020 UTC (4 years, 4 months ago) by espie
Branch: MAIN
CVS Tags: OPENBSD_7_3_BASE, OPENBSD_7_3, OPENBSD_7_2_BASE, OPENBSD_7_2, OPENBSD_7_1_BASE, OPENBSD_7_1, OPENBSD_7_0_BASE, OPENBSD_7_0, OPENBSD_6_9_BASE, OPENBSD_6_9, OPENBSD_6_8_BASE, OPENBSD_6_8, OPENBSD_6_7_BASE, OPENBSD_6_7
Changes since 1.10: +3 -1 lines

turns out there WAS something fishy in signal handling in the "generic"
reaper. Specifically, the sigprocmask/wait/sigsuspend dance is correct for
the main process, BUT you have to remember to reset the signal mask to
something sane for the child (this was a duh! moment, that bug is very
stupid)


Finally, bluhm@  saw the actual issue. Kudoes to him.

The change to "unify" sequential and parallel make  made the bug reproducible
under some circumstances
(because in the parallel make case, many things may happen in different
successions, so you don't get the wrong signal mask that often, but the
sequential case is reproducible, and using the "streamlined" reaper meant the
fork would occur WITHIN the signal loop instead of OUTSIDE)

So:
- discover signal state early on through Sigset_init;
- introduce reset_signal_mask to get back to the initial state;
- call reset_signal_mask systematically after fork

This organisation thanks to cmd_exec.  SOME cmd_exec happens before Job_Init
happens, some afterwards (variables are still lazy and both !!= and :sh will
occur AFTER parsing), so both fork() need to be protected.

okay bluhm@

thx to sthen@ and naddy@ and mpi@ for helping out.

/*	$OpenBSD: cmd_exec.c,v 1.11 2020/01/16 16:07:18 espie Exp $ */
/*
 * Copyright (c) 2001 Marc Espie.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OPENBSD
 * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include "config.h"
#include "defines.h"
#include "cmd_exec.h"
#include "buf.h"
#include "memory.h"
#include "pathnames.h"
#include "job.h"

char *
Cmd_Exec(const char *cmd, char **err)
{
	char	*args[4];	/* Args for invoking the shell */
	int 	fds[2]; 	/* Pipe streams */
	pid_t 	cpid;		/* Child PID */
	char	*result;	/* Result */
	int 	status; 	/* Command exit status */
	BUFFER	buf;		/* Buffer to store the result. */
	char	*cp;		/* Pointer into result. */
	ssize_t	cc;		/* Characters read from pipe. */
	size_t	length;		/* Total length of result. */


	*err = NULL;

	/* Set up arguments for the shell. */
	args[0] = "sh";
	args[1] = "-c";
	args[2] = (char *)cmd;
	args[3] = NULL;

	/* Open a pipe for retrieving shell's output. */
	if (pipe(fds) == -1) {
		*err = "Couldn't create pipe for \"%s\"";
		goto bad;
	}

	/* Fork */
	switch (cpid = fork()) {
	case 0:
		reset_signal_mask();
		/* Close input side of pipe */
		(void)close(fds[0]);

		/* Duplicate the output stream to the shell's output, then
		 * shut the extra thing down. Note we don't fetch the error
		 * stream: user can use redirection to grab it as this goes
		 * through /bin/sh.
		 */
		if (fds[1] != 1) {
			(void)dup2(fds[1], 1);
			(void)close(fds[1]);
		}

		(void)execv(_PATH_BSHELL, args);
		_exit(1);
		/*NOTREACHED*/

	case -1:
		*err = "Couldn't exec \"%s\"";
		goto bad;

	default:
		/* No need for the writing half. */
		(void)close(fds[1]);

		Buf_Init(&buf, MAKE_BSIZE);

		do {
			char   grab[BUFSIZ];

			cc = read(fds[0], grab, sizeof(grab));
			if (cc > 0)
				Buf_AddChars(&buf, cc, grab);
		} while (cc > 0 || (cc == -1 && errno == EINTR));

		/* Close the input side of the pipe.  */
		(void)close(fds[0]);

		/* Wait for the child to exit.  */
		while (waitpid(cpid, &status, 0) == -1 && errno == EINTR)
			continue;

		if (cc == -1)
			*err = "Couldn't read shell's output for \"%s\"";

		if (status)
			*err = "\"%s\" returned non-zero status";

		length = Buf_Size(&buf);
		result = Buf_Retrieve(&buf);

		/* The result is null terminated, Convert newlines to spaces. */
		cp = result + length - 1;

		if (cp >= result && *cp == '\n')
			/* A final newline is just stripped.  */
			*cp-- = '\0';

		while (cp >= result) {
			if (*cp == '\n')
				*cp = ' ';
			cp--;
		}
		break;
	}
	return result;
    bad:
	return estrdup("");
}