diff options
author | Nobuyoshi Nakada <nobu@ruby-lang.org> | 2023-08-05 22:11:52 +0900 |
---|---|---|
committer | Nobuyoshi Nakada <nobu@ruby-lang.org> | 2023-09-25 22:57:28 +0900 |
commit | 7e2775b057d19d5ed06877c3b02299e3b5d2beb7 (patch) | |
tree | bc78bf65a2be58d00212e1eb4dcbc927be968560 | |
parent | 0f642907046e98cc026b50416bd63212b92112cd (diff) | |
download | ruby-7e2775b057d19d5ed06877c3b02299e3b5d2beb7.tar.gz |
Invoke the command when RUBY_BUGREPORT_PATH starts with `|`
-rw-r--r-- | common.mk | 2 | ||||
-rw-r--r-- | error.c | 67 | ||||
-rw-r--r-- | io.c | 55 | ||||
-rw-r--r-- | test/ruby/test_rubyoptions.rb | 15 |
4 files changed, 125 insertions, 14 deletions
@@ -6324,6 +6324,7 @@ error.$(OBJEXT): $(top_srcdir)/internal/imemo.h error.$(OBJEXT): $(top_srcdir)/internal/io.h error.$(OBJEXT): $(top_srcdir)/internal/load.h error.$(OBJEXT): $(top_srcdir)/internal/object.h +error.$(OBJEXT): $(top_srcdir)/internal/process.h error.$(OBJEXT): $(top_srcdir)/internal/serial.h error.$(OBJEXT): $(top_srcdir)/internal/static_assert.h error.$(OBJEXT): $(top_srcdir)/internal/string.h @@ -6516,6 +6517,7 @@ error.$(OBJEXT): {$(VPATH)}st.h error.$(OBJEXT): {$(VPATH)}subst.h error.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h error.$(OBJEXT): {$(VPATH)}thread_native.h +error.$(OBJEXT): {$(VPATH)}util.h error.$(OBJEXT): {$(VPATH)}vm_core.h error.$(OBJEXT): {$(VPATH)}vm_opts.h error.$(OBJEXT): {$(VPATH)}warning.rbinc @@ -23,6 +23,10 @@ # include <unistd.h> #endif +#ifdef HAVE_SYS_WAIT_H +# include <sys/wait.h> +#endif + #if defined __APPLE__ # include <AvailabilityMacros.h> #endif @@ -34,12 +38,14 @@ #include "internal/io.h" #include "internal/load.h" #include "internal/object.h" +#include "internal/process.h" #include "internal/string.h" #include "internal/symbol.h" #include "internal/thread.h" #include "internal/variable.h" #include "ruby/encoding.h" #include "ruby/st.h" +#include "ruby/util.h" #include "ruby_assert.h" #include "vm_core.h" @@ -716,9 +722,12 @@ append_basename(char *p, const char *pe, struct path_string *path, VALUE str) } static void -finish_report(FILE *out) +finish_report(FILE *out, rb_pid_t pid) { if (out != stdout && out != stderr) fclose(out); +#ifdef HAVE_WORKING_FORK + if (pid > 0) waitpid(pid, NULL, 0); +#endif } struct report_expansion { @@ -743,19 +752,24 @@ struct report_expansion { * %p PID of dumped process in decimal. * %t Time of dump, expressed as seconds since the Epoch, * 1970-01-01 00:00:00 +0000 (UTC). + * %NNN Octal char code, upto 3 digits. */ static char * expand_report_argument(const char **input_template, struct report_expansion *values, - char *buf, size_t size) + char *buf, size_t size, bool word) { char *p = buf; char *end = buf + size; const char *template = *input_template; + bool store = true; if (p >= end-1 || !*template) return NULL; do { char c = *template++; + if (word && ISSPACE(c)) break; + if (!store) continue; if (c == '%') { + size_t n; switch (c = *template++) { case 'e': p = append_basename(p, end, &values->exe, rb_argv0); @@ -779,6 +793,13 @@ expand_report_argument(const char **input_template, struct report_expansion *val snprintf(p, end-p, "%" PRI_TIMET_PREFIX "d", values->time); p += strlen(p); continue; + default: + if (c >= '0' && c <= '7') { + c = (unsigned char)ruby_scan_oct(template-1, 3, &n); + template += n - 1; + if (!c) store = false; + } + break; } } if (p < end-1) *p++ = c; @@ -788,15 +809,31 @@ expand_report_argument(const char **input_template, struct report_expansion *val return ++p; } +FILE *ruby_popen_writer(char *const *argv, rb_pid_t *pid); + static FILE * -open_report_path(const char *template, char *buf, size_t size) +open_report_path(const char *template, char *buf, size_t size, rb_pid_t *pid) { struct report_expansion values = {{0}}; if (!template) return NULL; if (0) fprintf(stderr, "RUBY_BUGREPORT_PATH=%s\n", buf); - if (*template) { - expand_report_argument(&template, &values, buf, size); + if (*template == '|') { + char *argv[16], *bufend = buf + size, *p; + int argc; + template++; + for (argc = 0; argc < numberof(argv) - 1; ++argc) { + while (*template && ISSPACE(*template)) template++; + p = expand_report_argument(&template, &values, buf, bufend-buf, true); + if (!p) break; + argv[argc] = buf; + buf = p; + } + argv[argc] = NULL; + if (!p) return ruby_popen_writer(argv, pid); + } + else if (*template) { + expand_report_argument(&template, &values, buf, size, false); return fopen(buf, "w"); } return NULL; @@ -805,10 +842,10 @@ open_report_path(const char *template, char *buf, size_t size) /* SIGSEGV handler might have a very small stack. Thus we need to use it carefully. */ #define REPORT_BUG_BUFSIZ 256 static FILE * -bug_report_file(const char *file, int line) +bug_report_file(const char *file, int line, rb_pid_t *pid) { char buf[REPORT_BUG_BUFSIZ]; - FILE *out = open_report_path(getenv("RUBY_BUGREPORT_PATH"), buf, sizeof(buf)); + FILE *out = open_report_path(getenv("RUBY_BUGREPORT_PATH"), buf, sizeof(buf), pid); int len = err_position_0(buf, sizeof(buf), file, line); if (out) { @@ -926,7 +963,7 @@ bug_report_begin_valist(FILE *out, const char *fmt, va_list args) } while (0) static void -bug_report_end(FILE *out) +bug_report_end(FILE *out, rb_pid_t pid) { /* call additional bug reporters */ { @@ -937,24 +974,26 @@ bug_report_end(FILE *out) } } postscript_dump(out); - finish_report(out); + finish_report(out, pid); } #define report_bug(file, line, fmt, ctx) do { \ - FILE *out = bug_report_file(file, line); \ + rb_pid_t pid = -1; \ + FILE *out = bug_report_file(file, line, &pid); \ if (out) { \ bug_report_begin(out, fmt); \ rb_vm_bugreport(ctx, out); \ - bug_report_end(out); \ + bug_report_end(out, pid); \ } \ } while (0) \ #define report_bug_valist(file, line, fmt, ctx, args) do { \ - FILE *out = bug_report_file(file, line); \ + rb_pid_t pid = -1; \ + FILE *out = bug_report_file(file, line, &pid); \ if (out) { \ bug_report_begin_valist(out, fmt, args); \ rb_vm_bugreport(ctx, out); \ - bug_report_end(out); \ + bug_report_end(out, pid); \ } \ } while (0) \ @@ -1081,7 +1120,7 @@ rb_assert_failure(const char *file, int line, const char *name, const char *expr fprintf(out, "%s\n%s\n\n", expr, rb_dynamic_description); preface_dump(out); rb_vm_bugreport(NULL, out); - bug_report_end(out); + bug_report_end(out, -1); die(); } @@ -170,6 +170,7 @@ off_t __syscall(quad_t number, ...); #define open rb_w32_uopen #undef rename #define rename(f, t) rb_w32_urename((f), (t)) +#include "win32/file.h" #endif VALUE rb_cIO; @@ -7955,6 +7956,60 @@ popen_finish(VALUE port, VALUE klass) return port; } +#if defined(HAVE_WORKING_FORK) && !defined(__EMSCRIPTEN__) +struct popen_writer_arg { + char *const *argv; + struct popen_arg popen; +}; + +static int +exec_popen_writer(void *arg, char *errmsg, size_t buflen) +{ + struct popen_writer_arg *pw = arg; + pw->popen.modef = FMODE_WRITABLE; + popen_redirect(&pw->popen); + execv(pw->argv[0], pw->argv); + strlcpy(errmsg, strerror(errno), buflen); + return -1; +} +#endif + +FILE * +ruby_popen_writer(char *const *argv, rb_pid_t *pid) +{ +#if (defined(HAVE_WORKING_FORK) && !defined(__EMSCRIPTEN__)) || defined(_WIN32) +# ifdef HAVE_WORKING_FORK + struct popen_writer_arg pw; + int *const write_pair = pw.popen.pair; +# else + int write_pair[2]; +# endif + + int result = rb_cloexec_pipe(write_pair); + *pid = -1; + if (result == 0) { +# ifdef HAVE_WORKING_FORK + pw.argv = argv; + int status; + char errmsg[80] = {'\0'}; + *pid = rb_fork_async_signal_safe(&status, exec_popen_writer, &pw, Qnil, errmsg, sizeof(errmsg)); +# else + *pid = rb_w32_uspawn_process(P_NOWAIT, argv[0], argv, write_pair[0], -1, -1, 0); + const char *errmsg = (*pid < 0) ? strerror(errno) : NULL; +# endif + close(write_pair[0]); + if (*pid < 0) { + close(write_pair[1]); + fprintf(stderr, "ruby_popen_writer(%s): %s\n", argv[0], errmsg); + } + else { + return fdopen(write_pair[1], "w"); + } + } +#endif + return NULL; +} + static void rb_scan_open_args(int argc, const VALUE *argv, VALUE *fname_p, int *oflags_p, int *fmode_p, diff --git a/test/ruby/test_rubyoptions.rb b/test/ruby/test_rubyoptions.rb index 14264698d3..884a5af32e 100644 --- a/test/ruby/test_rubyoptions.rb +++ b/test/ruby/test_rubyoptions.rb @@ -880,6 +880,21 @@ class TestRubyOptions < Test::Unit::TestCase end end + def test_bugreport_path_pipe + if File.executable?(echo = "/bin/echo") + elsif /mswin|ming/ =~ RUBY_PLATFORM + echo = "echo" + else + omit "/bin/echo not found" + end + assert_in_out_err([{"RUBY_BUGREPORT_PATH"=>"| #{echo} %e:%f:%p"}], SEGVTest::KILL_SELF, + encoding: "ASCII-8BIT", + **SEGVTest::ExecOptions) do |stdout, stderr, status| + assert_empty(stderr) + assert_equal(["#{File.basename(EnvUtil.rubybin)}:-:#{status.pid}"], stdout) + end + end + def test_DATA Tempfile.create(["test_ruby_test_rubyoption", ".rb"]) {|t| t.puts "puts DATA.read.inspect" |