Project

General

Profile

Feature #5041 ยป close-on-exec-by-default.patch

akr (Akira Tanaka), 08/11/2011 02:43 PM

View differences:

NEWS (working copy)
* Signal.trap
See above.
* incompatible changes:
The :close_others option is true by default for system() and exec().
The close on exec flag is set by default for all new file descriptors.
This means file descriptors doesn't inherit to spawned process unless
explicitly requested as system(..., fd=>fd).
include/ruby/intern.h (working copy)
int rb_reserved_fd_p(int fd);
#define RB_RESERVED_FD_P(fd) rb_reserved_fd_p(fd)
void rb_update_max_fd(int fd);
void rb_fd_set_cloexec(int fd);
/* marshal.c */
VALUE rb_marshal_dump(VALUE, VALUE);
VALUE rb_marshal_load(VALUE);
thread_pthread.c (working copy)
if (err != 0) {
rb_bug_errno("thread_timer: Failed to create communication pipe for timer thread", errno);
}
rb_update_max_fd(timer_thread_pipe[0]);
rb_update_max_fd(timer_thread_pipe[1]);
rb_fd_set_cloexec(timer_thread_pipe[0]);
rb_fd_set_cloexec(timer_thread_pipe[1]);
#if defined(HAVE_FCNTL) && defined(F_GETFL) && defined(F_SETFL)
{
int oflags;
io.c (working copy)
if (max_file_descriptor < fd) max_file_descriptor = fd;
}
void rb_fd_set_cloexec(int fd)
{
int flags, ret;
flags = fcntl(fd, F_GETFD); /* should not fail except EBADF. */
if (flags == -1) {
rb_bug("rb_fd_set_cloexec: fcntl(%d, F_GETFD) failed: %s", fd, strerror(errno));
}
if (2 < fd) {
if (!(flags & FD_CLOEXEC)) {
flags |= FD_CLOEXEC;
ret = fcntl(fd, F_SETFD, flags);
if (ret == -1) {
rb_bug("rb_fd_set_cloexec: fcntl(%d, F_SETFD, %d) failed: %s", fd, flags, strerror(errno));
}
}
}
if (max_file_descriptor < fd) max_file_descriptor = fd;
}
#define argf_of(obj) (*(struct argf *)DATA_PTR(obj))
#define ARGF argf_of(argf)
......
rb_sys_fail(0);
}
}
rb_update_max_fd(fd);
rb_fd_set_cloexec(fd);
return fd;
}
......
int fd;
fd = (int)rb_thread_blocking_region(sysopen_func, data, RUBY_UBF_IO, 0);
if (0 <= fd)
rb_update_max_fd(fd);
rb_fd_set_cloexec(fd);
return fd;
}
......
}
#endif
if (ret == 0) {
rb_update_max_fd(pipes[0]);
rb_update_max_fd(pipes[1]);
rb_fd_set_cloexec(pipes[0]);
rb_fd_set_cloexec(pipes[1]);
}
return ret;
}
......
/* need to keep FILE objects of stdin, stdout and stderr */
if (dup2(fd2, fd) < 0)
rb_sys_fail_path(orig->pathv);
rb_update_max_fd(fd);
rb_fd_set_cloexec(fd);
}
else {
fclose(fptr->stdio_file);
......
fptr->fd = -1;
if (dup2(fd2, fd) < 0)
rb_sys_fail_path(orig->pathv);
rb_update_max_fd(fd);
rb_fd_set_cloexec(fd);
fptr->fd = fd;
}
rb_thread_fd_close(fd);
......
retval = (int)rb_thread_io_blocking_region(nogvl_io_cntl, &arg, fd);
#if defined(F_DUPFD)
if (!io_p && retval != -1 && cmd == F_DUPFD) {
rb_update_max_fd(retval);
rb_fd_set_cloexec(retval);
}
#endif
process.c (working copy)
rb_exec_arg_init(argc, argv, TRUE, &earg);
if (NIL_P(rb_ary_entry(earg.options, EXEC_OPTION_CLOSE_OTHERS)))
rb_exec_arg_addopt(&earg, ID2SYM(rb_intern("close_others")), Qfalse);
rb_exec_arg_addopt(&earg, ID2SYM(rb_intern("close_others")), Qtrue);
rb_exec_arg_fixup(&earg);
rb_exec_err(&earg, errmsg, sizeof(errmsg));
......
ret = fcntl(fdp[i], F_DUPFD, min);
if (ret == -1)
return -1;
rb_update_max_fd(ret);
rb_fd_set_cloexec(ret);
close(fdp[i]);
fdp[i] = ret;
}
......
chfunc = signal(SIGCHLD, SIG_DFL);
#endif
pid = rb_spawn_internal(argc, argv, FALSE, NULL, 0);
pid = rb_spawn_internal(argc, argv, TRUE, NULL, 0);
#if defined(HAVE_FORK) || defined(HAVE_SPAWNV)
if (pid > 0) {
rb_syswait(pid);
......
* integer : the file descriptor of specified the integer
* io : the file descriptor specified as io.fileno
* file descriptor inheritance: close non-redirected non-standard fds (3, 4, 5, ...) or not
* :close_others => false : inherit fds (default for system and exec)
* :close_others => true : don't inherit (default for spawn and IO.popen)
* :close_others => true : don't inherit
*
* If a hash is given as +env+, the environment is
* updated by +env+ before <code>exec(2)</code> in the child process.
......
if (ret == -1) return -1;
if ((fd = open("/dev/tty", O_RDWR)) >= 0) {
rb_update_max_fd(fd);
rb_fd_set_cloexec(fd);
ioctl(fd, TIOCNOTTY, NULL);
close(fd);
}
......
err = chdir("/");
if (!noclose && (n = open("/dev/null", O_RDWR, 0)) != -1) {
rb_update_max_fd(n);
rb_fd_set_cloexec(n);
(void)dup2(n, 0);
(void)dup2(n, 1);
(void)dup2(n, 2);
ext/pty/pty.c (working copy)
{
int i = open("/dev/tty", O_RDONLY);
if (i < 0) ERROR_EXIT("/dev/tty");
rb_update_max_fd(i);
rb_fd_set_cloexec(i);
if (ioctl(i, TIOCNOTTY, (char *)0))
ERROR_EXIT("ioctl(TIOCNOTTY)");
close(i);
......
if (slave < 0) {
ERROR_EXIT("open: pty slave");
}
rb_update_max_fd(slave);
rb_fd_set_cloexec(slave);
close(master);
#endif
dup2(slave,0);
......
sigemptyset(&dfl.sa_mask);
if ((masterfd = posix_openpt(O_RDWR|O_NOCTTY)) == -1) goto error;
rb_update_max_fd(masterfd);
rb_fd_set_cloexec(masterfd);
if (sigaction(SIGCHLD, &dfl, &old) == -1) goto error;
if (grantpt(masterfd) == -1) goto grantpt_error;
if (sigaction(SIGCHLD, &old, NULL) == -1) goto error;
......
if ((slavedevice = ptsname(masterfd)) == NULL) goto error;
if (no_mesg(slavedevice, nomesg) == -1) goto error;
if ((slavefd = open(slavedevice, O_RDWR|O_NOCTTY, 0)) == -1) goto error;
rb_update_max_fd(slavefd);
rb_fd_set_cloexec(slavefd);
#if defined I_PUSH && !defined linux
if (ioctl(slavefd, I_PUSH, "ptem") == -1) goto error;
......
if (!fail) return -1;
rb_raise(rb_eRuntimeError, "openpty() failed");
}
rb_update_max_fd(*master);
rb_update_max_fd(*slave);
rb_fd_set_cloexec(*master);
rb_fd_set_cloexec(*slave);
if (no_mesg(SlaveName, nomesg) == -1) {
if (!fail) return -1;
rb_raise(rb_eRuntimeError, "can't chmod slave pty");
......
if (!fail) return -1;
rb_raise(rb_eRuntimeError, "_getpty() failed");
}
rb_update_max_fd(*master);
rb_fd_set_cloexec(*master);
*slave = open(name, O_RDWR);
/* error check? */
rb_update_max_fd(*slave);
rb_fd_set_cloexec(*slave);
strlcpy(SlaveName, name, DEVICELEN);
return 0;
......
extern int grantpt(int);
if((masterfd = open("/dev/ptmx", O_RDWR, 0)) == -1) goto error;
rb_update_max_fd(masterfd);
rb_fd_set_cloexec(masterfd);
s = signal(SIGCHLD, SIG_DFL);
if(grantpt(masterfd) == -1) goto error;
signal(SIGCHLD, s);
......
if((slavedevice = ptsname(masterfd)) == NULL) goto error;
if (no_mesg(slavedevice, nomesg) == -1) goto error;
if((slavefd = open(slavedevice, O_RDWR, 0)) == -1) goto error;
rb_update_max_fd(slavefd);
rb_fd_set_cloexec(slavefd);
#if defined I_PUSH && !defined linux
if(ioctl(slavefd, I_PUSH, "ptem") == -1) goto error;
if(ioctl(slavefd, I_PUSH, "ldterm") == -1) goto error;
......
for (p = deviceNo; *p != NULL; p++) {
snprintf(MasterName, sizeof MasterName, MasterDevice, *p);
if ((masterfd = open(MasterName,O_RDWR,0)) >= 0) {
rb_update_max_fd(masterfd);
rb_fd_set_cloexec(masterfd);
*master = masterfd;
snprintf(SlaveName, DEVICELEN, SlaveDevice, *p);
if ((slavefd = open(SlaveName,O_RDWR,0)) >= 0) {
rb_update_max_fd(slavefd);
rb_fd_set_cloexec(slavefd);
*slave = slavefd;
if (chown(SlaveName, getuid(), getgid()) != 0) goto error;
if (chmod(SlaveName, nomesg ? 0600 : 0622) != 0) goto error;
......
wfptr->fd = dup(info.fd);
if (wfptr->fd == -1)
rb_sys_fail("dup()");
rb_update_max_fd(wfptr->fd);
rb_fd_set_cloexec(wfptr->fd);
wfptr->pathv = rfptr->pathv;
res = rb_ary_new2(3);
ext/openssl/ossl_bio.c (working copy)
if ((fd = dup(FPTR_TO_FD(fptr))) < 0){
rb_sys_fail(0);
}
rb_update_max_fd(fd);
rb_fd_set_cloexec(fd);
if (!(fp = fdopen(fd, "r"))){
close(fd);
rb_sys_fail(0);
ext/socket/init.c (working copy)
}
}
if (0 <= fd)
rb_update_max_fd(fd);
rb_fd_set_cloexec(fd);
return fd;
}
......
}
rb_sys_fail("accept(2)");
}
rb_update_max_fd(fd2);
rb_fd_set_cloexec(fd2);
make_fd_nonblock(fd2);
return rsock_init_sock(rb_obj_alloc(klass), fd2);
}
......
}
rb_sys_fail(0);
}
rb_update_max_fd(fd2);
rb_fd_set_cloexec(fd2);
if (!klass) return INT2NUM(fd2);
return rsock_init_sock(rb_obj_alloc(klass), fd2);
}
ext/socket/socket.c (working copy)
if (ret < 0) {
rb_sys_fail("socketpair(2)");
}
rb_update_max_fd(sp[0]);
rb_update_max_fd(sp[1]);
rb_fd_set_cloexec(sp[0]);
rb_fd_set_cloexec(sp[1]);
s1 = rsock_init_sock(rb_obj_alloc(klass), sp[0]);
s2 = rsock_init_sock(rb_obj_alloc(klass), sp[1]);
ext/socket/ancdata.c (working copy)
int *end = (int *)((char *)cmh + cmh->cmsg_len);
while ((char *)fdp + sizeof(int) <= (char *)end &&
(char *)fdp + sizeof(int) <= msg_end) {
rb_update_max_fd(*fdp);
rb_fd_set_cloexec(*fdp);
close(*fdp);
fdp++;
}
......
VALUE io;
if (fstat(fd, &stbuf) == -1)
rb_raise(rb_eSocket, "invalid fd in SCM_RIGHTS");
rb_update_max_fd(fd);
rb_fd_set_cloexec(fd);
if (S_ISSOCK(stbuf.st_mode))
io = rsock_init_sock(rb_obj_alloc(rb_cSocket), fd);
else
ext/socket/unixsocket.c (working copy)
#if FD_PASSING_BY_MSG_CONTROL
memcpy(&fd, CMSG_DATA(&cmsg.hdr), sizeof(int));
#endif
rb_update_max_fd(fd);
rb_fd_set_cloexec(fd);
if (klass == Qnil)
return INT2FIX(fd);
ext/io/console/console.c (working copy)
#ifdef CONSOLE_DEVICE_FOR_WRITING
fd = open(CONSOLE_DEVICE_FOR_WRITING, O_WRONLY);
if (fd < 0) return Qnil;
rb_update_max_fd(fd);
rb_fd_set_cloexec(fd);
args[1] = INT2FIX(O_WRONLY);
args[0] = INT2NUM(fd);
out = rb_class_new_instance(2, args, klass);
......
#endif
return Qnil;
}
rb_update_max_fd(fd);
rb_fd_set_cloexec(fd);
args[1] = INT2FIX(O_RDWR);
args[0] = INT2NUM(fd);
con = rb_class_new_instance(2, args, klass);
ruby.c (working copy)
if ((fd = open(fname, mode)) < 0) {
rb_load_fail(fname);
}
rb_update_max_fd(fd);
rb_fd_set_cloexec(fd);
f = rb_io_fdopen(fd, mode, fname);
}
test/ruby/test_io.rb (working copy)
def test_close_on_exec
skip "IO\#close_on_exec is not implemented." unless have_close_on_exec?
ruby do |f|
assert_equal(true, f.close_on_exec?)
f.close_on_exec = false
assert_equal(false, f.close_on_exec?)
f.close_on_exec = true
assert_equal(true, f.close_on_exec?)
......
end
with_pipe do |r, w|
assert_equal(true, r.close_on_exec?)
r.close_on_exec = false
assert_equal(false, r.close_on_exec?)
r.close_on_exec = true
assert_equal(true, r.close_on_exec?)
r.close_on_exec = false
assert_equal(false, r.close_on_exec?)
assert_equal(true, w.close_on_exec?)
w.close_on_exec = false
assert_equal(false, w.close_on_exec?)
w.close_on_exec = true
assert_equal(true, w.close_on_exec?)
test/ruby/test_process.rb (working copy)
def test_fd_inheritance
skip "inheritance of fd other than stdin,stdout and stderr is not supported" if windows?
with_pipe {|r, w|
system(RUBY, '-e', 'IO.new(ARGV[0].to_i, "w").puts(:ba)', w.fileno.to_s)
system(RUBY, '-e', 'IO.new(ARGV[0].to_i, "w").puts(:ba)', w.fileno.to_s, w=>w)
w.close
assert_equal("ba\n", r.read)
}
......
write_file("s", <<-"End")
exec(#{RUBY.dump}, '-e',
'IO.new(ARGV[0].to_i, "w").puts("bu") rescue nil',
#{w.fileno.to_s.dump})
#{w.fileno.to_s.dump}, :close_others=>false)
End
w.close_on_exec = false
Process.wait spawn(RUBY, "s", :close_others=>false)
w.close
assert_equal("bu\n", r.read)
......
File.unlink("err")
}
with_pipe {|r, w|
w.close_on_exec = false
Process.wait spawn(RUBY, '-e', 'IO.new(ARGV[0].to_i, "w").puts("bi")', w.fileno.to_s, :close_others=>false)
w.close
assert_equal("bi\n", r.read)
......
Process.wait
}
with_pipe {|r, w|
w.close_on_exec = false
io = IO.popen([RUBY, "-e", "STDERR.reopen(STDOUT); IO.new(#{w.fileno}, 'w').puts('mo')", :close_others=>false])
w.close
errmsg = io.read
......
Process.wait
}
with_pipe {|r, w|
w.close_on_exec = false
io = IO.popen([RUBY, "-e", "STDERR.reopen(STDOUT); IO.new(#{w.fileno}, 'w').puts('mo')", :close_others=>nil])
w.close
errmsg = io.read
file.c (working copy)
if ((tmpfd = open(StringValueCStr(path), 0)) < 0) {
rb_sys_fail(RSTRING_PTR(path));
}
rb_update_max_fd(tmpfd);
rb_fd_set_cloexec(tmpfd);
if (chsize(tmpfd, pos) < 0) {
close(tmpfd);
rb_sys_fail(RSTRING_PTR(path));
......
int ret = 1;
int fd = open(path, O_RDONLY);
if (fd == -1) return 0;
rb_update_max_fd(fd);
rb_fd_set_cloexec(fd);
#if !defined DOSISH
{
struct stat st;
random.c (working copy)
|O_NOCTTY
#endif
)) >= 0) {
rb_update_max_fd(fd);
rb_fd_set_cloexec(fd);
if (fstat(fd, &statbuf) == 0 && S_ISCHR(statbuf.st_mode)) {
if (read(fd, seed, DEFAULT_SEED_LEN) < DEFAULT_SEED_LEN) {
/* abandon */;
    (1-1/1)