Implement "close-on-exec" functionality for GNO.

We do this by maintaining a mask (for each pid) giving the fds to be closed on exec. We wrap functions that close fds so that their close-on-exec bits can be cleared at that point.

This implementation may close the fds even if the execve operation ultimately fails.
This commit is contained in:
Stephen Heumann 2014-12-03 21:04:46 -06:00
parent 8cdae3cd7a
commit fb9b298ca4
6 changed files with 137 additions and 5 deletions

View File

@ -98,10 +98,6 @@ DEFINES = -Dhush_main=main -DNDEBUG
# ftp://ftp.gno.org/pub/apple2/gs.specific/gno/base/v204/gnodisk1.sdk
LIBS = -l/usr/lib/libtermcap.204
# Hack to effectively disable close_on_exec_on method for now.
# This will cause us to leak file descriptors. TODO: Fix.
DEFINES += -DF_SETFD=-1 -DFD_CLOEXEC=-1
# For correct handling of varargs methods and fork, we need
# optimize bit 3 set (no stack repair code).
# Optimize bit 6 breaks some standard-compliant varargs code,

View File

@ -388,6 +388,24 @@ void close_on_exec_on(int fd) FAST_FUNC;
void xdup2(int, int) FAST_FUNC;
void xmove_fd(int, int) FAST_FUNC;
#ifdef __GNO__
struct cloexec_ent {
pid_t pid;
uint32_t cloexec_mask;
};
struct cloexec_ent *get_cloexec_ent(pid_t pid);
int new_cloexec_ent(uint32_t initial_mask);
void close_cloexec_fds(void);
void close_on_exec_on(int fd);
void close_on_exec_off(int fd);
int close_wrapper(int fd);
int fclose_wrapper(FILE *stream);
int dup2_wrapper(int fd, int fd2);
# define close(fd) close_wrapper(fd)
# define fclose(stream) fclose_wrapper(stream)
# define dup2(fd, fd2) dup2_wrapper(fd, fd2)
#endif
DIR *xopendir(const char *path) FAST_FUNC;
DIR *warn_opendir(const char *path) FAST_FUNC;

View File

@ -1,4 +1,4 @@
#include <libbb.h>
#include "libbb.h"
#include <gsos.h>
#include <orca.h>
@ -93,6 +93,11 @@ int execve(const char *path, char *const *argv, char *const *envp)
goto error_ret;
}
/* This will close the close-on-exec fds even if the exec
* ultimately fails. This should be OK for our uses, because
* hush just prints an error message and exits in those cases. */
close_cloexec_fds();
_execve(path, args);
free(args);

View File

@ -38,6 +38,8 @@ pid_t vfork_and_run(void (*fn)(void*) NORETURN, void *arg) {
# pragma databank 1
void fork_thunk(void (*fn)(void*) NORETURN, void *arg, long sigmask) {
struct cloexec_ent *ent = get_cloexec_ent(getppid());
new_cloexec_ent(ent ? ent->cloexec_mask : 0);
sigsetmask(sigmask);
fn(arg);
}

View File

@ -45,11 +45,117 @@ int FAST_FUNC ndelay_off(int fd)
}
#endif
#ifdef __GNO__
# undef close
# undef fclose
# undef dup2
# define N_CLOEXEC_ENT 4
static struct cloexec_ent cloexec_table[N_CLOEXEC_ENT];
struct cloexec_ent *get_cloexec_ent(pid_t pid)
{
int i;
for (i = 0; i < N_CLOEXEC_ENT; i++) {
if (cloexec_table[i].pid == pid)
return &cloexec_table[i];
}
return NULL;
}
int new_cloexec_ent(uint32_t initial_mask)
{
int i;
int newpid = getpid();
/* Find an entry that doesn't correspond to an active process */
for (i = 0; i < N_CLOEXEC_ENT; i++) {
int pid = cloexec_table[i].pid;
if (pid == 0 || pid == newpid)
break;
if (_getpgrp(pid) == -1 && errno == ESRCH)
break;
}
if (i == N_CLOEXEC_ENT)
return -1;
cloexec_table[i].pid = newpid;
cloexec_table[i].cloexec_mask = initial_mask;
return 0;
}
void close_cloexec_fds(void)
{
int fd;
struct cloexec_ent *ent = get_cloexec_ent(getpid());
if (ent == NULL)
return;
for (fd = 0; fd < 32; fd++) {
if (ent->cloexec_mask & (1L << fd)) {
close(fd);
}
}
/* Free the entry (since we're about to exec) */
ent->pid = 0;
}
void close_on_exec_on(int fd)
{
struct cloexec_ent *ent = get_cloexec_ent(getpid());
if (ent == NULL || fd < 0 || fd >= 32)
return;
ent->cloexec_mask |= (1L << fd);
}
void close_on_exec_off(int fd)
{
struct cloexec_ent *ent = get_cloexec_ent(getpid());
if (ent == NULL || fd < 0 || fd >= 32)
return;
ent->cloexec_mask &= ~(1L << fd);
}
int close_wrapper(int fd)
{
int result = close(fd);
if (result == 0)
close_on_exec_off(fd);
return result;
}
int fclose_wrapper(FILE *stream)
{
int fd = fileno(stream);
int result = fclose(stream);
if (result == 0)
close_on_exec_off(fd);
return result;
}
int dup2_wrapper(int fd, int fd2)
{
int result = dup2(fd, fd2);
if (result == 0 && fd != fd2)
close_on_exec_off(fd2);
return result;
}
# define close(fd) close_wrapper(fd)
# define fclose(stream) fclose_wrapper(stream)
# define dup2(fd, fd2) dup2_wrapper(fd, fd2)
#else
void FAST_FUNC close_on_exec_on(int fd)
{
fcntl(fd, F_SETFD, FD_CLOEXEC);
}
#endif
char* FAST_FUNC strncpy_IFNAMSIZ(char *dst, const char *src)
{
#ifndef IFNAMSIZ

View File

@ -1403,6 +1403,10 @@ static QuitRecGS quitRec = {0, NULL, 0};
* problems), so we define our own version without this problem.
*/
void _exit_wrapper(int status) {
struct cloexec_ent *ent = get_cloexec_ent(getpid());
if (ent)
ent->pid = 0;
if (getpid() == G.last_execed_pid) {
// We're the root shell or one that's been exec'd...
// Call regular _exit()
@ -8220,6 +8224,7 @@ int hush_main(int argc, char **argv)
if (environPush() == 0)
atexit(environPop);
environInit();
new_cloexec_ent(0);
#endif
if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */
G.last_exitcode = EXIT_SUCCESS;