diff --git a/Makefile b/Makefile index ed50a3ac4..0c728bc11 100644 --- a/Makefile +++ b/Makefile @@ -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, diff --git a/include/libbb.h b/include/libbb.h index 8cbeabc95..b71eddb14 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -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; diff --git a/libbb/exec.gno.c b/libbb/exec.gno.c index b9f9949af..306e1e682 100644 --- a/libbb/exec.gno.c +++ b/libbb/exec.gno.c @@ -1,4 +1,4 @@ -#include +#include "libbb.h" #include #include @@ -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); diff --git a/libbb/vfork.and.run.c b/libbb/vfork.and.run.c index d00c8d64c..d8d536820 100644 --- a/libbb/vfork.and.run.c +++ b/libbb/vfork.and.run.c @@ -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); } diff --git a/libbb/xfuncs.c b/libbb/xfuncs.c index 810809152..3caf5fd70 100644 --- a/libbb/xfuncs.c +++ b/libbb/xfuncs.c @@ -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 diff --git a/shell/hush.c b/shell/hush.c index 69452b064..eda7861af 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -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;