aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNobuyoshi Nakada <nobu@ruby-lang.org>2023-08-05 22:11:52 +0900
committerNobuyoshi Nakada <nobu@ruby-lang.org>2023-09-25 22:57:28 +0900
commit7e2775b057d19d5ed06877c3b02299e3b5d2beb7 (patch)
treebc78bf65a2be58d00212e1eb4dcbc927be968560
parent0f642907046e98cc026b50416bd63212b92112cd (diff)
downloadruby-7e2775b057d19d5ed06877c3b02299e3b5d2beb7.tar.gz
Invoke the command when RUBY_BUGREPORT_PATH starts with `|`
-rw-r--r--common.mk2
-rw-r--r--error.c67
-rw-r--r--io.c55
-rw-r--r--test/ruby/test_rubyoptions.rb15
4 files changed, 125 insertions, 14 deletions
diff --git a/common.mk b/common.mk
index 3fcbb52e50..392c24f47a 100644
--- a/common.mk
+++ b/common.mk
@@ -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
diff --git a/error.c b/error.c
index 5fd40585cb..248132d2cb 100644
--- a/error.c
+++ b/error.c
@@ -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();
}
diff --git a/io.c b/io.c
index 48cdc5b9a7..779f671876 100644
--- a/io.c
+++ b/io.c
@@ -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"