PostgreSQL Source Code  git master
basebackup_to_shell.c File Reference
#include "postgres.h"
#include "access/xact.h"
#include "miscadmin.h"
#include "replication/basebackup_target.h"
#include "storage/fd.h"
#include "utils/acl.h"
#include "utils/guc.h"
Include dependency graph for basebackup_to_shell.c:

Go to the source code of this file.

Data Structures

struct  bbsink_shell
 

Typedefs

typedef struct bbsink_shell bbsink_shell
 

Functions

void _PG_init (void)
 
static void * shell_check_detail (char *target, char *target_detail)
 
static bbsinkshell_get_sink (bbsink *next_sink, void *detail_arg)
 
static void bbsink_shell_begin_archive (bbsink *sink, const char *archive_name)
 
static void bbsink_shell_archive_contents (bbsink *sink, size_t len)
 
static void bbsink_shell_end_archive (bbsink *sink)
 
static void bbsink_shell_begin_manifest (bbsink *sink)
 
static void bbsink_shell_manifest_contents (bbsink *sink, size_t len)
 
static void bbsink_shell_end_manifest (bbsink *sink)
 
static char * shell_construct_command (char *base_command, const char *filename, char *target_detail)
 
static void shell_finish_command (bbsink_shell *sink)
 
static void shell_run_command (bbsink_shell *sink, const char *filename)
 
static void shell_send_data (bbsink_shell *sink, size_t len)
 

Variables

 PG_MODULE_MAGIC
 
static const bbsink_ops bbsink_shell_ops
 
static char * shell_command = ""
 
static char * shell_required_role = ""
 

Typedef Documentation

◆ bbsink_shell

typedef struct bbsink_shell bbsink_shell

Function Documentation

◆ _PG_init()

void _PG_init ( void  )

Definition at line 69 of file basebackup_to_shell.c.

70 {
71  DefineCustomStringVariable("basebackup_to_shell.command",
72  "Shell command to be executed for each backup file.",
73  NULL,
75  "",
76  PGC_SIGHUP,
77  0,
78  NULL, NULL, NULL);
79 
80  DefineCustomStringVariable("basebackup_to_shell.required_role",
81  "Backup user must be a member of this role to use shell backup target.",
82  NULL,
84  "",
85  PGC_SIGHUP,
86  0,
87  NULL, NULL, NULL);
88 
89  MarkGUCPrefixReserved("basebackup_to_shell");
90 
92 }
void BaseBackupAddTarget(char *name, void *(*check_detail)(char *, char *), bbsink *(*get_sink)(bbsink *, void *))
static void * shell_check_detail(char *target, char *target_detail)
static char * shell_required_role
static bbsink * shell_get_sink(bbsink *next_sink, void *detail_arg)
static char * shell_command
void DefineCustomStringVariable(const char *name, const char *short_desc, const char *long_desc, char **valueAddr, const char *bootValue, GucContext context, int flags, GucStringCheckHook check_hook, GucStringAssignHook assign_hook, GucShowHook show_hook)
Definition: guc.c:9568
void MarkGUCPrefixReserved(const char *className)
Definition: guc.c:9629
@ PGC_SIGHUP
Definition: guc.h:72

References BaseBackupAddTarget(), DefineCustomStringVariable(), MarkGUCPrefixReserved(), PGC_SIGHUP, shell_check_detail(), shell_command, shell_get_sink(), and shell_required_role.

◆ bbsink_shell_archive_contents()

static void bbsink_shell_archive_contents ( bbsink sink,
size_t  len 
)
static

Definition at line 366 of file basebackup_to_shell.c.

367 {
368  bbsink_shell *mysink = (bbsink_shell *) sink;
369 
370  shell_send_data(mysink, len);
372 }
void bbsink_forward_archive_contents(bbsink *sink, size_t len)
static void shell_send_data(bbsink_shell *sink, size_t len)
const void size_t len

References bbsink_forward_archive_contents(), len, and shell_send_data().

◆ bbsink_shell_begin_archive()

static void bbsink_shell_begin_archive ( bbsink sink,
const char *  archive_name 
)
static

Definition at line 354 of file basebackup_to_shell.c.

355 {
356  bbsink_shell *mysink = (bbsink_shell *) sink;
357 
358  shell_run_command(mysink, archive_name);
359  bbsink_forward_begin_archive(sink, archive_name);
360 }
void bbsink_forward_begin_archive(bbsink *sink, const char *archive_name)
static void shell_run_command(bbsink_shell *sink, const char *filename)

References bbsink_forward_begin_archive(), and shell_run_command().

◆ bbsink_shell_begin_manifest()

static void bbsink_shell_begin_manifest ( bbsink sink)
static

Definition at line 390 of file basebackup_to_shell.c.

391 {
392  bbsink_shell *mysink = (bbsink_shell *) sink;
393 
394  shell_run_command(mysink, "backup_manifest");
396 }
void bbsink_forward_begin_manifest(bbsink *sink)

References bbsink_forward_begin_manifest(), and shell_run_command().

◆ bbsink_shell_end_archive()

static void bbsink_shell_end_archive ( bbsink sink)
static

Definition at line 378 of file basebackup_to_shell.c.

379 {
380  bbsink_shell *mysink = (bbsink_shell *) sink;
381 
382  shell_finish_command(mysink);
384 }
void bbsink_forward_end_archive(bbsink *sink)
static void shell_finish_command(bbsink_shell *sink)

References bbsink_forward_end_archive(), and shell_finish_command().

◆ bbsink_shell_end_manifest()

static void bbsink_shell_end_manifest ( bbsink sink)
static

Definition at line 414 of file basebackup_to_shell.c.

415 {
416  bbsink_shell *mysink = (bbsink_shell *) sink;
417 
418  shell_finish_command(mysink);
420 }
void bbsink_forward_end_manifest(bbsink *sink)

References bbsink_forward_end_manifest(), and shell_finish_command().

◆ bbsink_shell_manifest_contents()

static void bbsink_shell_manifest_contents ( bbsink sink,
size_t  len 
)
static

Definition at line 402 of file basebackup_to_shell.c.

403 {
404  bbsink_shell *mysink = (bbsink_shell *) sink;
405 
406  shell_send_data(mysink, len);
408 }
void bbsink_forward_manifest_contents(bbsink *sink, size_t len)

References bbsink_forward_manifest_contents(), len, and shell_send_data().

◆ shell_check_detail()

static void * shell_check_detail ( char *  target,
char *  target_detail 
)
static

Definition at line 100 of file basebackup_to_shell.c.

101 {
102  if (shell_required_role[0] != '\0')
103  {
104  Oid roleid;
105 
107  roleid = get_role_oid(shell_required_role, true);
108  if (!has_privs_of_role(GetUserId(), roleid))
109  ereport(ERROR,
110  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
111  errmsg("permission denied to use basebackup_to_shell")));
113  }
114 
115  return target_detail;
116 }
bool has_privs_of_role(Oid member, Oid role)
Definition: acl.c:4951
Oid get_role_oid(const char *rolname, bool missing_ok)
Definition: acl.c:5177
int errcode(int sqlerrcode)
Definition: elog.c:693
int errmsg(const char *fmt,...)
Definition: elog.c:904
#define ERROR
Definition: elog.h:33
#define ereport(elevel,...)
Definition: elog.h:143
Oid GetUserId(void)
Definition: miscinit.c:492
unsigned int Oid
Definition: postgres_ext.h:31
void StartTransactionCommand(void)
Definition: xact.c:2925
void CommitTransactionCommand(void)
Definition: xact.c:3022

References CommitTransactionCommand(), ereport, errcode(), errmsg(), ERROR, get_role_oid(), GetUserId(), has_privs_of_role(), shell_required_role, and StartTransactionCommand().

Referenced by _PG_init().

◆ shell_construct_command()

static char* shell_construct_command ( char *  base_command,
const char *  filename,
char *  target_detail 
)
static

Definition at line 210 of file basebackup_to_shell.c.

212 {
214  char *c;
215 
217  for (c = base_command; *c != '\0'; ++c)
218  {
219  /* Anything other than '%' is copied verbatim. */
220  if (*c != '%')
221  {
223  continue;
224  }
225 
226  /* Any time we see '%' we eat the following character as well. */
227  ++c;
228 
229  /*
230  * The following character determines what we insert here, or may
231  * cause us to throw an error.
232  */
233  if (*c == '%')
234  {
235  /* '%%' is replaced by a single '%' */
236  appendStringInfoChar(&buf, '%');
237  }
238  else if (*c == 'f')
239  {
240  /* '%f' is replaced by the filename */
242  }
243  else if (*c == 'd')
244  {
245  /* '%d' is replaced by the target detail */
246  appendStringInfoString(&buf, target_detail);
247  }
248  else if (*c == '\0')
249  {
250  /* Incomplete escape sequence, expected a character afterward */
251  ereport(ERROR,
252  errcode(ERRCODE_SYNTAX_ERROR),
253  errmsg("shell command ends unexpectedly after escape character \"%%\""));
254  }
255  else
256  {
257  /* Unknown escape sequence */
258  ereport(ERROR,
259  errcode(ERRCODE_SYNTAX_ERROR),
260  errmsg("shell command contains unexpected escape sequence \"%c\"",
261  *c));
262  }
263  }
264 
265  return buf.data;
266 }
static char * filename
Definition: pg_dumpall.c:94
static char * buf
Definition: pg_test_fsync.c:67
char * c
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:176
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:188
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59

References appendStringInfoChar(), appendStringInfoString(), buf, ereport, errcode(), errmsg(), ERROR, filename, and initStringInfo().

Referenced by shell_run_command().

◆ shell_finish_command()

static void shell_finish_command ( bbsink_shell sink)
static

Definition at line 272 of file basebackup_to_shell.c.

273 {
274  int pclose_rc;
275 
276  /* There should be a command running. */
277  Assert(sink->current_command != NULL);
278  Assert(sink->pipe != NULL);
279 
280  /* Close down the pipe we opened. */
281  pclose_rc = ClosePipeStream(sink->pipe);
282  if (pclose_rc == -1)
283  ereport(ERROR,
285  errmsg("could not close pipe to external command: %m")));
286  else if (pclose_rc != 0)
287  {
288  ereport(ERROR,
289  (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
290  errmsg("shell command \"%s\" failed",
291  sink->current_command),
292  errdetail_internal("%s", wait_result_to_str(pclose_rc))));
293  }
294 
295  /* Clean up. */
296  sink->pipe = NULL;
297  pfree(sink->current_command);
298  sink->current_command = NULL;
299 }
int errdetail_internal(const char *fmt,...)
Definition: elog.c:1064
int errcode_for_file_access(void)
Definition: elog.c:716
int ClosePipeStream(FILE *file)
Definition: fd.c:2870
Assert(fmt[strlen(fmt) - 1] !='\n')
void pfree(void *pointer)
Definition: mcxt.c:1175
char * wait_result_to_str(int exitstatus)
Definition: wait_error.c:32

References Assert(), ClosePipeStream(), bbsink_shell::current_command, ereport, errcode(), errcode_for_file_access(), errdetail_internal(), errmsg(), ERROR, pfree(), bbsink_shell::pipe, and wait_result_to_str().

Referenced by bbsink_shell_end_archive(), bbsink_shell_end_manifest(), and shell_send_data().

◆ shell_get_sink()

static bbsink * shell_get_sink ( bbsink next_sink,
void *  detail_arg 
)
static

Definition at line 125 of file basebackup_to_shell.c.

126 {
127  bbsink_shell *sink;
128  bool has_detail_escape = false;
129  char *c;
130 
131  /*
132  * Set up the bbsink.
133  *
134  * We remember the current value of basebackup_to_shell.shell_command to
135  * be certain that it can't change under us during the backup.
136  */
137  sink = palloc0(sizeof(bbsink_shell));
138  *((const bbsink_ops **) &sink->base.bbs_ops) = &bbsink_shell_ops;
139  sink->base.bbs_next = next_sink;
140  sink->target_detail = detail_arg;
142 
143  /* Reject an empty shell command. */
144  if (sink->shell_command[0] == '\0')
145  ereport(ERROR,
146  errcode(ERRCODE_INVALID_PARAMETER_VALUE),
147  errmsg("shell command for backup is not configured"));
148 
149  /* Determine whether the shell command we're using contains %d. */
150  for (c = sink->shell_command; *c != '\0'; ++c)
151  {
152  if (c[0] == '%' && c[1] != '\0')
153  {
154  if (c[1] == 'd')
155  has_detail_escape = true;
156  ++c;
157  }
158  }
159 
160  /* There should be a target detail if %d was used, and not otherwise. */
161  if (has_detail_escape && sink->target_detail == NULL)
162  ereport(ERROR,
163  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
164  errmsg("a target detail is required because the configured command includes %%d"),
165  errhint("Try \"pg_basebackup --target shell:DETAIL ...\"")));
166  else if (!has_detail_escape && sink->target_detail != NULL)
167  ereport(ERROR,
168  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
169  errmsg("a target detail is not permitted because the configured command does not include %%d")));
170 
171  /*
172  * Since we're passing the string provided by the user to popen(), it will
173  * be interpreted by the shell, which is a potential security
174  * vulnerability, since the user invoking this module is not necessarily a
175  * superuser. To stay out of trouble, we must disallow any shell
176  * metacharacters here; to be conservative and keep things simple, we
177  * allow only alphanumerics.
178  */
179  if (sink->target_detail != NULL)
180  {
181  char *d;
182  bool scary = false;
183 
184  for (d = sink->target_detail; *d != '\0'; ++d)
185  {
186  if (*d >= 'a' && *d <= 'z')
187  continue;
188  if (*d >= 'A' && *d <= 'Z')
189  continue;
190  if (*d >= '0' && *d <= '9')
191  continue;
192  scary = true;
193  break;
194  }
195 
196  if (scary)
197  ereport(ERROR,
198  errcode(ERRCODE_INVALID_PARAMETER_VALUE),
199  errmsg("target detail must contain only alphanumeric characters"));
200  }
201 
202  return &sink->base;
203 }
static const bbsink_ops bbsink_shell_ops
int errhint(const char *fmt,...)
Definition: elog.c:1151
char * pstrdup(const char *in)
Definition: mcxt.c:1305
void * palloc0(Size size)
Definition: mcxt.c:1099
bbsink * bbs_next
const bbsink_ops * bbs_ops

References bbsink_shell::base, bbsink::bbs_next, bbsink::bbs_ops, bbsink_shell_ops, ereport, errcode(), errhint(), errmsg(), ERROR, palloc0(), pstrdup(), bbsink_shell::shell_command, shell_command, and bbsink_shell::target_detail.

Referenced by _PG_init().

◆ shell_run_command()

static void shell_run_command ( bbsink_shell sink,
const char *  filename 
)
static

Definition at line 305 of file basebackup_to_shell.c.

306 {
307  /* There should not be anything already running. */
308  Assert(sink->current_command == NULL);
309  Assert(sink->pipe == NULL);
310 
311  /* Construct a suitable command. */
313  filename,
314  sink->target_detail);
315 
316  /* Run it. */
318 }
static char * shell_construct_command(char *base_command, const char *filename, char *target_detail)
#define PG_BINARY_W
Definition: c.h:1271
FILE * OpenPipeStream(const char *command, const char *mode)
Definition: fd.c:2564

References Assert(), bbsink_shell::current_command, filename, OpenPipeStream(), PG_BINARY_W, bbsink_shell::pipe, bbsink_shell::shell_command, shell_construct_command(), and bbsink_shell::target_detail.

Referenced by bbsink_shell_begin_archive(), and bbsink_shell_begin_manifest().

◆ shell_send_data()

static void shell_send_data ( bbsink_shell sink,
size_t  len 
)
static

Definition at line 324 of file basebackup_to_shell.c.

325 {
326  /* There should be a command running. */
327  Assert(sink->current_command != NULL);
328  Assert(sink->pipe != NULL);
329 
330  /* Try to write the data. */
331  if (fwrite(sink->base.bbs_buffer, len, 1, sink->pipe) != 1 ||
332  ferror(sink->pipe))
333  {
334  if (errno == EPIPE)
335  {
336  /*
337  * The error we're about to throw would shut down the command
338  * anyway, but we may get a more meaningful error message by doing
339  * this. If not, we'll fall through to the generic error below.
340  */
341  shell_finish_command(sink);
342  errno = EPIPE;
343  }
344  ereport(ERROR,
346  errmsg("could not write to shell backup program: %m")));
347  }
348 }
char * bbs_buffer

References Assert(), bbsink_shell::base, bbsink::bbs_buffer, bbsink_shell::current_command, ereport, errcode_for_file_access(), errmsg(), ERROR, len, bbsink_shell::pipe, and shell_finish_command().

Referenced by bbsink_shell_archive_contents(), and bbsink_shell_manifest_contents().

Variable Documentation

◆ bbsink_shell_ops

const bbsink_ops bbsink_shell_ops
static
Initial value:
= {
.begin_backup = bbsink_forward_begin_backup,
.begin_archive = bbsink_shell_begin_archive,
.archive_contents = bbsink_shell_archive_contents,
.end_archive = bbsink_shell_end_archive,
.begin_manifest = bbsink_shell_begin_manifest,
.manifest_contents = bbsink_shell_manifest_contents,
.end_manifest = bbsink_shell_end_manifest,
}
void bbsink_forward_begin_backup(bbsink *sink)
void bbsink_forward_end_backup(bbsink *sink, XLogRecPtr endptr, TimeLineID endtli)
void bbsink_forward_cleanup(bbsink *sink)
static void bbsink_shell_begin_manifest(bbsink *sink)
static void bbsink_shell_end_manifest(bbsink *sink)
static void bbsink_shell_end_archive(bbsink *sink)
static void bbsink_shell_begin_archive(bbsink *sink, const char *archive_name)
static void bbsink_shell_archive_contents(bbsink *sink, size_t len)
static void bbsink_shell_manifest_contents(bbsink *sink, size_t len)

Definition at line 53 of file basebackup_to_shell.c.

Referenced by shell_get_sink().

◆ PG_MODULE_MAGIC

PG_MODULE_MAGIC

Definition at line 20 of file basebackup_to_shell.c.

◆ shell_command

char* shell_command = ""
static

Definition at line 65 of file basebackup_to_shell.c.

Referenced by _PG_init(), and shell_get_sink().

◆ shell_required_role

char* shell_required_role = ""
static

Definition at line 66 of file basebackup_to_shell.c.

Referenced by _PG_init(), and shell_check_detail().