1 /*-------------------------------------------------------------------------
4 * Recovery functions for a user-specified shell command.
6 * These recovery functions use a user-specified shell command (e.g. based
7 * on the GUC restore_command).
9 * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
10 * Portions Copyright (c) 1994, Regents of the University of California
12 * src/backend/access/transam/shell_restore.c
14 *-------------------------------------------------------------------------
21 #include "access/xlogarchive.h"
22 #include "access/xlogrecovery.h"
23 #include "common/percentrepl.h"
24 #include "storage/ipc.h"
25 #include "utils/wait_event.h"
27 static bool ExecuteRecoveryCommand(const char *command
,
28 const char *commandName
,
31 uint32 wait_event_info
,
35 * Attempt to execute a shell-based restore command.
37 * Returns true if the command has succeeded, false otherwise.
40 shell_restore(const char *file
, const char *path
,
41 const char *lastRestartPointFileName
)
43 char *nativePath
= pstrdup(path
);
47 /* Build the restore command to execute */
48 make_native_path(nativePath
);
49 cmd
= replace_percent_placeholders(recoveryRestoreCommand
,
50 "restore_command", "frp", file
,
51 lastRestartPointFileName
,
56 * Remember, we rollforward UNTIL the restore fails so failure here is
57 * just part of the process... that makes it difficult to determine
58 * whether the restore failed because there isn't an archive to restore,
59 * or because the administrator has specified the restore program
60 * incorrectly. We have to assume the former.
62 * However, if the failure was due to any sort of signal, it's best to
63 * punt and abort recovery. (If we "return false" here, upper levels will
64 * assume that recovery is complete and start up the database!) It's
65 * essential to abort on child SIGINT and SIGQUIT, because per spec
66 * system() ignores SIGINT and SIGQUIT while waiting; if we see one of
67 * those it's a good bet we should have gotten it too.
69 * On SIGTERM, assume we have received a fast shutdown request, and exit
70 * cleanly. It's pure chance whether we receive the SIGTERM first, or the
71 * child process. If we receive it first, the signal handler will call
72 * proc_exit, otherwise we do it here. If we or the child process received
73 * SIGTERM for any other reason than a fast shutdown request, postmaster
74 * will perform an immediate shutdown when it sees us exiting
77 * We treat hard shell errors such as "command not found" as fatal, too.
79 ret
= ExecuteRecoveryCommand(cmd
, "restore_command",
80 true, /* failOnSignal */
81 true, /* exitOnSigterm */
82 WAIT_EVENT_RESTORE_COMMAND
, DEBUG2
);
89 * Attempt to execute a shell-based archive cleanup command.
92 shell_archive_cleanup(const char *lastRestartPointFileName
)
96 cmd
= replace_percent_placeholders(archiveCleanupCommand
,
97 "archive_cleanup_command",
98 "r", lastRestartPointFileName
);
99 (void) ExecuteRecoveryCommand(cmd
, "archive_cleanup_command", false, false,
100 WAIT_EVENT_ARCHIVE_CLEANUP_COMMAND
, WARNING
);
105 * Attempt to execute a shell-based end-of-recovery command.
108 shell_recovery_end(const char *lastRestartPointFileName
)
112 cmd
= replace_percent_placeholders(recoveryEndCommand
,
113 "recovery_end_command",
114 "r", lastRestartPointFileName
);
115 (void) ExecuteRecoveryCommand(cmd
, "recovery_end_command", true, false,
116 WAIT_EVENT_RECOVERY_END_COMMAND
, WARNING
);
121 * Attempt to execute an external shell command during recovery.
123 * 'command' is the shell command to be executed, 'commandName' is a
124 * human-readable name describing the command emitted in the logs. If
125 * 'failOnSignal' is true and the command is killed by a signal, a FATAL
126 * error is thrown. Otherwise, 'fail_elevel' is used for the log message.
127 * If 'exitOnSigterm' is true and the command is killed by SIGTERM, we exit
130 * Returns whether the command succeeded.
133 ExecuteRecoveryCommand(const char *command
, const char *commandName
,
134 bool failOnSignal
, bool exitOnSigterm
,
135 uint32 wait_event_info
, int fail_elevel
)
139 Assert(command
&& commandName
);
142 (errmsg_internal("executing %s \"%s\"", commandName
, command
)));
145 * execute the constructed command
148 pgstat_report_wait_start(wait_event_info
);
149 rc
= system(command
);
150 pgstat_report_wait_end();
154 if (exitOnSigterm
&& wait_result_is_signal(rc
, SIGTERM
))
158 * If the failure was due to any sort of signal, it's best to punt and
159 * abort recovery. See comments in shell_restore().
161 ereport((failOnSignal
&& wait_result_is_any_signal(rc
, true)) ? FATAL
: fail_elevel
,
163 translator: First %s represents a postgresql.conf parameter name like
164 "recovery_end_command", the 2nd is the value of that parameter, the
165 third an already translated error message. */
166 (errmsg("%s \"%s\": %s", commandName
,
167 command
, wait_result_to_str(rc
))));