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