summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog20
-rw-r--r--include/ruby/intern.h2
-rw-r--r--process.c140
-rw-r--r--test/ruby/test_process.rb6
4 files changed, 117 insertions, 51 deletions
diff --git a/ChangeLog b/ChangeLog
index ff16faf31f..8a3d5fa9e6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,23 @@
+Sun Jun 3 17:23:52 2012 Tanaka Akira <akr@fsij.org>
+
+ * use execve() to preserve environment variables when exec method is
+ failed. [ruby-core:44093] [ruby-trunk - Bug #6249]
+
+ * include/ruby/intern.h (rb_exec_arg): add envp_str and envp_buf field
+ to store envp of execve().
+
+ * process.c (proc_exec_v): takes envp_str as an argument and use it
+ for execve().
+ (rb_proc_exec_ne): extended version of rb_proc_exec_n().
+ (rb_proc_exec_n): use rb_proc_exec_ne().
+ (rb_proc_exec): follow proc_exec_v() change.
+ (fill_envp_buf_i): new function.
+ (rb_exec_arg_fixup): set up envp_str and envp_buf.
+ (save_env_i): removed.
+ (save_env): removed.
+ (rb_run_exec_options_err): don't modify environment variables.
+ (rb_exec_err): use rb_proc_exec_ne().
+
Sun Jun 3 16:33:58 2012 Nobuyoshi Nakada <nobu@ruby-lang.org>
* marshal.c: revert r35879 "now marshal_{load|dump} are external."
diff --git a/include/ruby/intern.h b/include/ruby/intern.h
index 61ed85fcdb..668d59a1fb 100644
--- a/include/ruby/intern.h
+++ b/include/ruby/intern.h
@@ -592,6 +592,8 @@ struct rb_exec_arg {
VALUE options;
VALUE redirect_fds;
VALUE progname;
+ VALUE envp_str;
+ VALUE envp_buf;
};
int rb_proc_exec_n(int, VALUE*, const char*);
int rb_proc_exec(const char*);
diff --git a/process.c b/process.c
index cf9144f716..ca8efe90c5 100644
--- a/process.c
+++ b/process.c
@@ -1068,13 +1068,13 @@ exec_with_sh(const char *prog, char **argv)
#ifdef __native_client__
static int
-proc_exec_v(char **argv, const char *prog)
+proc_exec_v(char **argv, const char *prog, VALUE envp_str)
{
rb_notimplement();
}
#else
static int
-proc_exec_v(char **argv, const char *prog)
+proc_exec_v(char **argv, const char *prog, VALUE envp_str)
{
char fbuf[MAXPATHLEN];
# if defined(__EMX__) || defined(OS2)
@@ -1118,7 +1118,10 @@ proc_exec_v(char **argv, const char *prog)
}
# endif /* __EMX__ */
before_exec();
- execv(prog, argv);
+ if (envp_str)
+ execve(prog, argv, (char **)RSTRING_PTR(envp_str));
+ else
+ execv(prog, argv);
preserving_errno(try_with_sh(prog, argv); after_exec());
# if defined(__EMX__) || defined(OS2)
if (new_argv) {
@@ -1130,8 +1133,8 @@ proc_exec_v(char **argv, const char *prog)
}
#endif
-int
-rb_proc_exec_n(int argc, VALUE *argv, const char *prog)
+static int
+rb_proc_exec_ne(int argc, VALUE *argv, const char *prog, VALUE envp_str)
{
char **args;
int i;
@@ -1144,12 +1147,18 @@ rb_proc_exec_n(int argc, VALUE *argv, const char *prog)
}
args[i] = 0;
if (args[0]) {
- ret = proc_exec_v(args, prog);
+ ret = proc_exec_v(args, prog, envp_str);
}
ALLOCV_END(v);
return ret;
}
+int
+rb_proc_exec_n(int argc, VALUE *argv, const char *prog)
+{
+ return rb_proc_exec_ne(argc, argv, prog, Qfalse);
+}
+
#ifdef __native_client__
int
rb_proc_exec(const char *str)
@@ -1217,7 +1226,7 @@ rb_proc_exec(const char *str)
*a = NULL;
}
if (argv[0]) {
- ret = proc_exec_v(argv, 0);
+ ret = proc_exec_v(argv, NULL, Qfalse);
}
else {
errno = ENOENT;
@@ -1855,10 +1864,81 @@ rb_exec_arg_init(int argc, VALUE *argv, int accept_shell, struct rb_exec_arg *e)
return prog;
}
+static int
+fill_envp_buf_i(st_data_t st_key, st_data_t st_val, st_data_t arg)
+{
+ VALUE key = (VALUE)st_key;
+ VALUE val = (VALUE)st_val;
+ VALUE envp_buf = (VALUE)arg;
+
+ rb_str_buf_cat2(envp_buf, StringValueCStr(key));
+ rb_str_buf_cat2(envp_buf, "=");
+ rb_str_buf_cat2(envp_buf, StringValueCStr(val));
+ rb_str_buf_cat(envp_buf, "", 1); /* append '\0' */
+
+ return ST_CONTINUE;
+}
+
void
rb_exec_arg_fixup(struct rb_exec_arg *e)
{
+ VALUE unsetenv_others, envopts;
+
e->redirect_fds = check_exec_fds(e->options);
+
+ unsetenv_others = rb_ary_entry(e->options, EXEC_OPTION_UNSETENV_OTHERS);
+ envopts = rb_ary_entry(e->options, EXEC_OPTION_ENV);
+ if (RTEST(unsetenv_others) || !NIL_P(envopts)) {
+ VALUE envtbl, envp_str, envp_buf;
+ char *p, *ep;
+ if (RTEST(unsetenv_others)) {
+ envtbl = rb_hash_new();
+ }
+ else {
+ envtbl = rb_const_get(rb_cObject, rb_intern("ENV"));
+ envtbl = rb_convert_type(envtbl, T_HASH, "Hash", "to_hash");
+ }
+ hide_obj(envtbl);
+ if (!NIL_P(envopts)) {
+ st_table *stenv = RHASH_TBL(envtbl);
+ long i;
+ for (i = 0; i < RARRAY_LEN(envopts); i++) {
+ VALUE pair = RARRAY_PTR(envopts)[i];
+ VALUE key = RARRAY_PTR(pair)[0];
+ VALUE val = RARRAY_PTR(pair)[1];
+ if (NIL_P(val)) {
+ st_data_t stkey = (st_data_t)key;
+ st_delete(stenv, &stkey, NULL);
+ }
+ else {
+ st_insert(stenv, (st_data_t)key, (st_data_t)val);
+ }
+ }
+ }
+ envp_buf = rb_str_buf_new(0);
+ hide_obj(envp_buf);
+ st_foreach(RHASH_TBL(envtbl), fill_envp_buf_i, (st_data_t)envp_buf);
+ envp_str = rb_str_buf_new(sizeof(char*) * (RHASH_SIZE(envtbl) + 1));
+ hide_obj(envp_str);
+ p = RSTRING_PTR(envp_buf);
+ ep = p + RSTRING_LEN(envp_buf);
+ while (p < ep) {
+ rb_str_buf_cat(envp_str, (char *)&p, sizeof(p));
+ p += strlen(p) + 1;
+ }
+ p = NULL;
+ rb_str_buf_cat(envp_str, (char *)&p, sizeof(p));
+ e->envp_str = envp_str;
+ e->envp_buf = envp_buf;
+
+ /*
+ char **tmp_envp = (char **)RSTRING_PTR(envp_str);
+ while (*tmp_envp) {
+ printf("%s\n", *tmp_envp);
+ tmp_envp++;
+ }
+ */
+ }
}
/*
@@ -2027,28 +2107,6 @@ save_redirect_fd(int fd, VALUE save, char *errmsg, size_t errmsg_buflen)
return 0;
}
-static VALUE
-save_env_i(VALUE i, VALUE ary, int argc, VALUE *argv)
-{
- rb_ary_push(ary, hide_obj(rb_ary_dup(argv[0])));
- return Qnil;
-}
-
-static void
-save_env(VALUE save)
-{
- if (!NIL_P(save) && NIL_P(rb_ary_entry(save, EXEC_OPTION_ENV))) {
- VALUE env = rb_const_get(rb_cObject, rb_intern("ENV"));
- if (RTEST(env)) {
- VALUE ary = hide_obj(rb_ary_new());
- rb_block_call(env, rb_intern("each"), 0, 0, save_env_i,
- (VALUE)ary);
- rb_ary_store(save, EXEC_OPTION_ENV, ary);
- }
- rb_ary_store(save, EXEC_OPTION_UNSETENV_OTHERS, Qtrue);
- }
-}
-
static int
intcmp(const void *a, const void *b)
{
@@ -2372,6 +2430,7 @@ rb_run_exec_options_err(const struct rb_exec_arg *e, struct rb_exec_arg *s, char
s->options = soptions = hide_obj(rb_ary_new());
s->redirect_fds = Qnil;
s->progname = Qnil;
+ s->envp_str = s->envp_buf = 0;
}
#ifdef HAVE_SETPGID
@@ -2390,27 +2449,6 @@ rb_run_exec_options_err(const struct rb_exec_arg *e, struct rb_exec_arg *s, char
}
#endif
- obj = rb_ary_entry(options, EXEC_OPTION_UNSETENV_OTHERS);
- if (RTEST(obj)) {
- save_env(soptions);
- rb_env_clear();
- }
-
- obj = rb_ary_entry(options, EXEC_OPTION_ENV);
- if (!NIL_P(obj)) {
- long i;
- save_env(soptions);
- for (i = 0; i < RARRAY_LEN(obj); i++) {
- VALUE pair = RARRAY_PTR(obj)[i];
- VALUE key = RARRAY_PTR(pair)[0];
- VALUE val = RARRAY_PTR(pair)[1];
- if (NIL_P(val))
- ruby_setenv(StringValueCStr(key), 0);
- else
- ruby_setenv(StringValueCStr(key), StringValueCStr(val));
- }
- }
-
obj = rb_ary_entry(options, EXEC_OPTION_UMASK);
if (!NIL_P(obj)) {
mode_t mask = NUM2MODET(obj);
@@ -2492,7 +2530,7 @@ rb_exec_err(const struct rb_exec_arg *e, char *errmsg, size_t errmsg_buflen)
rb_proc_exec(prog);
}
else {
- rb_proc_exec_n(argc, argv, prog);
+ rb_proc_exec_ne(argc, argv, prog, e->envp_str);
}
return -1;
}
diff --git a/test/ruby/test_process.rb b/test/ruby/test_process.rb
index 34c1355e58..8bb1b4bb5f 100644
--- a/test/ruby/test_process.rb
+++ b/test/ruby/test_process.rb
@@ -303,6 +303,12 @@ class TestProcess < Test::Unit::TestCase
end
end
+ def test_execopts_preserve_env_on_exec_failure
+ ENV["mgg"] = nil
+ assert_raise(Errno::ENOENT) { Process.exec({"mgg" => "mggoo"}, "/nonexistent") }
+ assert_equal(nil, ENV["mgg"], "[ruby-core:44093] [ruby-trunk - Bug #6249]")
+ end
+
def test_execopts_unsetenv_others
h = {}
MANDATORY_ENVS.each {|k| e = ENV[k] and h[k] = e}