aboutsummaryrefslogblamecommitdiffstats
path: root/libexec_vrun.c
blob: 00a5f82a97e5381c7e823b22a3f0ddd1c24a3c96 (plain) (tree)



















































                                                                               
                                    





























































































































































































































































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


int
libexec_vrun(struct libexec_result *out, va_list args)
{
#define CMD (cmds[ncmds - 1])

	struct libexec_document *doc, **docs = NULL;
	struct libexec_command **cmds = NULL;
	size_t ncmds = 0, ndocs = 0, i;
	const char *fmt, *arg_str1, *arg_str2;
	char *arg_str_nc;
	int arg_int1, arg_int2, arg_int3, fd, *exit_statuses = NULL;
	enum libexec_insert_mode insert_mode;
	void *new, *arg_voidp1;
	enum libexec_pipe pipe_how = 0;
	struct epoll_event ev;
	int alien_epoll = -1;
	const sigset_t *sigmask;
	int (*on_alien_epoll)(int alien_epoll, uint32_t events, void *user);
	int (*on_alien_child_death)(pid_t pid, void *user);
	int (*after_fork)(struct libexec_command *cmd, int new_fd, void *user);
	int (*reap_mutex_control)(int action, void *user);
	int (*on_interrupt)(void *user);
	void *on_alien_epoll_user = NULL;
	void *on_alien_child_death_user = NULL;
	void *after_fork_user = NULL;
	void *reap_mutex_control_user = NULL;
	void *on_interrupt_user = NULL;
	int ret = -1;

	if (!out) {
		errno = EINVAL;
		return -1;
	}

	memset(&ev, 0, sizeof(ev));
	memset(out, 0, sizeof(*out));
	out->exit_statuses = NULL;

new_command:
	new = realloc(cmds, (ncmds + 1) * sizeof(*cmds));
	if (!new)
		goto fail;
	cmds = new;
	ncmds += 1;
	CMD = malloc(sizeof(CMD));
	if (!CMD)
		goto fail;
	*CMD = LIBEXEC_COMMAND_INIT;
	fmt = va_arg(args, const char *);
	if (libexec_vconstruct_command(CMD, fmt, args))
		goto fail;

	for (;;) {
		switch (va_arg(args, enum libexec_run_instruction)) {
		case LIBEXEC_RUN_END:
			if (ncmds > 1)
				if (libexec_pipe_commands(pipe_how, cmds[ncmds - 2], cmds[ncmds - 1]))
					goto fail;
			goto constructed;

		case LIBEXEC_RUN_END_CIRCULAR_PIPE:
			if (ncmds > 1)
				if (libexec_pipe_commands(pipe_how, cmds[ncmds - 2], cmds[ncmds - 1]))
					goto fail;
			pipe_how = va_arg(args, enum libexec_pipe);
			if (libexec_pipe_commands(pipe_how, cmds[ncmds - 1], cmds[0]))
				goto fail;
			goto constructed;

		case LIBEXEC_RUN_PIPE:
			if (ncmds > 1)
				if (libexec_pipe_commands(pipe_how, cmds[ncmds - 2], cmds[ncmds - 1]))
					goto fail;
			pipe_how = va_arg(args, enum libexec_pipe);
			goto new_command;

		case LIBEXEC_RUN_SET_EXECUTABLE:
			if (libexec_set_executable(CMD, va_arg(args, const char *)))
				goto fail;
			break;

		case LIBEXEC_RUN_SET_REQUIRE_PATH:
			libexec_set_require_path(CMD, va_arg(args, int));
			break;

		case LIBEXEC_RUN_SET_EXEC_FD:
			if (libexec_set_exec_fd(CMD, va_arg(args, int)))
				goto fail;
			break;

		case LIBEXEC_RUN_SET_EXEC_PATH:
			arg_int1 = va_arg(args, int);
			arg_str1 = va_arg(args, const char *);
			if (libexec_set_exec_path(CMD, arg_int1, arg_str1))
				goto fail;
			break;

		case LIBEXEC_RUN_COPY_ENVIRON:
			if (libexec_copy_environ(CMD, va_arg(args, const char *const *)))
				goto fail;
			break;

		case LIBEXEC_RUN_SET_ENVIRON:
			libexec_set_environ(CMD, va_arg(args, char **));
			break;

		case LIBEXEC_RUN_CLEAR_ENVIRON:
			if (libexec_clear_environ(CMD))
				goto fail;
			break;

		case LIBEXEC_RUN_UNSETENV:
			if (libexec_unsetenv(CMD, va_arg(args, const char *)))
				goto fail;
			break;

		case LIBEXEC_RUN_PUTENV:
			insert_mode = va_arg(args, enum libexec_insert_mode);
			arg_str1 = va_arg(args, const char *);
			if (libexec_putenv(CMD, insert_mode, arg_str1))
				goto fail;
			break;

		case LIBEXEC_RUN_SETENV:
			insert_mode = va_arg(args, enum libexec_insert_mode);
			arg_str1 = va_arg(args, const char *);
			arg_str2 = va_arg(args, const char *);
			if (libexec_setenv(CMD, insert_mode, arg_str1, arg_str2))
				goto fail;
			break;

		case LIBEXEC_RUN_PUTENVF:
			insert_mode = va_arg(args, enum libexec_insert_mode);
			arg_str1 = va_arg(args, const char *);
			if (libexec_vputenvf(CMD, insert_mode, arg_str1, args))
				goto fail;
			break;

		case LIBEXEC_RUN_SETENVF:
			insert_mode = va_arg(args, enum libexec_insert_mode);
			arg_str1 = va_arg(args, const char *);
			arg_str2 = va_arg(args, const char *);
			if (libexec_vsetenvf(CMD, insert_mode, arg_str1, arg_str2, args))
				goto fail;
			break;

		case LIBEXEC_RUN_OPEN:
			arg_int1 = va_arg(args, int);
			arg_str1 = va_arg(args, const char *);
			arg_int2 = va_arg(args, int);
			if (libexec_open(CMD, arg_int1, arg_str1, arg_int2, va_arg(args, mode_t)))
				goto fail;
			break;

		case LIBEXEC_RUN_OPENAT:
			arg_int1 = va_arg(args, int);
			arg_int2 = va_arg(args, int);
			arg_str1 = va_arg(args, const char *);
			arg_int3 = va_arg(args, int);
			if (libexec_openat(CMD, arg_int1, arg_int2, arg_str1, arg_int3, va_arg(args, mode_t)))
				goto fail;
			break;

		case LIBEXEC_RUN_OPENAT2:
			arg_int1 = va_arg(args, int);
			arg_int2 = va_arg(args, int);
			arg_str1 = va_arg(args, const char *);
			arg_voidp1 = va_arg(args, void *);
			if (libexec_openat2(CMD, arg_int1, arg_int2, arg_str1, arg_voidp1, va_arg(args, size_t)))
				goto fail;
			break;

		case LIBEXEC_RUN_DUP:
			arg_int1 = va_arg(args, int);
			if (libexec_dup(CMD, arg_int1, va_arg(args, int)))
				goto fail;
			break;

		case LIBEXEC_RUN_CLOSE:
			if (libexec_close(CMD, va_arg(args, int)))
				goto fail;
			break;

		case LIBEXEC_RUN_RENUMBER_FD:
			arg_int1 = va_arg(args, int);
			if (libexec_renumber_fd(CMD, arg_int1, va_arg(args, int)))
				goto fail;
			break;

		case LIBEXEC_RUN_INPUT_COPY:
			arg_int1 = va_arg(args, int);
			arg_str1 = va_arg(args, const char *);
			if (libexec_input_data_copy(CMD, arg_int1, arg_str1, va_arg(args, size_t)))
				goto fail;
			break;

		case LIBEXEC_RUN_INPUT_GIFT:
			arg_int1 = va_arg(args, int);
			arg_str_nc = va_arg(args, char *);
			if (libexec_input_data_gift(CMD, arg_int1, arg_str_nc, va_arg(args, size_t)))
				goto fail;
			break;

		case LIBEXEC_RUN_ADD_OUTPUT_FD:
			arg_int1 = va_arg(args, int);
			fd = va_arg(args, int);
			if (libexec_add_output_fd(CMD, arg_int1, fd))
				goto fail;
			if (alien_epoll >= 0) {
				ev.events = EPOLLIN;
				ev.data.fd = fd;
				if (epoll_ctl(alien_epoll, EPOLL_CTL_ADD, fd, &ev))
					goto fail;
			}
			break;

		case LIBEXEC_RUN_ADD_OUTPUT:
			arg_int1 = va_arg(args, int);
			doc = va_arg(args, struct libexec_document *);
			new = realloc(docs, (ndocs + 1) * sizeof(*docs));
			if (!new)
				goto fail;
			docs = new;
			docs[ndocs++] = doc;
			if (libexec_add_output_document(CMD, arg_int1, doc, O_CLOEXEC | O_NONBLOCK))
				goto fail;
			break;

		case LIBEXEC_RUN_SET_SIGMASK:
			sigmask = va_arg(args, const sigset_t *);
			break;

		case LIBEXEC_RUN_SET_FD_CALLBACK:
			on_alien_epoll = va_arg(args, int (*)(int, uint32_t, void *));
			on_alien_epoll_user = va_arg(args, void *);
			alien_epoll = epoll_create1(EPOLL_CLOEXEC);
			if (alien_epoll < 0)
				goto fail;
			break;

		case LIBEXEC_RUN_SET_REAPER:
			on_alien_child_death = va_arg(args, int (*)(pid_t, void *));
			on_alien_child_death_user = va_arg(args, void *);
			break;

		case LIBEXEC_RUN_SET_ATFORK:
			after_fork = va_arg(args, int (*)(struct libexec_command *, int, void *));
			after_fork_user = va_arg(args, void *);
			break;

		case LIBEXEC_RUN_SET_MUTEX:
			reap_mutex_control = va_arg(args, int (*)(int, void *));
			reap_mutex_control_user = va_arg(args, void *);
			break;

		case LIBEXEC_RUN_SET_INTERRUPT_CALLBACK:
			on_interrupt = va_arg(args, int (*)(void *));
			on_interrupt_user = va_arg(args, void *);
			break;

		default:
			errno = EINVAL;
			goto fail;
		}
	}

constructed:
	ret = libexec_run_pipeline(on_alien_epoll, alien_epoll, on_alien_epoll_user,
	                           on_alien_child_death, on_alien_child_death_user,
	                           after_fork, after_fork_user,
	                           reap_mutex_control, reap_mutex_control_user,
	                           on_interrupt, on_interrupt_user,
	                           sigmask, docs, ndocs, 0, cmds, exit_statuses, ncmds);
	if (!ret) {
		out->exit_statuses = exit_statuses;
		out->proc_count = ncmds;
		for (i = 0; i < ncmds; i++)
			if (exit_statuses[i])
				break;
		out->all_successful = (i == ncmds);
		exit_statuses = NULL;
	}

fail:
	while (ncmds--) {
		if (cmds[ncmds]) {
			libexec_destroy_command(cmds[ncmds]);
			free(cmds[ncmds]);
		}
	}
	free(cmds);
	free(docs);
	if (alien_epoll >= 0)
		close(alien_epoll);
	free(exit_statuses);
	return ret;
}


#else
TESTED_ELSEWHERE /* libexec_run.c */
#endif