ruby-changes:49907
From: k0kubun <ko1@a...>
Date: Wed, 24 Jan 2018 23:11:31 +0900 (JST)
Subject: [ruby-changes:49907] k0kubun:r62025 (trunk): process.c: add :exception option to Kernel.#system
k0kubun 2018-01-24 23:11:25 +0900 (Wed, 24 Jan 2018) New Revision: 62025 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=62025 Log: process.c: add :exception option to Kernel.#system to raise error when it fails. [Feature 14386] [GH-1795] Modified files: trunk/ext/pty/pty.c trunk/internal.h trunk/io.c trunk/process.c trunk/test/ruby/test_system.rb Index: test/ruby/test_system.rb =================================================================== --- test/ruby/test_system.rb (revision 62024) +++ test/ruby/test_system.rb (revision 62025) @@ -160,4 +160,23 @@ class TestSystem < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_system.rb#L160 assert_equal(true, system(tmpfilename), '[ruby-core:32745]') } end if File.executable?("/bin/sh") + + def test_system_exception + ruby = EnvUtil.rubybin + assert_nothing_raised do + system('feature_14235', exception: false) + system("'#{ruby}' -e 'exit 1'", exception: false) + end + assert_raise(Errno::ENOENT) do + system('feature_14235', exception: true) + end + assert_raise(RuntimeError) do + system("'#{ruby}' -e 'exit 1'", exception: true) + end + begin + system("'#{ruby}' -e 'exit 1'", exception: true) + rescue RuntimeError => e + assert_equal true, e.message.include?('status (1)') + end + end end Index: internal.h =================================================================== --- internal.h (revision 62024) +++ internal.h (revision 62025) @@ -1631,6 +1631,7 @@ struct rb_execarg { https://github.com/ruby/ruby/blob/trunk/internal.h#L1631 unsigned new_pgroup_flag : 1; unsigned uid_given : 1; unsigned gid_given : 1; + unsigned exception : 1; rb_pid_t pgroup_pgid; /* asis(-1), new pgroup(0), specified pgroup (0<V). */ VALUE rlimit_limits; /* Qfalse or [[rtype, softlim, hardlim], ...] */ mode_t umask_mask; @@ -1968,9 +1969,9 @@ VALUE rb_int_positive_pow(long x, unsign https://github.com/ruby/ruby/blob/trunk/internal.h#L1969 /* process.c (export) */ int rb_exec_async_signal_safe(const struct rb_execarg *e, char *errmsg, size_t errmsg_buflen); rb_pid_t rb_fork_async_signal_safe(int *status, int (*chfunc)(void*, char *, size_t), void *charg, VALUE fds, char *errmsg, size_t errmsg_buflen); -VALUE rb_execarg_new(int argc, const VALUE *argv, int accept_shell); +VALUE rb_execarg_new(int argc, const VALUE *argv, int accept_shell, int allow_exc_opt); struct rb_execarg *rb_execarg_get(VALUE execarg_obj); /* dangerous. needs GC guard. */ -VALUE rb_execarg_init(int argc, const VALUE *argv, int accept_shell, VALUE execarg_obj); +VALUE rb_execarg_init(int argc, const VALUE *argv, int accept_shell, VALUE execarg_obj, int allow_exc_opt); int rb_execarg_addopt(VALUE execarg_obj, VALUE key, VALUE val); void rb_execarg_parent_start(VALUE execarg_obj); void rb_execarg_parent_end(VALUE execarg_obj); Index: ext/pty/pty.c =================================================================== --- ext/pty/pty.c (revision 62024) +++ ext/pty/pty.c (revision 62025) @@ -182,7 +182,7 @@ establishShell(int argc, VALUE *argv, st https://github.com/ruby/ruby/blob/trunk/ext/pty/pty.c#L182 argv = &v; } - carg.execarg_obj = rb_execarg_new(argc, argv, 1); + carg.execarg_obj = rb_execarg_new(argc, argv, 1, 0); carg.eargp = rb_execarg_get(carg.execarg_obj); rb_execarg_parent_start(carg.execarg_obj); Index: io.c =================================================================== --- io.c (revision 62024) +++ io.c (revision 62025) @@ -6633,7 +6633,7 @@ pipe_open_s(VALUE prog, const char *mode https://github.com/ruby/ruby/blob/trunk/io.c#L6633 VALUE execarg_obj = Qnil; if (!is_popen_fork(prog)) - execarg_obj = rb_execarg_new(argc, argv, TRUE); + execarg_obj = rb_execarg_new(argc, argv, TRUE, FALSE); return pipe_open(execarg_obj, modestr, fmode, convconfig); } @@ -6766,14 +6766,14 @@ rb_io_s_popen(int argc, VALUE *argv, VAL https://github.com/ruby/ruby/blob/trunk/io.c#L6766 rb_raise(rb_eArgError, "too many arguments"); } #endif - execarg_obj = rb_execarg_new((int)len, RARRAY_CONST_PTR(tmp), FALSE); + execarg_obj = rb_execarg_new((int)len, RARRAY_CONST_PTR(tmp), FALSE, FALSE); RB_GC_GUARD(tmp); } else { SafeStringValue(pname); execarg_obj = Qnil; if (!is_popen_fork(pname)) - execarg_obj = rb_execarg_new(1, &pname, TRUE); + execarg_obj = rb_execarg_new(1, &pname, TRUE, FALSE); } if (!NIL_P(execarg_obj)) { if (!NIL_P(opt)) Index: process.c =================================================================== --- process.c (revision 62024) +++ process.c (revision 62025) @@ -249,7 +249,7 @@ typedef unsigned LONG_LONG unsigned_cloc https://github.com/ruby/ruby/blob/trunk/process.c#L249 typedef void (*sig_t) (int); #endif -static ID id_in, id_out, id_err, id_pid, id_uid, id_gid; +static ID id_in, id_out, id_err, id_pid, id_uid, id_gid, id_exception; static ID id_close, id_child; #ifdef HAVE_SETPGID static ID id_pgroup; @@ -2253,12 +2253,12 @@ rb_exec_fillarg(VALUE prog, int argc, VA https://github.com/ruby/ruby/blob/trunk/process.c#L2253 } VALUE -rb_execarg_new(int argc, const VALUE *argv, int accept_shell) +rb_execarg_new(int argc, const VALUE *argv, int accept_shell, int allow_exc_opt) { VALUE execarg_obj; struct rb_execarg *eargp; execarg_obj = TypedData_Make_Struct(0, struct rb_execarg, &exec_arg_data_type, eargp); - rb_execarg_init(argc, argv, accept_shell, execarg_obj); + rb_execarg_init(argc, argv, accept_shell, execarg_obj, allow_exc_opt); return execarg_obj; } @@ -2271,16 +2271,23 @@ rb_execarg_get(VALUE execarg_obj) https://github.com/ruby/ruby/blob/trunk/process.c#L2271 } VALUE -rb_execarg_init(int argc, const VALUE *orig_argv, int accept_shell, VALUE execarg_obj) +rb_execarg_init(int argc, const VALUE *orig_argv, int accept_shell, VALUE execarg_obj, int allow_exc_opt) { struct rb_execarg *eargp = rb_execarg_get(execarg_obj); - VALUE prog, ret; + VALUE prog, ret, exception = Qnil; VALUE env = Qnil, opthash = Qnil; VALUE argv_buf; VALUE *argv = ALLOCV_N(VALUE, argv_buf, argc); MEMCPY(argv, orig_argv, VALUE, argc); prog = rb_exec_getargs(&argc, &argv, accept_shell, &env, &opthash); + if (allow_exc_opt && !NIL_P(opthash) && rb_hash_has_key(opthash, ID2SYM(id_exception))) { + opthash = rb_hash_dup(opthash); + exception = rb_hash_delete(opthash, ID2SYM(id_exception)); + } rb_exec_fillarg(prog, argc, argv, env, opthash, execarg_obj); + if (RTEST(exception)) { + eargp->exception = 1; + } ALLOCV_END(argv_buf); ret = eargp->use_shell ? eargp->invoke.sh.shell_script : eargp->invoke.cmd.command_name; RB_GC_GUARD(execarg_obj); @@ -2599,7 +2606,7 @@ rb_f_exec(int argc, const VALUE *argv) https://github.com/ruby/ruby/blob/trunk/process.c#L2606 char errmsg[CHILD_ERRMSG_BUFLEN] = { '\0' }; int err; - execarg_obj = rb_execarg_new(argc, argv, TRUE); + execarg_obj = rb_execarg_new(argc, argv, TRUE, FALSE); eargp = rb_execarg_get(execarg_obj); before_exec(); /* stop timer thread before redirects */ rb_execarg_parent_start(execarg_obj); @@ -3989,7 +3996,7 @@ rb_spawn_internal(int argc, const VALUE https://github.com/ruby/ruby/blob/trunk/process.c#L3996 { VALUE execarg_obj; - execarg_obj = rb_execarg_new(argc, argv, TRUE); + execarg_obj = rb_execarg_new(argc, argv, TRUE, FALSE); return rb_execarg_spawn(execarg_obj, errmsg, errmsg_buflen); } @@ -4043,6 +4050,8 @@ rb_f_system(int argc, VALUE *argv) https://github.com/ruby/ruby/blob/trunk/process.c#L4050 { rb_pid_t pid; int status; + VALUE execarg_obj; + struct rb_execarg *eargp; #if defined(SIGCLD) && !defined(SIGCHLD) # define SIGCHLD SIGCLD @@ -4054,7 +4063,8 @@ rb_f_system(int argc, VALUE *argv) https://github.com/ruby/ruby/blob/trunk/process.c#L4063 rb_last_status_clear(); chfunc = signal(SIGCHLD, SIG_DFL); #endif - pid = rb_spawn_internal(argc, argv, NULL, 0); + execarg_obj = rb_execarg_new(argc, argv, TRUE, TRUE); + pid = rb_execarg_spawn(execarg_obj, NULL, 0); #if defined(HAVE_WORKING_FORK) || defined(HAVE_SPAWNV) if (pid > 0) { int ret, status; @@ -4066,12 +4076,26 @@ rb_f_system(int argc, VALUE *argv) https://github.com/ruby/ruby/blob/trunk/process.c#L4076 #ifdef SIGCHLD signal(SIGCHLD, chfunc); #endif + TypedData_Get_Struct(execarg_obj, struct rb_execarg, &exec_arg_data_type, eargp); if (pid < 0) { - return Qnil; + if (eargp->exception) { + int err = errno; + rb_syserr_fail_str(err, eargp->invoke.sh.shell_script); + RB_GC_GUARD(execarg_obj); + } + else { + return Qnil; + } } status = PST2INT(rb_last_status_get()); if (status == EXIT_SUCCESS) return Qtrue; - return Qfalse; + if (eargp->exception) { + rb_raise(rb_eRuntimeError, "Command failed with status (%d): %s", + WEXITSTATUS(status), RSTRING_PTR(eargp->invoke.sh.shell_script)); + } + else { + return Qfalse; + } } /* @@ -4350,7 +4374,7 @@ rb_f_spawn(int argc, VALUE *argv) https://github.com/ruby/ruby/blob/trunk/process.c#L4374 VALUE execarg_obj, fail_str; struct rb_execarg *eargp; - execarg_obj = rb_execarg_new(argc, argv, TRUE); + execarg_obj = rb_execarg_new(argc, argv, TRUE, FALSE); eargp = rb_execarg_get(execarg_obj); fail_str = eargp->use_shell ? eargp->invoke.sh.shell_script : eargp->invoke.cmd.command_name; @@ -8038,6 +8062,7 @@ Init_process(void) https://github.com/ruby/ruby/blob/trunk/process.c#L8062 id_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC = rb_intern("MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC"); #endif id_hertz = rb_intern("hertz"); + id_exception = rb_intern("exception"); InitVM(process); } -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/