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
61 2 : 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 2 : if (BaseBackupTargetTypeList == NIL)
71 2 : initialize_target_list();
72 :
73 : /* Search the target type list for an existing entry with this name. */
74 6 : foreach(lc, BaseBackupTargetTypeList)
75 : {
76 4 : BaseBackupTargetType *ttype = lfirst(lc);
77 :
78 4 : 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 : */
87 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 : */
98 2 : oldcontext = MemoryContextSwitchTo(TopMemoryContext);
99 2 : newtype = palloc(sizeof(BaseBackupTargetType));
100 2 : newtype->name = pstrdup(name);
101 2 : newtype->check_detail = check_detail;
102 2 : newtype->get_sink = get_sink;
103 2 : BaseBackupTargetTypeList = lappend(BaseBackupTargetTypeList, newtype);
104 2 : 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 26 : BaseBackupGetTargetHandle(char *target, char *target_detail)
118 : {
119 : ListCell *lc;
120 :
121 : /* If the target list is not yet initialized, do that first. */
122 26 : if (BaseBackupTargetTypeList == NIL)
123 14 : initialize_target_list();
124 :
125 : /* Search the target type list for a match. */
126 64 : foreach(lc, BaseBackupTargetTypeList)
127 : {
128 62 : BaseBackupTargetType *ttype = lfirst(lc);
129 :
130 62 : if (strcmp(ttype->name, target) == 0)
131 : {
132 : BaseBackupTargetHandle *handle;
133 :
134 : /* Found the target. */
135 24 : handle = palloc(sizeof(BaseBackupTargetHandle));
136 24 : handle->type = ttype;
137 24 : handle->detail_arg = ttype->check_detail(target, target_detail);
138 :
139 22 : return handle;
140 : }
141 : }
142 :
143 : /* Did not find the target. */
144 2 : 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 22 : BaseBackupGetSink(BaseBackupTargetHandle *handle, bbsink *next_sink)
164 : {
165 22 : 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 16 : initialize_target_list(void)
173 : {
174 16 : BaseBackupTargetType *ttype = builtin_backup_targets;
175 : MemoryContext oldcontext;
176 :
177 16 : oldcontext = MemoryContextSwitchTo(TopMemoryContext);
178 48 : while (ttype->name != NULL)
179 : {
180 32 : BaseBackupTargetTypeList = lappend(BaseBackupTargetTypeList, ttype);
181 32 : ++ttype;
182 : }
183 16 : MemoryContextSwitchTo(oldcontext);
184 16 : }
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 2 : blackhole_get_sink(bbsink *next_sink, void *detail_arg)
195 : {
196 2 : return next_sink;
197 : }
198 :
199 : /*
200 : * Create a bbsink implementing a server-side backup.
201 : */
202 : static bbsink *
203 10 : server_get_sink(bbsink *next_sink, void *detail_arg)
204 : {
205 10 : 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 2 : reject_target_detail(char *target, char *target_detail)
214 : {
215 2 : if (target_detail != NULL)
216 0 : ereport(ERROR,
217 : (errcode(ERRCODE_SYNTAX_ERROR),
218 : errmsg("target \"%s\" does not accept a target detail",
219 : target)));
220 :
221 2 : 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 10 : server_check_detail(char *target, char *target_detail)
233 : {
234 10 : if (target_detail == NULL)
235 0 : ereport(ERROR,
236 : (errcode(ERRCODE_SYNTAX_ERROR),
237 : errmsg("target \"%s\" requires a target detail",
238 : target)));
239 :
240 10 : return target_detail;
241 : }
|