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