Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * shell_archive.c
4 : *
5 : * This archiving function uses a user-specified shell command (the
6 : * archive_command GUC) to copy write-ahead log files. It is used as the
7 : * default, but other modules may define their own custom archiving logic.
8 : *
9 : * Copyright (c) 2022-2024, PostgreSQL Global Development Group
10 : *
11 : * IDENTIFICATION
12 : * src/backend/archive/shell_archive.c
13 : *
14 : *-------------------------------------------------------------------------
15 : */
16 : #include "postgres.h"
17 :
18 : #include <sys/wait.h>
19 :
20 : #include "access/xlog.h"
21 : #include "archive/archive_module.h"
22 : #include "archive/shell_archive.h"
23 : #include "common/percentrepl.h"
24 : #include "pgstat.h"
25 :
26 : static bool shell_archive_configured(ArchiveModuleState *state);
27 : static bool shell_archive_file(ArchiveModuleState *state,
28 : const char *file,
29 : const char *path);
30 : static void shell_archive_shutdown(ArchiveModuleState *state);
31 :
32 : static const ArchiveModuleCallbacks shell_archive_callbacks = {
33 : .startup_cb = NULL,
34 : .check_configured_cb = shell_archive_configured,
35 : .archive_file_cb = shell_archive_file,
36 : .shutdown_cb = shell_archive_shutdown
37 : };
38 :
39 : const ArchiveModuleCallbacks *
40 26 : shell_archive_init(void)
41 : {
42 26 : return &shell_archive_callbacks;
43 : }
44 :
45 : static bool
46 86 : shell_archive_configured(ArchiveModuleState *state)
47 : {
48 86 : if (XLogArchiveCommand[0] != '\0')
49 82 : return true;
50 :
51 4 : arch_module_check_errdetail("\"%s\" is not set.",
52 : "archive_command");
53 4 : return false;
54 : }
55 :
56 : static bool
57 82 : shell_archive_file(ArchiveModuleState *state, const char *file,
58 : const char *path)
59 : {
60 : char *xlogarchcmd;
61 82 : char *nativePath = NULL;
62 : int rc;
63 :
64 82 : if (path)
65 : {
66 82 : nativePath = pstrdup(path);
67 82 : make_native_path(nativePath);
68 : }
69 :
70 82 : xlogarchcmd = replace_percent_placeholders(XLogArchiveCommand,
71 : "archive_command", "fp",
72 : file, nativePath);
73 :
74 82 : ereport(DEBUG3,
75 : (errmsg_internal("executing archive command \"%s\"",
76 : xlogarchcmd)));
77 :
78 82 : fflush(NULL);
79 82 : pgstat_report_wait_start(WAIT_EVENT_ARCHIVE_COMMAND);
80 82 : rc = system(xlogarchcmd);
81 82 : pgstat_report_wait_end();
82 :
83 82 : if (rc != 0)
84 : {
85 : /*
86 : * If either the shell itself, or a called command, died on a signal,
87 : * abort the archiver. We do this because system() ignores SIGINT and
88 : * SIGQUIT while waiting; so a signal is very likely something that
89 : * should have interrupted us too. Also die if the shell got a hard
90 : * "command not found" type of error. If we overreact it's no big
91 : * deal, the postmaster will just start the archiver again.
92 : */
93 14 : int lev = wait_result_is_any_signal(rc, true) ? FATAL : LOG;
94 :
95 14 : if (WIFEXITED(rc))
96 : {
97 14 : ereport(lev,
98 : (errmsg("archive command failed with exit code %d",
99 : WEXITSTATUS(rc)),
100 : errdetail("The failed archive command was: %s",
101 : xlogarchcmd)));
102 : }
103 0 : else if (WIFSIGNALED(rc))
104 : {
105 : #if defined(WIN32)
106 : ereport(lev,
107 : (errmsg("archive command was terminated by exception 0x%X",
108 : WTERMSIG(rc)),
109 : errhint("See C include file \"ntstatus.h\" for a description of the hexadecimal value."),
110 : errdetail("The failed archive command was: %s",
111 : xlogarchcmd)));
112 : #else
113 0 : ereport(lev,
114 : (errmsg("archive command was terminated by signal %d: %s",
115 : WTERMSIG(rc), pg_strsignal(WTERMSIG(rc))),
116 : errdetail("The failed archive command was: %s",
117 : xlogarchcmd)));
118 : #endif
119 : }
120 : else
121 : {
122 0 : ereport(lev,
123 : (errmsg("archive command exited with unrecognized status %d",
124 : rc),
125 : errdetail("The failed archive command was: %s",
126 : xlogarchcmd)));
127 : }
128 14 : pfree(xlogarchcmd);
129 :
130 14 : return false;
131 : }
132 68 : pfree(xlogarchcmd);
133 :
134 68 : elog(DEBUG1, "archived write-ahead log file \"%s\"", file);
135 68 : return true;
136 : }
137 :
138 : static void
139 26 : shell_archive_shutdown(ArchiveModuleState *state)
140 : {
141 26 : elog(DEBUG1, "archiver process shutting down");
142 26 : }
|