errdetail-pid-fix2.patch
text/x-patch
Filename: errdetail-pid-fix2.patch
Type: text/x-patch
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
| File | + | − |
|---|---|---|
| src/backend/replication/syncrep.c | 12 | 16 |
| src/backend/tcop/postgres.c | 8 | 0 |
| src/bin/psql/t/001_basic.pl | 4 | 1 |
| src/include/libpq/pqsignal.h | 4 | 0 |
| src/port/pqsignal.c | 49 | 9 |
diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c
index 46a778f0917..896ba45412d 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -300,22 +300,18 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit)
*/
if (ProcDiePending)
{
- /*
- * ProcDieSenderPid/Uid are read directly from the globals here
- * rather than copied to locals first; a second SIGTERM could
- * change them between reads, but that is harmless because the
- * process is about to die anyway. The signal sender detail is
- * inlined rather than using a separate errdetail() call because
- * it must be appended to the existing detail message.
- */
- ereport(WARNING,
- (errcode(ERRCODE_ADMIN_SHUTDOWN),
- errmsg("canceling the wait for synchronous replication and terminating connection due to administrator command"),
- errdetail("The transaction has already committed locally, but might not have been replicated to the standby.%s",
- ProcDieSenderPid == 0 ? "" :
- psprintf("\nSignal sent by PID %d, UID %d.",
- (int) ProcDieSenderPid,
- (int) ProcDieSenderUid))));
+ if (ProcDieSenderPid != 0)
+ ereport(WARNING,
+ (errcode(ERRCODE_ADMIN_SHUTDOWN),
+ errmsg("canceling the wait for synchronous replication and terminating connection due to administrator command"),
+ errdetail("The transaction has already committed locally, but might not have been replicated to the standby. Signal sent by PID %d, UID %d.",
+ (int) ProcDieSenderPid,
+ (int) ProcDieSenderUid)));
+ else
+ ereport(WARNING,
+ (errcode(ERRCODE_ADMIN_SHUTDOWN),
+ errmsg("canceling the wait for synchronous replication and terminating connection due to administrator command"),
+ errdetail("The transaction has already committed locally, but might not have been replicated to the standby.")));
whereToSendOutput = DestNone;
SyncRepCancelWait();
break;
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 4fb18741dc5..df336e3f194 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3023,8 +3023,16 @@ die(SIGNAL_ARGS)
/* Don't joggle the elbow of proc_exit */
if (!proc_exit_inprogress)
{
+ int sender_pid = 0;
+ int sender_uid = 0;
+
InterruptPending = true;
ProcDiePending = true;
+#ifdef HAVE_SA_SIGINFO
+ pqsignal_get_sender(&sender_pid, &sender_uid);
+#endif
+ ProcDieSenderPid = sender_pid;
+ ProcDieSenderUid = sender_uid;
}
/* for the cumulative stats system */
diff --git a/src/bin/psql/t/001_basic.pl b/src/bin/psql/t/001_basic.pl
index 7c21204c1f2..9d966c7bece 100644
--- a/src/bin/psql/t/001_basic.pl
+++ b/src/bin/psql/t/001_basic.pl
@@ -142,8 +142,11 @@ my ($ret, $out, $err) = $node->psql('postgres',
is($ret, 2, 'server crash: psql exit code');
like($out, qr/before/, 'server crash: output before crash');
unlike($out, qr/AFTER/, 'server crash: no output after crash');
+my $detail_re = check_pg_config("#define HAVE_SA_SIGINFO 1")
+ ? qr/DETAIL: Signal sent by PID \d+, UID \d+\.\n/
+ : qr//;
like( $err, qr/psql:<stdin>:2: FATAL: terminating connection due to administrator command
-(?:DETAIL: Signal sent by PID \d+, UID \d+\.\n)?psql:<stdin>:2: server closed the connection unexpectedly
+${detail_re}psql:<stdin>:2: server closed the connection unexpectedly
This probably means the server terminated abnormally
before or while processing the request.
psql:<stdin>:2: error: connection to server was lost/,
diff --git a/src/include/libpq/pqsignal.h b/src/include/libpq/pqsignal.h
index 9f494a1fdf9..40ed553cdf6 100644
--- a/src/include/libpq/pqsignal.h
+++ b/src/include/libpq/pqsignal.h
@@ -51,4 +51,8 @@ extern PGDLLIMPORT sigset_t StartupBlockSig;
extern void pqinitmask(void);
+#ifdef HAVE_SA_SIGINFO
+extern void pqsignal_get_sender(int *sender_pid, int *sender_uid);
+#endif
+
#endif /* PQSIGNAL_H */
diff --git a/src/port/pqsignal.c b/src/port/pqsignal.c
index 8841464b5cb..7ee101517aa 100644
--- a/src/port/pqsignal.c
+++ b/src/port/pqsignal.c
@@ -71,6 +71,16 @@ StaticAssertDecl(SIGALRM < PG_NSIG, "SIGALRM >= PG_NSIG");
static volatile pqsigfunc pqsignal_handlers[PG_NSIG];
+#if !defined(FRONTEND) && defined(HAVE_SA_SIGINFO)
+/*
+ * Pointer to the siginfo_t for the signal currently being handled. This
+ * lives on wrapper_handler's stack frame and is saved/restored around the
+ * call to the real handler, so nested signals each see their own siginfo.
+ * Signal handlers can call pqsignal_get_sender() to retrieve it.
+ */
+static volatile siginfo_t *current_siginfo;
+#endif
+
/*
* Except when called with SIG_IGN or SIG_DFL, pqsignal() sets up this function
* as the handler for all signals. This wrapper handler function checks that
@@ -84,7 +94,7 @@ static volatile pqsigfunc pqsignal_handlers[PG_NSIG];
*/
#if !defined(FRONTEND) && defined(HAVE_SA_SIGINFO)
static void
-wrapper_handler(int signo, siginfo_t * info, void *context)
+wrapper_handler(int postgres_signal_arg, siginfo_t *info, void *context)
#else
static void
wrapper_handler(SIGNAL_ARGS)
@@ -92,8 +102,7 @@ wrapper_handler(SIGNAL_ARGS)
{
int save_errno = errno;
#if !defined(FRONTEND) && defined(HAVE_SA_SIGINFO)
- /* SA_SIGINFO signature uses signo, not SIGNAL_ARGS macro */
- int postgres_signal_arg = signo;
+ volatile siginfo_t *saved_siginfo = current_siginfo;
#endif
Assert(postgres_signal_arg > 0);
@@ -116,19 +125,50 @@ wrapper_handler(SIGNAL_ARGS)
}
#ifdef HAVE_SA_SIGINFO
- if (signo == SIGTERM && info)
- {
- ProcDieSenderPid = info->si_pid;
- ProcDieSenderUid = info->si_uid;
- }
-#endif
+ current_siginfo = info;
+ (*pqsignal_handlers[postgres_signal_arg]) (postgres_signal_arg);
+ current_siginfo = saved_siginfo;
+#else
+ (*pqsignal_handlers[postgres_signal_arg]) (postgres_signal_arg);
#endif
+#else /* FRONTEND */
(*pqsignal_handlers[postgres_signal_arg]) (postgres_signal_arg);
+#endif
errno = save_errno;
}
+#if !defined(FRONTEND) && defined(HAVE_SA_SIGINFO)
+/*
+ * pqsignal_get_sender - retrieve the PID and UID of the current signal sender
+ *
+ * This may only be called from within a signal handler. The values come from
+ * the siginfo_t passed by the kernel to wrapper_handler, which is on the
+ * stack and thus safe against nesting: if a different signal interrupts, its
+ * wrapper_handler invocation saves and restores the pointer, so the outer
+ * handler still sees the correct siginfo after the nested handler returns.
+ *
+ * If siginfo is not available (e.g. info was NULL), returns zeros.
+ */
+void
+pqsignal_get_sender(int *sender_pid, int *sender_uid)
+{
+ volatile siginfo_t *info = current_siginfo;
+
+ if (info)
+ {
+ *sender_pid = info->si_pid;
+ *sender_uid = info->si_uid;
+ }
+ else
+ {
+ *sender_pid = 0;
+ *sender_uid = 0;
+ }
+}
+#endif
+
/*
* Set up a signal handler, with SA_RESTART, for signal "signo"
*