aboutsummaryrefslogblamecommitdiffstats
path: root/libexec_exec.c
blob: 3f306d0604ddeee646d45c6e4dea98fa7af02754 (plain) (tree)







































































































































































































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


static char *
which(const char *name, char *path, char **end_colon_out)
{
	size_t namelen_nul, dirlen, maxlen;
	char *p, *q, *buf;
	int eacces_encountered = 0;

	if (!path || !*path)
		goto enoent;

	maxlen = 0;
	for (p = path; p; p = q) {
		q = strchr(p, ':');
		if (q)
			dirlen = (size_t)(q++ - p);
		else
			dirlen = strlen(p);
		if (dirlen > maxlen)
			dirlen = maxlen;
	}

	namelen_nul = strlen(name) + 1;

	buf = NULL;
	if (maxlen) {
		buf = malloc(maxlen + 1 + namelen_nul);
		if (!buf)
			return NULL;
	}

	for (; path; path = q) {
		q = strchr(p, ':');
		if (q)
			dirlen = (size_t)(q++ - path);
		else
			dirlen = strlen(path);
		p = buf;
		if (dirlen) {
			memcpy(p, path, dirlen);
			p = &p[dirlen];
			*p++ = '/';
		}
		memcpy(p, name, namelen_nul);
		if (!faccessat(AT_FDCWD, buf, X_OK, AT_EACCESS)) {
			*end_colon_out = q;
			return buf;
		}
		if (errno == EACCES)
			eacces_encountered = 1;
	}

	free(buf);

	if (eacces_encountered) {
		errno = EACCES;
		return NULL;
	}

enoent:
	errno = ENOENT;
	return NULL;
}


static void
exec_path(struct libexec_command *cmd)
{
	const char *file = cmd->executable ? cmd->executable : cmd->arguments[0];
	execve(file, cmd->arguments, cmd->environ ? cmd->environ : environ);
}


static void
exec_name(struct libexec_command *cmd)
{
	/* execvpe is non-standard */
	const char *file = cmd->executable ? cmd->executable : cmd->arguments[0];
	char *end_colon = NULL, *file_free = NULL;
	if (!strchr(file, '/')) {
		file = file_free = which(file, getenv("PATH"), &end_colon);
		if (!file)
			return;
	}
	execve(file, cmd->arguments, cmd->environ ? cmd->environ : environ);
	free(file_free);
	if (end_colon)
		*end_colon = ':';
}


static void
exec_fd(struct libexec_command *cmd)
{
	fexecve(cmd->exec_fd, cmd->arguments, cmd->environ ? cmd->environ : environ);
}


static void
exec_at(struct libexec_command *cmd)
{
	const char *file = cmd->executable ? cmd->executable : cmd->arguments[0];
	execveat(cmd->exec_fd, file, cmd->arguments, cmd->environ ? cmd->environ : environ, 0);
}


int
libexec_exec(struct libexec_command *cmd)
{
	void (*exec_function)(struct libexec_command *cmd);
	size_t i;
	int fd;

	if (!cmd)
		goto einval;

	if (cmd->library_version > LIBEXEC_VERSION)
		abort();

	if (!cmd->narguments)
		goto einval;

	if (cmd->exec_how == LIBEXEC_REQUIRE_PATH)
		exec_function = &exec_path;
	else if (cmd->exec_how == LIBEXEC_ALLOW_NAME)
		exec_function = &exec_name;
	else if (cmd->exec_how == LIBEXEC_EXEC_FD)
		exec_function = &exec_fd;
	else if (cmd->exec_how == LIBEXEC_EXEC_AT)
		exec_function = &exec_at;
	else
		goto einval;

	for (i = 0; i < cmd->nplumings; i++) {
		switch (cmd->plumings[i].type) {
		case LIBEXEC_PLUMING_CLOSE:
			if (cmd->plumings[i].fd >= 0) {
				close(cmd->plumings[i].fd);
				cmd->plumings[i].fd = -1;
			}
			break;

#if defined(__linux__)
		case LIBEXEC_PLUMING_OPENAT2:
			fd = (int)syscall(SYS_openat2,
			                  cmd->plumings[i].target.dirfd, cmd->plumings[i].target.file,
			                  cmd->plumings[i].target.how, cmd->plumings[i].target.how_size);
			if (fd < 0)
				return -1;
			free(cmd->plumings[i].target.file);
			free(cmd->plumings[i].target.how);
			goto openned_fd;
#endif

		case LIBEXEC_PLUMING_OPENAT:
			fd = openat(cmd->plumings[i].target.dirfd, cmd->plumings[i].target.file,
			            cmd->plumings[i].target.flags, cmd->plumings[i].target.mode);
			if (fd < 0)
				return -1;
			free(cmd->plumings[i].target.file);
#if defined(__linux__)
		openned_fd:
#endif
			cmd->plumings[i].type = LIBEXEC_PLUMING_PIPE;
			cmd->plumings[i].target.fd = fd;
			/* fall through */

		case LIBEXEC_PLUMING_DUP2:
		case LIBEXEC_PLUMING_PIPE:
			if (cmd->plumings[i].fd == cmd->plumings[i].target.fd)
				break;
			if (dup2(cmd->plumings[i].target.fd, cmd->plumings[i].fd) == -1)
				return -1;
			if (cmd->plumings[i].type == LIBEXEC_PLUMING_PIPE)
				close(cmd->plumings[i].target.fd);
			cmd->plumings[i].target.fd = cmd->plumings[i].fd;
			break;

		default:
		case LIBEXEC_PLUMING_DOCUMENT:
			goto einval;
		}
	}

	(*exec_function)(cmd);
	return -1;

einval:
	errno = EINVAL;
	return -1;
}


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