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