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-2025, 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 *
932 michael@paquier.xyz 40 :CBC 12 : shell_archive_init(void)
41 : : {
42 : 12 : return &shell_archive_callbacks;
43 : : }
44 : :
45 : : static bool
46 : 360 : shell_archive_configured(ArchiveModuleState *state)
47 : : {
551 nathan@postgresql.or 48 [ + + ]: 360 : if (XLogArchiveCommand[0] != '\0')
49 : 358 : return true;
50 : :
367 michael@paquier.xyz 51 : 2 : arch_module_check_errdetail("\"%s\" is not set.",
52 : : "archive_command");
551 nathan@postgresql.or 53 : 2 : return false;
54 : : }
55 : :
56 : : static bool
932 michael@paquier.xyz 57 : 358 : shell_archive_file(ArchiveModuleState *state, const char *file,
58 : : const char *path)
59 : : {
60 : : char *xlogarchcmd;
969 peter@eisentraut.org 61 : 358 : char *nativePath = NULL;
62 : : int rc;
63 : :
64 [ + - ]: 358 : if (path)
65 : : {
66 : 358 : nativePath = pstrdup(path);
67 : 358 : make_native_path(nativePath);
68 : : }
69 : :
932 michael@paquier.xyz 70 : 358 : xlogarchcmd = replace_percent_placeholders(XLogArchiveCommand,
71 : : "archive_command", "fp",
72 : : file, nativePath);
73 : :
1317 rhaas@postgresql.org 74 [ - + ]: 358 : ereport(DEBUG3,
75 : : (errmsg_internal("executing archive command \"%s\"",
76 : : xlogarchcmd)));
77 : :
1104 tgl@sss.pgh.pa.us 78 : 358 : fflush(NULL);
1317 rhaas@postgresql.org 79 : 358 : pgstat_report_wait_start(WAIT_EVENT_ARCHIVE_COMMAND);
80 : 358 : rc = system(xlogarchcmd);
81 : 358 : pgstat_report_wait_end();
82 : :
83 [ + + ]: 358 : 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 [ - + ]: 7 : int lev = wait_result_is_any_signal(rc, true) ? FATAL : LOG;
94 : :
95 [ + - ]: 7 : if (WIFEXITED(rc))
96 : : {
97 [ + - ]: 7 : 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 : : }
1317 rhaas@postgresql.org 103 [ # # ]:UBC 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 : : }
480 dgustafsson@postgres 128 :CBC 7 : pfree(xlogarchcmd);
129 : :
1317 rhaas@postgresql.org 130 : 7 : return false;
131 : : }
480 dgustafsson@postgres 132 : 351 : pfree(xlogarchcmd);
133 : :
1317 rhaas@postgresql.org 134 [ + + ]: 351 : elog(DEBUG1, "archived write-ahead log file \"%s\"", file);
135 : 351 : return true;
136 : : }
137 : :
138 : : static void
932 michael@paquier.xyz 139 : 12 : shell_archive_shutdown(ArchiveModuleState *state)
140 : : {
1053 141 [ + + ]: 12 : elog(DEBUG1, "archiver process shutting down");
142 : 12 : }
|