Age Owner Branch data TLA 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-2026, 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 : : #include "utils/wait_event.h"
26 : :
27 : : static bool shell_archive_configured(ArchiveModuleState *state);
28 : : static bool shell_archive_file(ArchiveModuleState *state,
29 : : const char *file,
30 : : const char *path);
31 : : static void shell_archive_shutdown(ArchiveModuleState *state);
32 : :
33 : : static const ArchiveModuleCallbacks shell_archive_callbacks = {
34 : : .startup_cb = NULL,
35 : : .check_configured_cb = shell_archive_configured,
36 : : .archive_file_cb = shell_archive_file,
37 : : .shutdown_cb = shell_archive_shutdown
38 : : };
39 : :
40 : : const ArchiveModuleCallbacks *
1122 michael@paquier.xyz 41 :CBC 16 : shell_archive_init(void)
42 : : {
43 : 16 : return &shell_archive_callbacks;
44 : : }
45 : :
46 : : static bool
47 : 373 : shell_archive_configured(ArchiveModuleState *state)
48 : : {
741 nathan@postgresql.or 49 [ + + ]: 373 : if (XLogArchiveCommand[0] != '\0')
50 : 371 : return true;
51 : :
557 michael@paquier.xyz 52 : 2 : arch_module_check_errdetail("\"%s\" is not set.",
53 : : "archive_command");
741 nathan@postgresql.or 54 : 2 : return false;
55 : : }
56 : :
57 : : static bool
1122 michael@paquier.xyz 58 : 371 : shell_archive_file(ArchiveModuleState *state, const char *file,
59 : : const char *path)
60 : : {
61 : : char *xlogarchcmd;
1159 peter@eisentraut.org 62 : 371 : char *nativePath = NULL;
63 : : int rc;
64 : :
65 [ + - ]: 371 : if (path)
66 : : {
67 : 371 : nativePath = pstrdup(path);
68 : 371 : make_native_path(nativePath);
69 : : }
70 : :
1122 michael@paquier.xyz 71 : 371 : xlogarchcmd = replace_percent_placeholders(XLogArchiveCommand,
72 : : "archive_command", "fp",
73 : : file, nativePath);
74 : :
1507 rhaas@postgresql.org 75 [ - + ]: 371 : ereport(DEBUG3,
76 : : (errmsg_internal("executing archive command \"%s\"",
77 : : xlogarchcmd)));
78 : :
1294 tgl@sss.pgh.pa.us 79 : 371 : fflush(NULL);
1507 rhaas@postgresql.org 80 : 371 : pgstat_report_wait_start(WAIT_EVENT_ARCHIVE_COMMAND);
81 : 371 : rc = system(xlogarchcmd);
82 : 371 : pgstat_report_wait_end();
83 : :
84 [ + + ]: 371 : if (rc != 0)
85 : : {
86 : : /*
87 : : * If either the shell itself, or a called command, died on a signal,
88 : : * abort the archiver. We do this because system() ignores SIGINT and
89 : : * SIGQUIT while waiting; so a signal is very likely something that
90 : : * should have interrupted us too. Also die if the shell got a hard
91 : : * "command not found" type of error. If we overreact it's no big
92 : : * deal, the postmaster will just start the archiver again.
93 : : */
94 [ - + ]: 7 : int lev = wait_result_is_any_signal(rc, true) ? FATAL : LOG;
95 : :
96 [ + - ]: 7 : if (WIFEXITED(rc))
97 : : {
98 [ + - ]: 7 : ereport(lev,
99 : : (errmsg("archive command failed with exit code %d",
100 : : WEXITSTATUS(rc)),
101 : : errdetail("The failed archive command was: %s",
102 : : xlogarchcmd)));
103 : : }
1507 rhaas@postgresql.org 104 [ # # ]:UBC 0 : else if (WIFSIGNALED(rc))
105 : : {
106 : : #if defined(WIN32)
107 : : ereport(lev,
108 : : (errmsg("archive command was terminated by exception 0x%X",
109 : : WTERMSIG(rc)),
110 : : errhint("See C include file \"ntstatus.h\" for a description of the hexadecimal value."),
111 : : errdetail("The failed archive command was: %s",
112 : : xlogarchcmd)));
113 : : #else
114 [ # # ]: 0 : ereport(lev,
115 : : (errmsg("archive command was terminated by signal %d: %s",
116 : : WTERMSIG(rc), pg_strsignal(WTERMSIG(rc))),
117 : : errdetail("The failed archive command was: %s",
118 : : xlogarchcmd)));
119 : : #endif
120 : : }
121 : : else
122 : : {
123 [ # # ]: 0 : ereport(lev,
124 : : (errmsg("archive command exited with unrecognized status %d",
125 : : rc),
126 : : errdetail("The failed archive command was: %s",
127 : : xlogarchcmd)));
128 : : }
670 dgustafsson@postgres 129 :CBC 7 : pfree(xlogarchcmd);
130 : :
1507 rhaas@postgresql.org 131 : 7 : return false;
132 : : }
670 dgustafsson@postgres 133 : 364 : pfree(xlogarchcmd);
134 : :
1507 rhaas@postgresql.org 135 [ + + ]: 364 : elog(DEBUG1, "archived write-ahead log file \"%s\"", file);
136 : 364 : return true;
137 : : }
138 : :
139 : : static void
1122 michael@paquier.xyz 140 : 16 : shell_archive_shutdown(ArchiveModuleState *state)
141 : : {
1243 142 [ + + ]: 16 : elog(DEBUG1, "archiver process shutting down");
143 : 16 : }
|