Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * basebackup_target.c
4 : : * Base backups can be "targeted", which means that they can be sent
5 : : * somewhere other than to the client which requested the backup.
6 : : * Furthermore, new targets can be defined by extensions. This file
7 : : * contains code to support that functionality.
8 : : *
9 : : * Portions Copyright (c) 2010-2024, PostgreSQL Global Development Group
10 : : *
11 : : * IDENTIFICATION
12 : : * src/backend/backup/basebackup_target.c
13 : : *
14 : : *-------------------------------------------------------------------------
15 : : */
16 : : #include "postgres.h"
17 : :
18 : : #include "backup/basebackup_target.h"
19 : : #include "utils/memutils.h"
20 : :
21 : : typedef struct BaseBackupTargetType
22 : : {
23 : : char *name;
24 : : void *(*check_detail) (char *, char *);
25 : : bbsink *(*get_sink) (bbsink *, void *);
26 : : } BaseBackupTargetType;
27 : :
28 : : struct BaseBackupTargetHandle
29 : : {
30 : : BaseBackupTargetType *type;
31 : : void *detail_arg;
32 : : };
33 : :
34 : : static void initialize_target_list(void);
35 : : static bbsink *blackhole_get_sink(bbsink *next_sink, void *detail_arg);
36 : : static bbsink *server_get_sink(bbsink *next_sink, void *detail_arg);
37 : : static void *reject_target_detail(char *target, char *target_detail);
38 : : static void *server_check_detail(char *target, char *target_detail);
39 : :
40 : : static BaseBackupTargetType builtin_backup_targets[] =
41 : : {
42 : : {
43 : : "blackhole", reject_target_detail, blackhole_get_sink
44 : : },
45 : : {
46 : : "server", server_check_detail, server_get_sink
47 : : },
48 : : {
49 : : NULL
50 : : }
51 : : };
52 : :
53 : : static List *BaseBackupTargetTypeList = NIL;
54 : :
55 : : /*
56 : : * Add a new base backup target type.
57 : : *
58 : : * This is intended for use by server extensions.
59 : : */
60 : : void
761 rhaas@postgresql.org 61 :CBC 1 : BaseBackupAddTarget(char *name,
62 : : void *(*check_detail) (char *, char *),
63 : : bbsink *(*get_sink) (bbsink *, void *))
64 : : {
65 : : BaseBackupTargetType *newtype;
66 : : MemoryContext oldcontext;
67 : : ListCell *lc;
68 : :
69 : : /* If the target list is not yet initialized, do that first. */
70 [ + - ]: 1 : if (BaseBackupTargetTypeList == NIL)
71 : 1 : initialize_target_list();
72 : :
73 : : /* Search the target type list for an existing entry with this name. */
74 [ + - + + : 3 : foreach(lc, BaseBackupTargetTypeList)
+ + ]
75 : : {
76 : 2 : BaseBackupTargetType *ttype = lfirst(lc);
77 : :
78 [ - + ]: 2 : if (strcmp(ttype->name, name) == 0)
79 : : {
80 : : /*
81 : : * We found one, so update it.
82 : : *
83 : : * It is probably not a great idea to call BaseBackupAddTarget for
84 : : * the same name multiple times, but if it happens, this seems
85 : : * like the sanest behavior.
86 : : */
761 rhaas@postgresql.org 87 :UBC 0 : ttype->check_detail = check_detail;
88 : 0 : ttype->get_sink = get_sink;
89 : 0 : return;
90 : : }
91 : : }
92 : :
93 : : /*
94 : : * We use TopMemoryContext for allocations here to make sure that the data
95 : : * we need doesn't vanish under us; that's also why we copy the target
96 : : * name into a newly-allocated chunk of memory.
97 : : */
761 rhaas@postgresql.org 98 :CBC 1 : oldcontext = MemoryContextSwitchTo(TopMemoryContext);
603 drowley@postgresql.o 99 : 1 : newtype = palloc(sizeof(BaseBackupTargetType));
100 : 1 : newtype->name = pstrdup(name);
101 : 1 : newtype->check_detail = check_detail;
102 : 1 : newtype->get_sink = get_sink;
103 : 1 : BaseBackupTargetTypeList = lappend(BaseBackupTargetTypeList, newtype);
761 rhaas@postgresql.org 104 : 1 : MemoryContextSwitchTo(oldcontext);
105 : : }
106 : :
107 : : /*
108 : : * Look up a base backup target and validate the target_detail.
109 : : *
110 : : * Extensions that define new backup targets will probably define a new
111 : : * type of bbsink to match. Validation of the target_detail can be performed
112 : : * either in the check_detail routine called here, or in the bbsink
113 : : * constructor, which will be called from BaseBackupGetSink. It's mostly
114 : : * a matter of taste, but the check_detail function runs somewhat earlier.
115 : : */
116 : : BaseBackupTargetHandle *
117 : 15 : BaseBackupGetTargetHandle(char *target, char *target_detail)
118 : : {
119 : : ListCell *lc;
120 : :
121 : : /* If the target list is not yet initialized, do that first. */
122 [ + + ]: 15 : if (BaseBackupTargetTypeList == NIL)
123 : 9 : initialize_target_list();
124 : :
125 : : /* Search the target type list for a match. */
126 [ + - + + : 36 : foreach(lc, BaseBackupTargetTypeList)
+ + ]
127 : : {
128 : 35 : BaseBackupTargetType *ttype = lfirst(lc);
129 : :
130 [ + + ]: 35 : if (strcmp(ttype->name, target) == 0)
131 : : {
132 : : BaseBackupTargetHandle *handle;
133 : :
134 : : /* Found the target. */
135 : 14 : handle = palloc(sizeof(BaseBackupTargetHandle));
136 : 14 : handle->type = ttype;
137 : 14 : handle->detail_arg = ttype->check_detail(target, target_detail);
138 : :
139 : 13 : return handle;
140 : : }
141 : : }
142 : :
143 : : /* Did not find the target. */
144 [ + - ]: 1 : ereport(ERROR,
145 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
146 : : errmsg("unrecognized target: \"%s\"", target)));
147 : :
148 : : /* keep compiler quiet */
149 : : return NULL;
150 : : }
151 : :
152 : : /*
153 : : * Construct a bbsink that will implement the backup target.
154 : : *
155 : : * The get_sink function does all the real work, so all we have to do here
156 : : * is call it with the correct arguments. Whatever the check_detail function
157 : : * returned is here passed through to the get_sink function. This lets those
158 : : * two functions communicate with each other, if they wish. If not, the
159 : : * check_detail function can simply return the target_detail and let the
160 : : * get_sink function take it from there.
161 : : */
162 : : bbsink *
163 : 13 : BaseBackupGetSink(BaseBackupTargetHandle *handle, bbsink *next_sink)
164 : : {
165 : 13 : return handle->type->get_sink(next_sink, handle->detail_arg);
166 : : }
167 : :
168 : : /*
169 : : * Load predefined target types into BaseBackupTargetTypeList.
170 : : */
171 : : static void
172 : 10 : initialize_target_list(void)
173 : : {
174 : 10 : BaseBackupTargetType *ttype = builtin_backup_targets;
175 : : MemoryContext oldcontext;
176 : :
177 : 10 : oldcontext = MemoryContextSwitchTo(TopMemoryContext);
178 [ + + ]: 30 : while (ttype->name != NULL)
179 : : {
180 : 20 : BaseBackupTargetTypeList = lappend(BaseBackupTargetTypeList, ttype);
181 : 20 : ++ttype;
182 : : }
183 : 10 : MemoryContextSwitchTo(oldcontext);
184 : 10 : }
185 : :
186 : : /*
187 : : * Normally, a get_sink function should construct and return a new bbsink that
188 : : * implements the backup target, but the 'blackhole' target just throws the
189 : : * data away. We could implement that by adding a bbsink that does nothing
190 : : * but forward, but it's even cheaper to implement that by not adding a bbsink
191 : : * at all.
192 : : */
193 : : static bbsink *
194 : 1 : blackhole_get_sink(bbsink *next_sink, void *detail_arg)
195 : : {
196 : 1 : return next_sink;
197 : : }
198 : :
199 : : /*
200 : : * Create a bbsink implementing a server-side backup.
201 : : */
202 : : static bbsink *
203 : 7 : server_get_sink(bbsink *next_sink, void *detail_arg)
204 : : {
205 : 7 : return bbsink_server_new(next_sink, detail_arg);
206 : : }
207 : :
208 : : /*
209 : : * Implement target-detail checking for a target that does not accept a
210 : : * detail.
211 : : */
212 : : static void *
213 : 1 : reject_target_detail(char *target, char *target_detail)
214 : : {
215 [ - + ]: 1 : if (target_detail != NULL)
761 rhaas@postgresql.org 216 [ # # ]:UBC 0 : ereport(ERROR,
217 : : (errcode(ERRCODE_SYNTAX_ERROR),
218 : : errmsg("target \"%s\" does not accept a target detail",
219 : : target)));
220 : :
761 rhaas@postgresql.org 221 :CBC 1 : return NULL;
222 : : }
223 : :
224 : : /*
225 : : * Implement target-detail checking for a server-side backup.
226 : : *
227 : : * target_detail should be the name of the directory to which the backup
228 : : * should be written, but we don't check that here. Rather, that check,
229 : : * as well as the necessary permissions checking, happens in bbsink_server_new.
230 : : */
231 : : static void *
232 : 7 : server_check_detail(char *target, char *target_detail)
233 : : {
234 [ - + ]: 7 : if (target_detail == NULL)
761 rhaas@postgresql.org 235 [ # # ]:UBC 0 : ereport(ERROR,
236 : : (errcode(ERRCODE_SYNTAX_ERROR),
237 : : errmsg("target \"%s\" requires a target detail",
238 : : target)));
239 : :
761 rhaas@postgresql.org 240 :CBC 7 : return target_detail;
241 : : }
|