aboutsummaryrefslogblamecommitdiffstats
path: root/libexec_spawn.c
blob: f0b46e63de5a421c6493a73e63e43cda56947f34 (plain) (tree)
























































































                                                                                            
/* See LICENSE file for copyright and license details. */
#include "common.h"
#ifndef TEST


int
libexec_spawn(pid_t *out, struct libexec_command *cmd,
              int (*after_fork)(struct libexec_command *cmd, int new_fd, void *user),
              void *user, struct libexec_document **docsp, size_t *ndocsp, int doc_fd_flags)
{
	int fds[2], error, new_fd;
	size_t i;
	ssize_t r;

	if (!docsp || !ndocsp || !out || !cmd) {
		errno = EINVAL;
		return -1;
	}

	*out = -1;

	if (pipe2(fds, O_CLOEXEC))
		return -1;

	new_fd = fds[1];
	for (i = 0; i < cmd->nplumings; i++)
		if (cmd->plumings[i].fd >= new_fd)
			new_fd = cmd->plumings[i].fd + 1;

	if (new_fd != fds[1]) {
		new_fd = fcntl(fds[1], F_DUPFD_CLOEXEC, new_fd);
		if (new_fd == -1)
			goto fail;
		fds[1] = new_fd;
	}

	if (libexec_get_documents(cmd, docsp, ndocsp, doc_fd_flags))
		goto fail;

	*out = fork();
	switch (*out) {
	case -1:
		goto fail;

	case 0:
		close(fds[0]);
		if (after_fork)
			if ((*after_fork)(cmd, fds[1], user))
				goto child_fail;
		libexec_exec(cmd);
	child_fail:
		error = errno;
	write_again:
		r = write(fds[1], &error, sizeof(error));
		if (r <= 0) {
			if (errno == EINTR)
				goto write_again;
			abort();
		}
		_exit(1);

	default:
		close(fds[1]);
	read_again:
		r = read(fds[0], &error, sizeof(error));
		if (r < 0) {
			if (errno == EINTR)
				goto read_again;
		} else if (r == 0) {
			return 0;
		} else if ((size_t)r >= sizeof(error)) {
			errno = error;
		}

		return -1;
	}

fail:
	error = errno;
	close(fds[0]);
	close(fds[1]);
	errno = error;
	return -1;
}


#else
LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */
#endif