nbdkit_error has traditionally been a "fancy wrapper around fprintf"
(kind of, don't take that literally). It is encouraged that plugins
and filters do something like:
if (error) {
nbdkit_error ("oops, a bad thing happened");
return -1;
}
but we don't enforce this. Plugins might call nbdkit_error more than
once or not at all.
The point where we get to sending an error back over the wire to the
NBD client is long after the plugin returned above, and after
nbdkit_error was called.
Therefore in order to send errors back to the NBD client, we must keep
the last error message around.
This change simply modifies nbdkit_error to make a best-effort attempt
to save the last error message in thread-local storage.
We also clear the last error when a new request starts, to ensure that
we don't leak errors across different callbacks or connections.
---
server/log.c | 21 +++++++++++++++++++++
server/protocol.c | 5 +++++
2 files changed, 26 insertions(+)
diff --git a/server/log.c b/server/log.c
index 9c1f667a9b..acf14d573e 100644
--- a/server/log.c
+++ b/server/log.c
@@ -40,6 +40,25 @@
#include "internal.h"
+/* Copy the error message to threadlocal. This is sent to callers
+ * which are using structured replies, but is for extra information
+ * only so don't fail if we are unable to copy it.
+ */
+static void
+copy_error_to_threadlocal (int orig_errno, const char *fs, va_list args)
+{
+ va_list args_copy;
+ char *msg;
+ int r;
+
+ va_copy (args_copy, args);
+ errno = orig_errno; /* must restore in case fs contains %m */
+ r = vasprintf (&msg, fs, args_copy);
+ va_end (args_copy);
+ if (r != -1 && msg)
+ threadlocal_set_last_error (msg); /* ownership passed to threadlocal */
+}
+
/* Call the right log_*_verror function depending on log_sink.
* Note: preserves the previous value of errno.
*/
@@ -48,6 +67,8 @@ log_verror (const char *fs, va_list args)
{
int orig_errno = errno;
+ copy_error_to_threadlocal (orig_errno, fs, args);
+
switch (log_to) {
case LOG_TO_DEFAULT:
if (forked_into_background)
diff --git a/server/protocol.c b/server/protocol.c
index 677da05cc2..d428bfc852 100644
--- a/server/protocol.c
+++ b/server/protocol.c
@@ -239,6 +239,11 @@ handle_request (uint16_t cmd, uint16_t flags, uint64_t offset,
uint32_t count,
*/
threadlocal_set_errno (0);
+ /* Also clear the last error in this thread so we will only save
+ * nbdkit_error() from this request.
+ */
+ threadlocal_clear_last_error ();
+
switch (cmd) {
case NBD_CMD_READ:
if (backend_pread (c, buf, count, offset, 0, &err) == -1)
--
2.44.0