зеркало из https://github.com/github/ruby.git
* include/ruby/intern.h (rb_env_clear): declared.
(rb_io_mode_modenum): declared. (rb_close_before_exec): declared. (struct rb_exec_arg): add options and redirect_fds field. (rb_check_argv): removed. (rb_exec_initarg): declared. (rb_exec_getargs): declared. (rb_exec_initarg2): declared. (rb_fork): add third argument: fds. * io.c (max_file_descriptor): new static variable to record maximum file descriptor ruby used. (UPDATE_MAXFD): new macro. (UPDATE_MAXFD_PIPE): new macro. (rb_io_mode_modenum): externed. (rb_sysopen): update max_file_descriptor. (rb_close_before_exec): new function. (popen_exec): redirection removed because it is done by extended spawn mechanism. (pipe_open): generate a hash for spawn options to specify redirections. (pipe_open_v): use rb_exec_getargs. (pipe_open_s): use rb_exec_getargs. (rb_io_initialize): update max_file_descriptor.. * process.c (hide_obj): new function. (check_exec_redirect_fd): new function. (check_exec_redirect): new function. (check_exec_options_i): new function. (check_exec_fds): new function. (rb_check_exec_options): new function. (check_exec_env_i): new function. (rb_check_exec_env): new function. (rb_exec_getargs): new function. (rb_exec_initarg2): new function. (rb_exec_initarg): new function. (rb_f_exec): use rb_exec_initarg. (intcmp): new function. (run_exec_dup2): new function. (run_exec_close): new function. (run_exec_open): new function. (run_exec_pgroup): new function. (run_exec_rlimit): new function. (run_exec_options): new function. (rb_exec): call run_exec_options. (move_fds_to_avoid_crash): new function. (pipe_nocrash): new function. (rb_fork): use pipe_nocrash to avoid file descriptor conflicts. (rb_spawn): use rb_exec_initarg. (rlimit_resource_name2int): extracted from rlimit_resource_type. (rlimit_type_by_hname): new function. (rlimit_type_by_lname): new function. (rlimit_resource_type): use rlimit_type_by_hname. (proc_daemon): add fds argument for rb_fork. * hash.c (rb_env_clear): renamed from env_clear and externed. [ruby-dev:34086] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@16183 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
Родитель
9b694aaa85
Коммит
278b63a3e4
61
ChangeLog
61
ChangeLog
|
@ -1,3 +1,64 @@
|
|||
Thu Apr 24 23:25:17 2008 Tanaka Akira <akr@fsij.org>
|
||||
|
||||
* include/ruby/intern.h (rb_env_clear): declared.
|
||||
(rb_io_mode_modenum): declared.
|
||||
(rb_close_before_exec): declared.
|
||||
(struct rb_exec_arg): add options and redirect_fds field.
|
||||
(rb_check_argv): removed.
|
||||
(rb_exec_initarg): declared.
|
||||
(rb_exec_getargs): declared.
|
||||
(rb_exec_initarg2): declared.
|
||||
(rb_fork): add third argument: fds.
|
||||
|
||||
* io.c (max_file_descriptor): new static variable to record maximum
|
||||
file descriptor ruby used.
|
||||
(UPDATE_MAXFD): new macro.
|
||||
(UPDATE_MAXFD_PIPE): new macro.
|
||||
(rb_io_mode_modenum): externed.
|
||||
(rb_sysopen): update max_file_descriptor.
|
||||
(rb_close_before_exec): new function.
|
||||
(popen_exec): redirection removed because it is done by extended
|
||||
spawn mechanism.
|
||||
(pipe_open): generate a hash for spawn options to specify
|
||||
redirections.
|
||||
(pipe_open_v): use rb_exec_getargs.
|
||||
(pipe_open_s): use rb_exec_getargs.
|
||||
(rb_io_initialize): update max_file_descriptor..
|
||||
|
||||
* process.c (hide_obj): new function.
|
||||
(check_exec_redirect_fd): new function.
|
||||
(check_exec_redirect): new function.
|
||||
(check_exec_options_i): new function.
|
||||
(check_exec_fds): new function.
|
||||
(rb_check_exec_options): new function.
|
||||
(check_exec_env_i): new function.
|
||||
(rb_check_exec_env): new function.
|
||||
(rb_exec_getargs): new function.
|
||||
(rb_exec_initarg2): new function.
|
||||
(rb_exec_initarg): new function.
|
||||
(rb_f_exec): use rb_exec_initarg.
|
||||
(intcmp): new function.
|
||||
(run_exec_dup2): new function.
|
||||
(run_exec_close): new function.
|
||||
(run_exec_open): new function.
|
||||
(run_exec_pgroup): new function.
|
||||
(run_exec_rlimit): new function.
|
||||
(run_exec_options): new function.
|
||||
(rb_exec): call run_exec_options.
|
||||
(move_fds_to_avoid_crash): new function.
|
||||
(pipe_nocrash): new function.
|
||||
(rb_fork): use pipe_nocrash to avoid file descriptor conflicts.
|
||||
(rb_spawn): use rb_exec_initarg.
|
||||
(rlimit_resource_name2int): extracted from rlimit_resource_type.
|
||||
(rlimit_type_by_hname): new function.
|
||||
(rlimit_type_by_lname): new function.
|
||||
(rlimit_resource_type): use rlimit_type_by_hname.
|
||||
(proc_daemon): add fds argument for rb_fork.
|
||||
|
||||
* hash.c (rb_env_clear): renamed from env_clear and externed.
|
||||
|
||||
[ruby-dev:34086]
|
||||
|
||||
Thu Apr 24 23:00:58 2008 Yusuke Endoh <mame@tsg.ne.jp>
|
||||
|
||||
* test/ruby/test_thread.rb: fix typos.
|
||||
|
|
6
hash.c
6
hash.c
|
@ -2215,8 +2215,8 @@ env_select(VALUE ehash)
|
|||
return result;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
env_clear(void)
|
||||
VALUE
|
||||
rb_env_clear(void)
|
||||
{
|
||||
volatile VALUE keys;
|
||||
long i;
|
||||
|
@ -2640,7 +2640,7 @@ Init_Hash(void)
|
|||
rb_define_singleton_method(envtbl,"each_value", env_each_value, 0);
|
||||
rb_define_singleton_method(envtbl,"delete", env_delete_m, 1);
|
||||
rb_define_singleton_method(envtbl,"delete_if", env_delete_if, 0);
|
||||
rb_define_singleton_method(envtbl,"clear", env_clear, 0);
|
||||
rb_define_singleton_method(envtbl,"clear", rb_env_clear, 0);
|
||||
rb_define_singleton_method(envtbl,"reject", env_reject, 0);
|
||||
rb_define_singleton_method(envtbl,"reject!", env_reject_bang, 0);
|
||||
rb_define_singleton_method(envtbl,"select", env_select, 0);
|
||||
|
|
|
@ -353,6 +353,7 @@ VALUE rb_hash_delete(VALUE,VALUE);
|
|||
struct st_table *rb_hash_tbl(VALUE);
|
||||
int rb_path_check(const char*);
|
||||
int rb_env_path_tainted(void);
|
||||
VALUE rb_env_clear(void);
|
||||
/* io.c */
|
||||
#define rb_defout rb_stdout
|
||||
RUBY_EXTERN VALUE rb_fs;
|
||||
|
@ -377,6 +378,8 @@ VALUE rb_file_open(const char*, const char*);
|
|||
VALUE rb_gets(void);
|
||||
void rb_write_error(const char*);
|
||||
void rb_write_error2(const char*, long);
|
||||
int rb_io_mode_modenum(const char *mode);
|
||||
void rb_close_before_exec(int lowfd, int maxhint, VALUE noclose_fds);
|
||||
/* marshal.c */
|
||||
VALUE rb_marshal_dump(VALUE, VALUE);
|
||||
VALUE rb_marshal_load(VALUE);
|
||||
|
@ -444,12 +447,16 @@ struct rb_exec_arg {
|
|||
int argc;
|
||||
VALUE *argv;
|
||||
const char *prog;
|
||||
VALUE options;
|
||||
VALUE redirect_fds;
|
||||
};
|
||||
int rb_proc_exec_n(int, VALUE*, const char*);
|
||||
int rb_proc_exec(const char*);
|
||||
VALUE rb_check_argv(int, VALUE*);
|
||||
VALUE rb_exec_initarg(int argc, VALUE *argv, int accept_shell, struct rb_exec_arg *e);
|
||||
VALUE rb_exec_getargs(int *argc_p, VALUE **argv_p, int accept_shell, VALUE *env_ret, VALUE *opthash_ret);
|
||||
void rb_exec_initarg2(VALUE prog, int argc, VALUE *argv, VALUE env, VALUE opthash, struct rb_exec_arg *e);
|
||||
int rb_exec(const struct rb_exec_arg*);
|
||||
rb_pid_t rb_fork(int*, int (*)(void*), void*);
|
||||
rb_pid_t rb_fork(int*, int (*)(void*), void*, VALUE);
|
||||
VALUE rb_f_exec(int,VALUE*);
|
||||
rb_pid_t rb_waitpid(rb_pid_t pid, int *status, int flags);
|
||||
void rb_syswait(rb_pid_t pid);
|
||||
|
|
116
io.c
116
io.c
|
@ -139,6 +139,18 @@ struct argf {
|
|||
rb_encoding *enc, *enc2;
|
||||
};
|
||||
|
||||
static int max_file_descriptor = NOFILE;
|
||||
#define UPDATE_MAXFD(fd) \
|
||||
do { \
|
||||
if (max_file_descriptor < (fd)) max_file_descriptor = (fd); \
|
||||
} while (0)
|
||||
#define UPDATE_MAXFD_PIPE(filedes) \
|
||||
do { \
|
||||
UPDATE_MAXFD((filedes)[0]); \
|
||||
UPDATE_MAXFD((filedes)[1]); \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define argf_of(obj) (*(struct argf *)DATA_PTR(obj))
|
||||
#define ARGF argf_of(argf)
|
||||
|
||||
|
@ -3228,7 +3240,7 @@ rb_io_modenum_flags(int mode)
|
|||
return flags;
|
||||
}
|
||||
|
||||
static int
|
||||
int
|
||||
rb_io_mode_modenum(const char *mode)
|
||||
{
|
||||
int flags = 0;
|
||||
|
@ -3387,6 +3399,7 @@ rb_sysopen(char *fname, int flags, unsigned int mode)
|
|||
rb_sys_fail(fname);
|
||||
}
|
||||
}
|
||||
UPDATE_MAXFD(fd);
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
@ -3629,26 +3642,41 @@ popen_redirect(struct popen_arg *p)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
rb_close_before_exec(int lowfd, int maxhint, VALUE noclose_fds)
|
||||
{
|
||||
int fd, ret;
|
||||
int max = max_file_descriptor;
|
||||
if (max < maxhint)
|
||||
max = maxhint;
|
||||
for (fd = lowfd; fd <= max; fd++) {
|
||||
if (!NIL_P(noclose_fds) &&
|
||||
RTEST(rb_hash_lookup(noclose_fds, INT2FIX(fd))))
|
||||
continue;
|
||||
#ifdef FD_CLOEXEC
|
||||
ret = fcntl(fd, F_GETFD);
|
||||
if (ret != -1 && !(ret & FD_CLOEXEC)) {
|
||||
fcntl(fd, F_SETFD, ret|FD_CLOEXEC);
|
||||
}
|
||||
#else
|
||||
close(fd);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
popen_exec(void *pp)
|
||||
{
|
||||
struct popen_arg *p = (struct popen_arg*)pp;
|
||||
int fd;
|
||||
|
||||
rb_thread_atfork();
|
||||
popen_redirect(p);
|
||||
for (fd = 3; fd < NOFILE; fd++) {
|
||||
#ifdef FD_CLOEXEC
|
||||
if (fcntl(fd, F_GETFD) & FD_CLOEXEC) continue;
|
||||
#endif
|
||||
close(fd);
|
||||
}
|
||||
return rb_exec(&p->exec);
|
||||
}
|
||||
#endif
|
||||
|
||||
static VALUE
|
||||
pipe_open(const char *cmd, int argc, VALUE *argv, const char *mode)
|
||||
pipe_open(VALUE prog, int argc, VALUE *argv,
|
||||
VALUE env, VALUE opthash, const char *mode)
|
||||
{
|
||||
int modef = rb_io_mode_flags(mode);
|
||||
int pid = 0;
|
||||
|
@ -3659,6 +3687,7 @@ pipe_open(const char *cmd, int argc, VALUE *argv, const char *mode)
|
|||
#if defined(HAVE_FORK)
|
||||
int status;
|
||||
struct popen_arg arg;
|
||||
VALUE env2 = 0;
|
||||
#elif defined(_WIN32)
|
||||
int openmode = rb_io_mode_modenum(mode);
|
||||
const char *exename = NULL;
|
||||
|
@ -3667,8 +3696,16 @@ pipe_open(const char *cmd, int argc, VALUE *argv, const char *mode)
|
|||
FILE *fp = 0;
|
||||
int fd = -1;
|
||||
int write_fd = -1;
|
||||
const char *cmd = 0;
|
||||
|
||||
if (prog)
|
||||
cmd = StringValueCStr(prog);
|
||||
|
||||
#if defined(HAVE_FORK)
|
||||
if (prog) {
|
||||
env2 = rb_hash_new();
|
||||
RBASIC(env2)->klass = 0;
|
||||
}
|
||||
arg.modef = modef;
|
||||
arg.pair[0] = arg.pair[1] = -1;
|
||||
arg.write_pair[0] = arg.write_pair[1] = -1;
|
||||
|
@ -3676,6 +3713,7 @@ pipe_open(const char *cmd, int argc, VALUE *argv, const char *mode)
|
|||
case FMODE_READABLE|FMODE_WRITABLE:
|
||||
if (pipe(arg.write_pair) < 0)
|
||||
rb_sys_fail(cmd);
|
||||
UPDATE_MAXFD_PIPE(arg.write_pair);
|
||||
if (pipe(arg.pair) < 0) {
|
||||
int e = errno;
|
||||
close(arg.write_pair[0]);
|
||||
|
@ -3683,29 +3721,47 @@ pipe_open(const char *cmd, int argc, VALUE *argv, const char *mode)
|
|||
errno = e;
|
||||
rb_sys_fail(cmd);
|
||||
}
|
||||
UPDATE_MAXFD_PIPE(arg.pair);
|
||||
if (env2) {
|
||||
rb_hash_aset(env2, INT2FIX(0), INT2FIX(arg.write_pair[0]));
|
||||
rb_hash_aset(env2, INT2FIX(1), INT2FIX(arg.pair[1]));
|
||||
}
|
||||
break;
|
||||
case FMODE_READABLE:
|
||||
if (pipe(arg.pair) < 0)
|
||||
rb_sys_fail(cmd);
|
||||
UPDATE_MAXFD_PIPE(arg.pair);
|
||||
if (env2)
|
||||
rb_hash_aset(env2, INT2FIX(1), INT2FIX(arg.pair[1]));
|
||||
break;
|
||||
case FMODE_WRITABLE:
|
||||
if (pipe(arg.pair) < 0)
|
||||
rb_sys_fail(cmd);
|
||||
UPDATE_MAXFD_PIPE(arg.pair);
|
||||
if (env2)
|
||||
rb_hash_aset(env2, INT2FIX(0), INT2FIX(arg.pair[0]));
|
||||
break;
|
||||
default:
|
||||
rb_sys_fail(cmd);
|
||||
}
|
||||
if (cmd) {
|
||||
arg.exec.argc = argc;
|
||||
arg.exec.argv = argv;
|
||||
arg.exec.prog = cmd;
|
||||
pid = rb_fork(&status, popen_exec, &arg);
|
||||
if (prog) {
|
||||
VALUE close_others = ID2SYM(rb_intern("close_others"));
|
||||
if (NIL_P(opthash) || !st_lookup(RHASH_TBL(opthash), close_others, 0))
|
||||
rb_hash_aset(env2, close_others, Qtrue);
|
||||
if (NIL_P(opthash))
|
||||
opthash = env2;
|
||||
else {
|
||||
opthash = rb_assoc_new(env2, opthash);
|
||||
RBASIC(opthash)->klass = 0;
|
||||
}
|
||||
rb_exec_initarg2(prog, argc, argv, env, opthash, &arg.exec);
|
||||
pid = rb_fork(&status, popen_exec, &arg, arg.exec.redirect_fds);
|
||||
}
|
||||
else {
|
||||
fflush(stdin); /* is it really needed? */
|
||||
rb_io_flush(rb_stdout);
|
||||
rb_io_flush(rb_stderr);
|
||||
pid = rb_fork(&status, 0, 0);
|
||||
pid = rb_fork(&status, 0, 0, Qnil);
|
||||
if (pid == 0) { /* child */
|
||||
popen_redirect(&arg);
|
||||
rb_io_synchronized(RFILE(orig_stdout)->fptr);
|
||||
|
@ -3812,27 +3868,29 @@ pipe_open(const char *cmd, int argc, VALUE *argv, const char *mode)
|
|||
static VALUE
|
||||
pipe_open_v(int argc, VALUE *argv, const char *mode)
|
||||
{
|
||||
VALUE prog = rb_check_argv(argc, argv);
|
||||
const char *cmd;
|
||||
|
||||
if (!RB_GC_GUARD(prog)) prog = argv[0];
|
||||
cmd = StringValueCStr(prog);
|
||||
return pipe_open(cmd, argc, argv, mode);
|
||||
VALUE prog, env=Qnil, opthash=Qnil;
|
||||
prog = rb_exec_getargs(&argc, &argv, Qfalse, &env, &opthash);
|
||||
return pipe_open(prog, argc, argv, env, opthash, mode);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
pipe_open_s(VALUE prog, const char *mode)
|
||||
{
|
||||
const char *cmd = (rb_check_argv(1, &prog), RSTRING_PTR(prog));
|
||||
const char *cmd = RSTRING_PTR(prog);
|
||||
int argc = 1;
|
||||
VALUE *argv = &prog;
|
||||
VALUE env=Qnil, opthash=Qnil;
|
||||
|
||||
if (strcmp("-", cmd) == 0) {
|
||||
if (RSTRING_LEN(prog) == 1 && cmd[0] == '-') {
|
||||
#if !defined(HAVE_FORK)
|
||||
rb_raise(rb_eNotImpError,
|
||||
"fork() function is unimplemented on this machine");
|
||||
#endif
|
||||
cmd = 0;
|
||||
return pipe_open(0, 0, 0, Qnil, Qnil, mode);
|
||||
}
|
||||
return pipe_open(cmd, 0, &prog, mode);
|
||||
|
||||
rb_exec_getargs(&argc, &argv, Qtrue, &env, &opthash);
|
||||
return pipe_open(prog, 0, 0, Qnil, Qnil, mode);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -3845,7 +3903,9 @@ pipe_open_s(VALUE prog, const char *mode)
|
|||
* <code>IO</code> object. If _cmd_ is a +String+
|
||||
* ``<code>-</code>'', then a new instance of Ruby is started as the
|
||||
* subprocess. If <i>cmd</i> is an +Array+ of +String+, then it will
|
||||
* be used as the subprocess's +argv+ bypassing a shell. The default
|
||||
* be used as the subprocess's +argv+ bypassing a shell.
|
||||
* The array can contains a hash at first for environments and
|
||||
* a hash at last for options similar to <code>spawn</code>. The default
|
||||
* mode for the new file object is ``r'', but <i>mode</i> may be set
|
||||
* to any of the modes listed in the description for class IO.
|
||||
*
|
||||
|
@ -4873,6 +4933,7 @@ rb_io_initialize(int argc, VALUE *argv, VALUE io)
|
|||
orig = rb_io_check_io(fnum);
|
||||
if (NIL_P(orig)) {
|
||||
fd = NUM2INT(fnum);
|
||||
UPDATE_MAXFD(fd);
|
||||
if (argc != 2) {
|
||||
#if defined(HAVE_FCNTL) && defined(F_GETFL)
|
||||
flags = fcntl(fd, F_GETFL);
|
||||
|
@ -6021,6 +6082,7 @@ rb_io_s_pipe(int argc, VALUE *argv, VALUE klass)
|
|||
rb_scan_args(argc, argv, "02", &v1, &v2);
|
||||
if (pipe(pipes) == -1)
|
||||
rb_sys_fail(0);
|
||||
UPDATE_MAXFD_PIPE(pipes);
|
||||
|
||||
args[0] = klass;
|
||||
args[1] = INT2NUM(pipes[0]);
|
||||
|
|
1106
process.c
1106
process.c
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,4 +1,7 @@
|
|||
require 'test/unit'
|
||||
require 'tmpdir'
|
||||
require 'require_relative'
|
||||
require_relative 'envutil'
|
||||
|
||||
class TestProcess < Test::Unit::TestCase
|
||||
def test_rlimit_availability
|
||||
|
@ -74,4 +77,431 @@ class TestProcess < Test::Unit::TestCase
|
|||
assert_raise(Errno::EPERM) { Process.setrlimit(:NOFILE, :INFINITY) }
|
||||
assert_raise(Errno::EPERM) { Process.setrlimit(:NOFILE, "INFINITY") }
|
||||
end
|
||||
|
||||
def with_tmpchdir
|
||||
Dir.mktmpdir {|d|
|
||||
Dir.chdir(d) {
|
||||
yield d
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def test_execopts_opts
|
||||
assert_nothing_raised {
|
||||
Process.wait Process.spawn("true", {})
|
||||
}
|
||||
assert_raise(ArgumentError) {
|
||||
Process.wait Process.spawn("true", :foo => 100)
|
||||
}
|
||||
assert_raise(ArgumentError) {
|
||||
Process.wait Process.spawn("true", Process => 100)
|
||||
}
|
||||
end
|
||||
|
||||
def test_execopts_pgroup
|
||||
ruby = EnvUtil.rubybin
|
||||
assert_nothing_raised { system("true", :pgroup=>false) }
|
||||
|
||||
io = IO.popen([ruby, "-e", "print Process.getpgrp"])
|
||||
assert_equal(Process.getpgrp.to_s, io.read)
|
||||
io.close
|
||||
|
||||
io = IO.popen([ruby, "-e", "print Process.getpgrp", :pgroup=>true])
|
||||
assert_equal(io.pid.to_s, io.read)
|
||||
io.close
|
||||
|
||||
assert_raise(ArgumentError) { system("true", :pgroup=>-1) }
|
||||
assert_raise(Errno::EPERM) { Process.wait spawn("true", :pgroup=>1) }
|
||||
|
||||
io1 = IO.popen([ruby, "-e", "print Process.getpgrp", :pgroup=>true])
|
||||
io2 = IO.popen([ruby, "-e", "print Process.getpgrp", :pgroup=>io1.pid])
|
||||
assert_equal(io1.pid.to_s, io1.read)
|
||||
assert_equal(io1.pid.to_s, io2.read)
|
||||
Process.wait io1.pid
|
||||
Process.wait io2.pid
|
||||
io1.close
|
||||
io2.close
|
||||
|
||||
end
|
||||
|
||||
def test_execopts_rlimit
|
||||
return unless rlimit_exist?
|
||||
assert_raise(ArgumentError) { system("true", :rlimit_foo=>0) }
|
||||
assert_raise(ArgumentError) { system("true", :rlimit_NOFILE=>0) }
|
||||
assert_raise(ArgumentError) { system("true", :rlimit_nofile=>[]) }
|
||||
assert_raise(ArgumentError) { system("true", :rlimit_nofile=>[1,2,3]) }
|
||||
|
||||
max = Process.getrlimit(:CORE).last
|
||||
|
||||
n = max
|
||||
IO.popen([EnvUtil.rubybin, "-e",
|
||||
"p Process.getrlimit(:CORE)", :rlimit_core=>n]) {|io|
|
||||
assert_equal("[#{n}, #{n}]\n", io.read)
|
||||
}
|
||||
|
||||
n = 0
|
||||
IO.popen([EnvUtil.rubybin, "-e",
|
||||
"p Process.getrlimit(:CORE)", :rlimit_core=>n]) {|io|
|
||||
assert_equal("[#{n}, #{n}]\n", io.read)
|
||||
}
|
||||
|
||||
n = max
|
||||
IO.popen([EnvUtil.rubybin, "-e",
|
||||
"p Process.getrlimit(:CORE)", :rlimit_core=>[n]]) {|io|
|
||||
assert_equal("[#{n}, #{n}]", io.read.chomp)
|
||||
}
|
||||
|
||||
m, n = 0, max
|
||||
IO.popen([EnvUtil.rubybin, "-e",
|
||||
"p Process.getrlimit(:CORE)", :rlimit_core=>[m,n]]) {|io|
|
||||
assert_equal("[#{m}, #{n}]", io.read.chomp)
|
||||
}
|
||||
|
||||
m, n = 0, 0
|
||||
IO.popen([EnvUtil.rubybin, "-e",
|
||||
"p Process.getrlimit(:CORE)", :rlimit_core=>[m,n]]) {|io|
|
||||
assert_equal("[#{m}, #{n}]", io.read.chomp)
|
||||
}
|
||||
|
||||
n = max
|
||||
IO.popen([EnvUtil.rubybin, "-e",
|
||||
"p Process.getrlimit(:CORE), Process.getrlimit(:CPU)",
|
||||
:rlimit_core=>n, :rlimit_cpu=>3600]) {|io|
|
||||
assert_equal("[#{n}, #{n}]\n[3600, 3600]", io.read.chomp)
|
||||
}
|
||||
end
|
||||
|
||||
def test_execopts_env
|
||||
assert_raise(ArgumentError) {
|
||||
system({"F=O"=>"BAR"}, "env")
|
||||
}
|
||||
|
||||
h = {}
|
||||
ENV.each {|k,v| h[k] = nil unless k == "PATH" }
|
||||
IO.popen([h, "env"]) {|io|
|
||||
assert_equal(1, io.readlines.length)
|
||||
}
|
||||
|
||||
IO.popen([{"FOO"=>"BAR"}, "env"]) {|io|
|
||||
assert_match(/FOO=BAR/, io.read)
|
||||
}
|
||||
|
||||
with_tmpchdir {|d|
|
||||
system({"fofo"=>"haha"}, "env", STDOUT=>"out")
|
||||
assert_match(/fofo=haha/, File.read("out").chomp)
|
||||
}
|
||||
end
|
||||
|
||||
def test_execopts_unsetenv_others
|
||||
IO.popen(["/usr/bin/env", :unsetenv_others=>true]) {|io|
|
||||
assert_equal("", io.read)
|
||||
}
|
||||
IO.popen([{"A"=>"B"}, "/usr/bin/env", :unsetenv_others=>true]) {|io|
|
||||
assert_equal("A=B\n", io.read)
|
||||
}
|
||||
end
|
||||
|
||||
def test_execopts_chdir
|
||||
with_tmpchdir {|d|
|
||||
Process.wait Process.spawn("pwd > dir", :chdir => d)
|
||||
assert_equal(d, File.read("#{d}/dir").chomp)
|
||||
assert_raise(Errno::ENOENT) {
|
||||
Process.wait Process.spawn("true", :chdir => "d/notexist")
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def test_execopts_umask
|
||||
with_tmpchdir {|d|
|
||||
n = "#{d}/mask"
|
||||
Process.wait Process.spawn("sh -c umask > #{n}", :umask => 0)
|
||||
assert_equal("0000", File.read(n).chomp)
|
||||
Process.wait Process.spawn("sh -c umask > #{n}", :umask => 0777)
|
||||
assert_equal("0777", File.read(n).chomp)
|
||||
}
|
||||
end
|
||||
|
||||
def with_pipe
|
||||
begin
|
||||
r, w = IO.pipe
|
||||
yield r, w
|
||||
ensure
|
||||
r.close unless r.closed?
|
||||
w.close unless w.closed?
|
||||
end
|
||||
end
|
||||
|
||||
def with_pipes(n)
|
||||
ary = []
|
||||
begin
|
||||
n.times {
|
||||
ary << IO.pipe
|
||||
}
|
||||
yield ary
|
||||
ensure
|
||||
ary.each {|r, w|
|
||||
r.close unless r.closed?
|
||||
w.close unless w.closed?
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def test_execopts_redirect
|
||||
with_tmpchdir {|d|
|
||||
Process.wait Process.spawn("echo a", STDOUT=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644])
|
||||
assert_equal("a", File.read("out").chomp)
|
||||
Process.wait Process.spawn("echo 0", STDOUT=>["out", File::WRONLY|File::CREAT|File::APPEND, 0644])
|
||||
assert_equal("a\n0\n", File.read("out"))
|
||||
Process.wait Process.spawn("sort", STDIN=>["out", File::RDONLY, 0644],
|
||||
STDOUT=>["out2", File::WRONLY|File::CREAT|File::TRUNC, 0644])
|
||||
assert_equal("0\na\n", File.read("out2"))
|
||||
Process.wait Process.spawn("echo b", [STDOUT, STDERR]=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644])
|
||||
assert_equal("b", File.read("out").chomp)
|
||||
Process.wait Process.spawn("echo a", STDOUT=>:close, STDERR=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644])
|
||||
#p File.read("out")
|
||||
assert(!File.read("out").empty?) # error message such as "echo: write error: Bad file descriptor\n".
|
||||
Process.wait Process.spawn("echo c", STDERR=>STDOUT, STDOUT=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644])
|
||||
assert_equal("c", File.read("out").chomp)
|
||||
File.open("out", "w") {|f|
|
||||
Process.wait Process.spawn("echo d", f=>STDOUT, STDOUT=>f)
|
||||
assert_equal("d", File.read("out").chomp)
|
||||
}
|
||||
Process.wait Process.spawn("echo e", STDOUT=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644],
|
||||
3=>STDOUT, 4=>STDOUT, 5=>STDOUT, 6=>STDOUT, 7=>STDOUT)
|
||||
assert_equal("e", File.read("out").chomp)
|
||||
File.open("out", "w") {|f|
|
||||
h = {STDOUT=>f, f=>STDOUT}
|
||||
3.upto(30) {|i| h[i] = STDOUT if f.fileno != i }
|
||||
Process.wait Process.spawn("echo f", h)
|
||||
assert_equal("f", File.read("out").chomp)
|
||||
}
|
||||
assert_raise(ArgumentError) {
|
||||
Process.wait Process.spawn("echo f", 1=>Process)
|
||||
}
|
||||
assert_raise(ArgumentError) {
|
||||
Process.wait Process.spawn("echo f", [Process]=>1)
|
||||
}
|
||||
assert_raise(ArgumentError) {
|
||||
Process.wait Process.spawn("echo f", [1, STDOUT]=>2)
|
||||
}
|
||||
assert_raise(ArgumentError) {
|
||||
Process.wait Process.spawn("echo f", -1=>2)
|
||||
}
|
||||
Process.wait Process.spawn("echo hhh; echo ggg", STDOUT=>"out")
|
||||
assert_equal("hhh\nggg\n", File.read("out"))
|
||||
Process.wait Process.spawn("sort", STDIN=>"out", STDOUT=>"out2")
|
||||
assert_equal("ggg\nhhh\n", File.read("out2"))
|
||||
|
||||
assert_raise(Errno::ENOENT) {
|
||||
Process.wait Process.spawn("non-existing-command", (3..100).to_a=>["err", File::WRONLY|File::CREAT])
|
||||
}
|
||||
assert_equal("", File.read("err"))
|
||||
|
||||
system("echo bb; echo aa", STDOUT=>["out", "w"])
|
||||
assert_equal("bb\naa\n", File.read("out"))
|
||||
system("sort", STDIN=>["out"], STDOUT=>"out2")
|
||||
assert_equal("aa\nbb\n", File.read("out2"))
|
||||
|
||||
with_pipe {|r1, w1|
|
||||
with_pipe {|r2, w2|
|
||||
pid = spawn("sort", STDIN=>r1, STDOUT=>w2, w1=>:close, r2=>:close)
|
||||
r1.close
|
||||
w2.close
|
||||
w1.puts "c"
|
||||
w1.puts "a"
|
||||
w1.puts "b"
|
||||
w1.close
|
||||
assert_equal("a\nb\nc\n", r2.read)
|
||||
}
|
||||
}
|
||||
|
||||
with_pipes(5) {|pipes|
|
||||
ios = pipes.flatten
|
||||
h = {}
|
||||
ios.length.times {|i| h[ios[i]] = ios[(i-1)%ios.length] }
|
||||
h2 = h.invert
|
||||
rios = pipes.map {|r, w| r }
|
||||
wios = pipes.map {|r, w| w }
|
||||
child_wfds = wios.map {|w| h2[w].fileno }
|
||||
pid = spawn(EnvUtil.rubybin, "-e",
|
||||
"[#{child_wfds.join(',')}].each {|fd| IO.new(fd).puts fd }", h)
|
||||
pipes.each {|r, w|
|
||||
assert_equal("#{h2[w].fileno}\n", r.gets)
|
||||
}
|
||||
Process.wait pid;
|
||||
}
|
||||
|
||||
with_pipes(5) {|pipes|
|
||||
ios = pipes.flatten
|
||||
h = {}
|
||||
ios.length.times {|i| h[ios[i]] = ios[(i+1)%ios.length] }
|
||||
h2 = h.invert
|
||||
rios = pipes.map {|r, w| r }
|
||||
wios = pipes.map {|r, w| w }
|
||||
child_wfds = wios.map {|w| h2[w].fileno }
|
||||
pid = spawn(EnvUtil.rubybin, "-e",
|
||||
"[#{child_wfds.join(',')}].each {|fd| IO.new(fd).puts fd }", h)
|
||||
pipes.each {|r, w|
|
||||
assert_equal("#{h2[w].fileno}\n", r.gets)
|
||||
}
|
||||
Process.wait pid;
|
||||
}
|
||||
|
||||
closed_fd = nil
|
||||
with_pipes(5) {|pipes|
|
||||
io = pipes.last.last
|
||||
closed_fd = io.fileno
|
||||
}
|
||||
assert_raise(Errno::EBADF) { Process.wait spawn("true", closed_fd=>closed_fd) }
|
||||
|
||||
with_pipe {|r, w|
|
||||
w.close_on_exec = true
|
||||
pid = spawn(EnvUtil.rubybin, "-e", "IO.new(#{w.fileno}).print 'a'", w=>w)
|
||||
w.close
|
||||
assert_equal("a", r.read)
|
||||
Process.wait pid
|
||||
}
|
||||
|
||||
system("echo funya", :out=>"out")
|
||||
assert_equal("funya\n", File.read("out"))
|
||||
system("echo henya 1>&2", :err=>"out")
|
||||
assert_equal("henya\n", File.read("out"))
|
||||
IO.popen(["cat", :in=>"out"]) {|io|
|
||||
assert_equal("henya\n", io.read)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def test_execopts_exec
|
||||
with_tmpchdir {|d|
|
||||
pid = fork {
|
||||
exec "echo aaa", STDOUT=>"foo"
|
||||
}
|
||||
Process.wait pid
|
||||
assert_equal("aaa\n", File.read("foo"))
|
||||
}
|
||||
end
|
||||
|
||||
def test_execopts_popen
|
||||
with_tmpchdir {|d|
|
||||
IO.popen("echo foo") {|io| assert_equal("foo\n", io.read) }
|
||||
assert_raise(Errno::ENOENT) { IO.popen(["echo bar"]) {} }
|
||||
IO.popen(["echo", "baz"]) {|io| assert_equal("baz\n", io.read) }
|
||||
#IO.popen(["echo", "qux", STDOUT=>STDOUT]) {|io| assert_equal("qux\n", io.read) }
|
||||
IO.popen(["echo", "hoge", STDERR=>STDOUT]) {|io|
|
||||
assert_equal("hoge\n", io.read)
|
||||
}
|
||||
#IO.popen(["echo", "fuga", STDOUT=>"out"]) {|io|
|
||||
# assert_equal("", io.read)
|
||||
#}
|
||||
#assert_equal("fuga\n", File.read("out"))
|
||||
#IO.popen(["sh", "-c", "echo a >&3", 3=>STDOUT]) {|io|
|
||||
# assert_equal("a\n", io.read)
|
||||
#}
|
||||
IO.popen(["sh", "-c", "echo b >&9",
|
||||
9=>["out2", File::WRONLY|File::CREAT|File::TRUNC]]) {|io|
|
||||
assert_equal("", io.read)
|
||||
}
|
||||
assert_equal("b\n", File.read("out2"))
|
||||
IO.popen("-") {|io|
|
||||
if !io
|
||||
puts "fooo"
|
||||
else
|
||||
assert_equal("fooo\n", io.read)
|
||||
end
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def test_fd_inheritance
|
||||
with_pipe {|r, w|
|
||||
system("echo ba >&#{w.fileno}")
|
||||
w.close
|
||||
assert_equal("ba\n", r.read)
|
||||
}
|
||||
with_pipe {|r, w|
|
||||
Process.wait spawn("echo bi >&#{w.fileno}")
|
||||
w.close
|
||||
assert_equal("bi\n", r.read)
|
||||
}
|
||||
with_pipe {|r, w|
|
||||
Process.wait fork { exec("echo bu >&#{w.fileno}") }
|
||||
w.close
|
||||
assert_equal("bu\n", r.read)
|
||||
}
|
||||
with_pipe {|r, w|
|
||||
io = IO.popen("echo be 2>&1 >&#{w.fileno}")
|
||||
w.close
|
||||
errmsg = io.read
|
||||
assert_equal("", r.read)
|
||||
assert_not_equal("", errmsg)
|
||||
}
|
||||
with_pipe {|r, w|
|
||||
io = IO.popen([EnvUtil.rubybin, "-e", "STDERR.reopen(STDOUT); IO.new(#{w.fileno}).puts('me')"])
|
||||
w.close
|
||||
errmsg = io.read
|
||||
assert_equal("", r.read)
|
||||
assert_not_equal("", errmsg)
|
||||
}
|
||||
with_pipe {|r, w|
|
||||
errmsg = `echo bo 2>&1 >&#{w.fileno}`
|
||||
w.close
|
||||
assert_equal("", r.read)
|
||||
assert_not_equal("", errmsg)
|
||||
}
|
||||
end
|
||||
|
||||
def test_execopts_close_others
|
||||
with_tmpchdir {|d|
|
||||
with_pipe {|r, w|
|
||||
system("exec 2>err; echo ma >&#{w.fileno}", :close_others=>true)
|
||||
w.close
|
||||
assert_equal("", r.read)
|
||||
assert_not_equal("", File.read("err"))
|
||||
File.unlink("err")
|
||||
}
|
||||
with_pipe {|r, w|
|
||||
Process.wait spawn("exec 2>err; echo mi >&#{w.fileno}", :close_others=>true)
|
||||
w.close
|
||||
assert_equal("", r.read)
|
||||
assert_not_equal("", File.read("err"))
|
||||
File.unlink("err")
|
||||
}
|
||||
with_pipe {|r, w|
|
||||
Process.wait fork { exec("exec 2>err; echo mu >&#{w.fileno}", :close_others=>true) }
|
||||
w.close
|
||||
assert_equal("", r.read)
|
||||
assert_not_equal("", File.read("err"))
|
||||
File.unlink("err")
|
||||
}
|
||||
with_pipe {|r, w|
|
||||
io = IO.popen([EnvUtil.rubybin, "-e", "STDERR.reopen(STDOUT); IO.new(#{w.fileno}).puts('me')", :close_others=>true])
|
||||
w.close
|
||||
errmsg = io.read
|
||||
assert_equal("", r.read)
|
||||
assert_not_equal("", errmsg)
|
||||
}
|
||||
with_pipe {|r, w|
|
||||
io = IO.popen([EnvUtil.rubybin, "-e", "STDERR.reopen(STDOUT); IO.new(#{w.fileno}).puts('mo')", :close_others=>false])
|
||||
w.close
|
||||
errmsg = io.read
|
||||
assert_equal("mo\n", r.read)
|
||||
assert_equal("", errmsg)
|
||||
}
|
||||
with_pipe {|r, w|
|
||||
io = IO.popen([EnvUtil.rubybin, "-e", "STDERR.reopen(STDOUT); IO.new(#{w.fileno}).puts('mo')", :close_others=>nil])
|
||||
w.close
|
||||
errmsg = io.read
|
||||
assert_equal("mo\n", r.read)
|
||||
assert_equal("", errmsg)
|
||||
}
|
||||
|
||||
}
|
||||
end
|
||||
|
||||
def test_system
|
||||
str = "echo fofo"
|
||||
assert_nil(system([str, str]))
|
||||
end
|
||||
|
||||
end
|
||||
|
|
Загрузка…
Ссылка в новой задаче