diff options
| author | ksss <[email protected]> | 2014-12-04 09:05:24 +0900 |
|---|---|---|
| committer | ksss <[email protected]> | 2014-12-04 10:25:03 +0900 |
| commit | 9439b981d816a25883bd9d8f05af62664f21e0bc (patch) | |
| tree | 7e021a0e7c9b55a69c352bc13dd972cafd6bc4e3 | |
| parent | d1fee5892adb23565fddd9f8e762cc4ca8fd8cb3 (diff) | |
| download | mruby-9439b981d816a25883bd9d8f05af62664f21e0bc.tar.gz mruby-9439b981d816a25883bd9d8f05af62664f21e0bc.zip | |
Opened fd should be set FD_CLOEXEC by default
| -rw-r--r-- | src/io.c | 79 | ||||
| -rw-r--r-- | test/io.rb | 16 |
2 files changed, 68 insertions, 27 deletions
@@ -125,6 +125,30 @@ mrb_io_flags_to_modenum(mrb_state *mrb, int flags) return modenum; } +void +mrb_fd_cloexec(mrb_state *mrb, int fd) +{ + int flags, flags2; + +#if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC) + flags = fcntl(fd, F_GETFD); + if (flags == -1) { + mrb_sys_fail(mrb, "fcntl"); + } + if (fd <= 2) { + flags2 = flags & ~FD_CLOEXEC; /* Clear CLOEXEC for standard file descriptors: 0, 1, 2. */ + } + else { + flags2 = flags | FD_CLOEXEC; /* Set CLOEXEC for non-standard file descriptors: 3, 4, 5, ... */ + } + if (flags != flags2) { + if (fcntl(fd, F_SETFD, flags2) == -1) { + mrb_sys_fail(mrb, "fcntl"); + } + } +#endif +} + #ifndef _WIN32 static int mrb_proc_exec(const char *pname) @@ -367,6 +391,38 @@ mrb_io_s_sysclose(mrb_state *mrb, mrb_value klass) return mrb_fixnum_value(0); } +int +mrb_cloexec_open(mrb_state *mrb, const char *pathname, mrb_int flags, mrb_int mode) +{ + int fd, retry = FALSE; + +#ifdef O_CLOEXEC + /* O_CLOEXEC is available since Linux 2.6.23. Linux 2.6.18 silently ignore it. */ + flags |= O_CLOEXEC; +#elif defined O_NOINHERIT + flags |= O_NOINHERIT; +#endif +reopen: + fd = open(pathname, flags, mode); + if (fd == -1) { + if (!retry) { + switch (errno) { + case ENFILE: + case EMFILE: + mrb_garbage_collect(mrb); + retry = TRUE; + goto reopen; + } + } + mrb_sys_fail(mrb, "open"); + } + + if (fd <= 2) { + mrb_fd_cloexec(mrb, fd); + } + return fd; +} + mrb_value mrb_io_s_sysopen(mrb_state *mrb, mrb_value klass) { @@ -374,7 +430,7 @@ mrb_io_s_sysopen(mrb_state *mrb, mrb_value klass) mrb_value mode = mrb_nil_value(); mrb_int fd, flags, perm = -1; const char *pat; - int modenum, retry = FALSE; + int modenum; mrb_get_args(mrb, "S|Si", &path, &mode, &perm); if (mrb_nil_p(mode)) { @@ -387,22 +443,7 @@ mrb_io_s_sysopen(mrb_state *mrb, mrb_value klass) pat = mrb_string_value_cstr(mrb, &path); flags = mrb_io_modestr_to_flags(mrb, mrb_string_value_cstr(mrb, &mode)); modenum = mrb_io_flags_to_modenum(mrb, flags); - - reopen: - fd = open(pat, modenum, perm); - if (fd == -1) { - if (!retry) { - switch (errno) { - case ENFILE: - case EMFILE: - mrb_garbage_collect(mrb); - retry = TRUE; - goto reopen; - } - } - mrb_sys_fail(mrb, pat); - } - + fd = mrb_cloexec_open(mrb, pat, modenum, perm); return mrb_fixnum_value(fd); } @@ -745,7 +786,7 @@ mrb_io_close_on_exec_p(mrb_state *mrb, mrb_value io) #if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC) struct mrb_io *fptr; int ret; - + fptr = (struct mrb_io *)mrb_get_datatype(mrb, io, &mrb_io_type); if (fptr->fd < 0) { mrb_raise(mrb, E_IO_ERROR, "closed stream"); @@ -787,7 +828,7 @@ mrb_io_set_close_on_exec(mrb_state *mrb, mrb_value io) if ((ret & FD_CLOEXEC) != flag) { ret = (ret & ~FD_CLOEXEC) | flag; ret = fcntl(fptr->fd2, F_SETFD, ret); - + if (ret == -1) mrb_sys_fail(mrb, "F_SETFD failed"); } } diff --git a/test/io.rb b/test/io.rb index 006c7cc8d..9c7ce741f 100644 --- a/test/io.rb +++ b/test/io.rb @@ -362,28 +362,28 @@ assert('IO#fileno') do io.closed? end -assert('IO#close_on_exec') do +assert('IO#close_on_exec') do fd = IO.sysopen $mrbtest_io_wfname, "w" io = IO.new fd, "w" - begin - # IO.sysopen opens a file descripter without O_CLOEXEC flag. - assert_equal(false, io.close_on_exec?) + begin + # IO.sysopen opens a file descripter with O_CLOEXEC flag. + assert_true io.close_on_exec? rescue ScriptError skip "IO\#close_on_exec is not implemented." end - io.close_on_exec = true - assert_equal(true, io.close_on_exec?) io.close_on_exec = false assert_equal(false, io.close_on_exec?) io.close_on_exec = true assert_equal(true, io.close_on_exec?) - + io.close_on_exec = false + assert_equal(false, io.close_on_exec?) + io.close io.closed? # # Use below when IO.pipe is implemented. - # begin + # begin # r, w = IO.pipe # assert_equal(false, r.close_on_exec?) # r.close_on_exec = true |
