postgresql/src/bin/pg_dump/pg_backup_utils.c

157 lines
4.0 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* pg_backup_utils.c
* Utility routines shared by pg_dump and pg_restore
*
*
* Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* src/bin/pg_dump/pg_backup_utils.c
*
*-------------------------------------------------------------------------
*/
#include "postgres_fe.h"
#include "parallel.h"
#include "pg_backup_utils.h"
/* Globals exported by this file */
const char *progname = NULL;
#define MAX_ON_EXIT_NICELY 20
static struct
{
on_exit_nicely_callback function;
void *arg;
2017-06-21 20:39:04 +02:00
} on_exit_nicely_list[MAX_ON_EXIT_NICELY];
static int on_exit_nicely_index;
/*
* Parse a --section=foo command line argument.
*
* Set or update the bitmask in *dumpSections according to arg.
* dumpSections is initialised as DUMP_UNSECTIONED by pg_dump and
* pg_restore so they can know if this has even been called.
*/
void
set_dump_section(const char *arg, int *dumpSections)
{
/* if this is the first call, clear all the bits */
if (*dumpSections == DUMP_UNSECTIONED)
*dumpSections = 0;
if (strcmp(arg, "pre-data") == 0)
*dumpSections |= DUMP_PRE_DATA;
else if (strcmp(arg, "data") == 0)
*dumpSections |= DUMP_DATA;
else if (strcmp(arg, "post-data") == 0)
*dumpSections |= DUMP_POST_DATA;
else
{
fprintf(stderr, _("%s: unrecognized section name: \"%s\"\n"),
progname, arg);
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
exit_nicely(1);
}
}
/*
* Write a printf-style message to stderr.
*
* The program name is prepended, if "progname" has been set.
* Also, if modulename isn't NULL, that's included too.
* Note that we'll try to translate the modulename and the fmt string.
*/
void
write_msg(const char *modulename, const char *fmt,...)
{
va_list ap;
va_start(ap, fmt);
vwrite_msg(modulename, fmt, ap);
va_end(ap);
}
/*
* As write_msg, but pass a va_list not variable arguments.
*/
void
vwrite_msg(const char *modulename, const char *fmt, va_list ap)
{
if (progname)
{
if (modulename)
fprintf(stderr, "%s: [%s] ", progname, _(modulename));
else
fprintf(stderr, "%s: ", progname);
}
vfprintf(stderr, _(fmt), ap);
}
Fix broken error handling in parallel pg_dump/pg_restore. In the original design for parallel dump, worker processes reported errors by sending them up to the master process, which would print the messages. This is unworkably fragile for a couple of reasons: it risks deadlock if a worker sends an error at an unexpected time, and if the master has already died for some reason, the user will never get to see the error at all. Revert that idea and go back to just always printing messages to stderr. This approach means that if all the workers fail for similar reasons (eg, bad password or server shutdown), the user will see N copies of that message, not only one as before. While that's slightly annoying, it's certainly better than not seeing any message; not to mention that we shouldn't assume that only the first failure is interesting. An additional problem in the same area was that the master failed to disable SIGPIPE (at least until much too late), which meant that sending a command to an already-dead worker would cause the master to crash silently. That was bad enough in itself but was made worse by the total reliance on the master to print errors: even if the worker had reported an error, you would probably not see it, depending on timing. Instead disable SIGPIPE right after we've forked the workers, before attempting to send them anything. Additionally, the master relies on seeing socket EOF to realize that a worker has exited prematurely --- but on Windows, there would be no EOF since the socket is attached to the process that includes both the master and worker threads, so it remains open. Make archive_close_connection() close the worker end of the sockets so that this acts more like the Unix case. It's not perfect, because if a worker thread exits without going through exit_nicely() the closures won't happen; but that's not really supposed to happen. This has been wrong all along, so back-patch to 9.3 where parallel dump was introduced. Report: <2458.1450894615@sss.pgh.pa.us>
2016-05-25 18:39:57 +02:00
/*
* Fail and die, with a message to stderr. Parameters as for write_msg.
*
* Note that on_exit_nicely callbacks will get run.
*/
void
exit_horribly(const char *modulename, const char *fmt,...)
{
va_list ap;
va_start(ap, fmt);
vwrite_msg(modulename, fmt, ap);
va_end(ap);
exit_nicely(1);
}
/* Register a callback to be run when exit_nicely is invoked. */
void
on_exit_nicely(on_exit_nicely_callback function, void *arg)
{
if (on_exit_nicely_index >= MAX_ON_EXIT_NICELY)
exit_horribly(NULL, "out of on_exit_nicely slots\n");
on_exit_nicely_list[on_exit_nicely_index].function = function;
on_exit_nicely_list[on_exit_nicely_index].arg = arg;
on_exit_nicely_index++;
}
/*
* Run accumulated on_exit_nicely callbacks in reverse order and then exit
Fix broken error handling in parallel pg_dump/pg_restore. In the original design for parallel dump, worker processes reported errors by sending them up to the master process, which would print the messages. This is unworkably fragile for a couple of reasons: it risks deadlock if a worker sends an error at an unexpected time, and if the master has already died for some reason, the user will never get to see the error at all. Revert that idea and go back to just always printing messages to stderr. This approach means that if all the workers fail for similar reasons (eg, bad password or server shutdown), the user will see N copies of that message, not only one as before. While that's slightly annoying, it's certainly better than not seeing any message; not to mention that we shouldn't assume that only the first failure is interesting. An additional problem in the same area was that the master failed to disable SIGPIPE (at least until much too late), which meant that sending a command to an already-dead worker would cause the master to crash silently. That was bad enough in itself but was made worse by the total reliance on the master to print errors: even if the worker had reported an error, you would probably not see it, depending on timing. Instead disable SIGPIPE right after we've forked the workers, before attempting to send them anything. Additionally, the master relies on seeing socket EOF to realize that a worker has exited prematurely --- but on Windows, there would be no EOF since the socket is attached to the process that includes both the master and worker threads, so it remains open. Make archive_close_connection() close the worker end of the sockets so that this acts more like the Unix case. It's not perfect, because if a worker thread exits without going through exit_nicely() the closures won't happen; but that's not really supposed to happen. This has been wrong all along, so back-patch to 9.3 where parallel dump was introduced. Report: <2458.1450894615@sss.pgh.pa.us>
2016-05-25 18:39:57 +02:00
* without printing any message.
*
* If running in a parallel worker thread on Windows, we only exit the thread,
* not the whole process.
*
* Note that in parallel operation on Windows, the callback(s) will be run
* by each thread since the list state is necessarily shared by all threads;
* each callback must contain logic to ensure it does only what's appropriate
* for its thread. On Unix, callbacks are also run by each process, but only
* for callbacks established before we fork off the child processes. (It'd
* be cleaner to reset the list after fork(), and let each child establish
* its own callbacks; but then the behavior would be completely inconsistent
* between Windows and Unix. For now, just be sure to establish callbacks
* before forking to avoid inconsistency.)
*/
void
exit_nicely(int code)
{
int i;
for (i = on_exit_nicely_index - 1; i >= 0; i--)
on_exit_nicely_list[i].function(code,
on_exit_nicely_list[i].arg);
#ifdef WIN32
if (parallel_init_done && GetCurrentThreadId() != mainThreadId)
_endthreadex(code);
#endif
exit(code);
}