1 /*
2 * Copyright 2008-2021 the Pacemaker project contributors
3 *
4 * The version control history for this file may have further details.
5 *
6 * This source code is licensed under the GNU General Public License version 2
7 * or later (GPLv2+) WITHOUT ANY WARRANTY.
8 */
9
10 #include <crm_internal.h>
11
12 #include <sys/param.h>
13 #include <stdio.h>
14 #include <sys/types.h>
15 #include <unistd.h>
16
17 #include <stdlib.h>
18 #include <errno.h>
19 #include <fcntl.h>
20
21 #include <crm/crm.h>
22 #include <crm/cib.h>
23 #include <crm/msg_xml.h>
24 #include <crm/common/ipc.h>
25 #include <crm/cluster.h>
26
27 #include <crm/common/xml.h>
28
29 #include <pacemaker-based.h>
30
31 gboolean stand_alone = FALSE;
32
33 extern int cib_perform_command(xmlNode * request, xmlNode ** reply, xmlNode ** cib_diff,
34 gboolean privileged);
35
36 static xmlNode *
cib_prepare_common(xmlNode * root,const char * section)37 cib_prepare_common(xmlNode * root, const char *section)
38 {
39 xmlNode *data = NULL;
40
41 /* extract the CIB from the fragment */
42 if (root == NULL) {
43 return NULL;
44
45 } else if (pcmk__strcase_any_of(crm_element_name(root), XML_TAG_FRAGMENT,
46 F_CRM_DATA, F_CIB_CALLDATA, NULL)) {
47 data = first_named_child(root, XML_TAG_CIB);
48
49 } else {
50 data = root;
51 }
52
53 /* grab the section specified for the command */
54 if (section != NULL && data != NULL && pcmk__str_eq(crm_element_name(data), XML_TAG_CIB, pcmk__str_none)) {
55 data = get_object_root(section, data);
56 }
57
58 /* crm_log_xml_trace(root, "cib:input"); */
59 return data;
60 }
61
62 static int
cib_prepare_none(xmlNode * request,xmlNode ** data,const char ** section)63 cib_prepare_none(xmlNode * request, xmlNode ** data, const char **section)
64 {
65 *data = NULL;
66 *section = crm_element_value(request, F_CIB_SECTION);
67 return pcmk_ok;
68 }
69
70 static int
cib_prepare_data(xmlNode * request,xmlNode ** data,const char ** section)71 cib_prepare_data(xmlNode * request, xmlNode ** data, const char **section)
72 {
73 xmlNode *input_fragment = get_message_xml(request, F_CIB_CALLDATA);
74
75 *section = crm_element_value(request, F_CIB_SECTION);
76 *data = cib_prepare_common(input_fragment, *section);
77 /* crm_log_xml_debug(*data, "data"); */
78 return pcmk_ok;
79 }
80
81 static int
cib_prepare_sync(xmlNode * request,xmlNode ** data,const char ** section)82 cib_prepare_sync(xmlNode * request, xmlNode ** data, const char **section)
83 {
84 *data = NULL;
85 *section = crm_element_value(request, F_CIB_SECTION);
86 return pcmk_ok;
87 }
88
89 static int
cib_prepare_diff(xmlNode * request,xmlNode ** data,const char ** section)90 cib_prepare_diff(xmlNode * request, xmlNode ** data, const char **section)
91 {
92 xmlNode *input_fragment = NULL;
93 const char *update = crm_element_value(request, F_CIB_GLOBAL_UPDATE);
94
95 *data = NULL;
96 *section = NULL;
97
98 if (crm_is_true(update)) {
99 input_fragment = get_message_xml(request, F_CIB_UPDATE_DIFF);
100
101 } else {
102 input_fragment = get_message_xml(request, F_CIB_CALLDATA);
103 }
104
105 CRM_CHECK(input_fragment != NULL, crm_log_xml_warn(request, "no input"));
106 *data = cib_prepare_common(input_fragment, NULL);
107 return pcmk_ok;
108 }
109
110 static int
cib_cleanup_query(int options,xmlNode ** data,xmlNode ** output)111 cib_cleanup_query(int options, xmlNode ** data, xmlNode ** output)
112 {
113 CRM_LOG_ASSERT(*data == NULL);
114 if ((options & cib_no_children)
115 || pcmk__str_eq(crm_element_name(*output), "xpath-query", pcmk__str_casei)) {
116 free_xml(*output);
117 }
118 return pcmk_ok;
119 }
120
121 static int
cib_cleanup_data(int options,xmlNode ** data,xmlNode ** output)122 cib_cleanup_data(int options, xmlNode ** data, xmlNode ** output)
123 {
124 free_xml(*output);
125 *data = NULL;
126 return pcmk_ok;
127 }
128
129 static int
cib_cleanup_output(int options,xmlNode ** data,xmlNode ** output)130 cib_cleanup_output(int options, xmlNode ** data, xmlNode ** output)
131 {
132 free_xml(*output);
133 return pcmk_ok;
134 }
135
136 static int
cib_cleanup_none(int options,xmlNode ** data,xmlNode ** output)137 cib_cleanup_none(int options, xmlNode ** data, xmlNode ** output)
138 {
139 CRM_LOG_ASSERT(*data == NULL);
140 CRM_LOG_ASSERT(*output == NULL);
141 return pcmk_ok;
142 }
143
144 static cib_operation_t cib_server_ops[] = {
145 // Booleans are modifies_cib, needs_privileges, needs_quorum
146 {NULL, FALSE, FALSE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_default},
147 {CIB_OP_QUERY, FALSE, FALSE, FALSE, cib_prepare_none, cib_cleanup_query, cib_process_query},
148 {CIB_OP_MODIFY, TRUE, TRUE, TRUE, cib_prepare_data, cib_cleanup_data, cib_process_modify},
149 {CIB_OP_APPLY_DIFF,TRUE, TRUE, TRUE, cib_prepare_diff, cib_cleanup_data, cib_server_process_diff},
150 {CIB_OP_REPLACE, TRUE, TRUE, TRUE, cib_prepare_data, cib_cleanup_data, cib_process_replace_svr},
151 {CIB_OP_CREATE, TRUE, TRUE, TRUE, cib_prepare_data, cib_cleanup_data, cib_process_create},
152 {CIB_OP_DELETE, TRUE, TRUE, TRUE, cib_prepare_data, cib_cleanup_data, cib_process_delete},
153 {CIB_OP_SYNC, FALSE, TRUE, FALSE, cib_prepare_sync, cib_cleanup_none, cib_process_sync},
154 {CIB_OP_BUMP, TRUE, TRUE, TRUE, cib_prepare_none, cib_cleanup_output, cib_process_bump},
155 {CIB_OP_ERASE, TRUE, TRUE, TRUE, cib_prepare_none, cib_cleanup_output, cib_process_erase},
156 {CRM_OP_NOOP, FALSE, FALSE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_default},
157 {CIB_OP_DELETE_ALT,TRUE, TRUE, TRUE, cib_prepare_data, cib_cleanup_data, cib_process_delete_absolute},
158 {CIB_OP_UPGRADE, TRUE, TRUE, TRUE, cib_prepare_none, cib_cleanup_output, cib_process_upgrade_server},
159 {CIB_OP_SLAVE, FALSE, TRUE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_readwrite},
160 {CIB_OP_SLAVEALL, FALSE, TRUE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_readwrite},
161 {CIB_OP_SYNC_ONE, FALSE, TRUE, FALSE, cib_prepare_sync, cib_cleanup_none, cib_process_sync_one},
162 {CIB_OP_MASTER, TRUE, TRUE, FALSE, cib_prepare_data, cib_cleanup_data, cib_process_readwrite},
163 {CIB_OP_ISMASTER, FALSE, TRUE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_readwrite},
164 {"cib_shutdown_req",FALSE, TRUE, FALSE, cib_prepare_sync, cib_cleanup_none, cib_process_shutdown_req},
165 {CRM_OP_PING, FALSE, FALSE, FALSE, cib_prepare_none, cib_cleanup_output, cib_process_ping},
166 };
167
168 int
cib_get_operation_id(const char * op,int * operation)169 cib_get_operation_id(const char *op, int *operation)
170 {
171 static GHashTable *operation_hash = NULL;
172
173 if (operation_hash == NULL) {
174 int lpc = 0;
175 int max_msg_types = PCMK__NELEM(cib_server_ops);
176
177 operation_hash = pcmk__strkey_table(NULL, free);
178 for (lpc = 1; lpc < max_msg_types; lpc++) {
179 int *value = malloc(sizeof(int));
180
181 if(value) {
182 *value = lpc;
183 g_hash_table_insert(operation_hash, (gpointer) cib_server_ops[lpc].operation, value);
184 }
185 }
186 }
187
188 if (op != NULL) {
189 int *value = g_hash_table_lookup(operation_hash, op);
190
191 if (value) {
192 *operation = *value;
193 return pcmk_ok;
194 }
195 }
196 crm_err("Operation %s is not valid", op);
197 *operation = -1;
198 return -EINVAL;
199 }
200
201 xmlNode *
cib_msg_copy(xmlNode * msg,gboolean with_data)202 cib_msg_copy(xmlNode * msg, gboolean with_data)
203 {
204 int lpc = 0;
205 const char *field = NULL;
206 const char *value = NULL;
207 xmlNode *value_struct = NULL;
208
209 static const char *field_list[] = {
210 F_XML_TAGNAME,
211 F_TYPE,
212 F_CIB_CLIENTID,
213 F_CIB_CALLOPTS,
214 F_CIB_CALLID,
215 F_CIB_OPERATION,
216 F_CIB_ISREPLY,
217 F_CIB_SECTION,
218 F_CIB_HOST,
219 F_CIB_RC,
220 F_CIB_DELEGATED,
221 F_CIB_OBJID,
222 F_CIB_OBJTYPE,
223 F_CIB_EXISTING,
224 F_CIB_SEENCOUNT,
225 F_CIB_TIMEOUT,
226 F_CIB_CALLBACK_TOKEN,
227 F_CIB_GLOBAL_UPDATE,
228 F_CIB_CLIENTNAME,
229 F_CIB_USER,
230 F_CIB_NOTIFY_TYPE,
231 F_CIB_NOTIFY_ACTIVATE
232 };
233
234 static const char *data_list[] = {
235 F_CIB_CALLDATA,
236 F_CIB_UPDATE,
237 F_CIB_UPDATE_RESULT
238 };
239
240 xmlNode *copy = create_xml_node(NULL, "copy");
241
242 CRM_ASSERT(copy != NULL);
243
244 for (lpc = 0; lpc < PCMK__NELEM(field_list); lpc++) {
245 field = field_list[lpc];
246 value = crm_element_value(msg, field);
247 if (value != NULL) {
248 crm_xml_add(copy, field, value);
249 }
250 }
251 for (lpc = 0; with_data && lpc < PCMK__NELEM(data_list); lpc++) {
252 field = data_list[lpc];
253 value_struct = get_message_xml(msg, field);
254 if (value_struct != NULL) {
255 add_message_xml(copy, field, value_struct);
256 }
257 }
258
259 return copy;
260 }
261
262 cib_op_t *
cib_op_func(int call_type)263 cib_op_func(int call_type)
264 {
265 return &(cib_server_ops[call_type].fn);
266 }
267
268 gboolean
cib_op_modifies(int call_type)269 cib_op_modifies(int call_type)
270 {
271 return cib_server_ops[call_type].modifies_cib;
272 }
273
274 int
cib_op_can_run(int call_type,int call_options,gboolean privileged,gboolean global_update)275 cib_op_can_run(int call_type, int call_options, gboolean privileged, gboolean global_update)
276 {
277 if (privileged == FALSE && cib_server_ops[call_type].needs_privileges) {
278 /* abort */
279 return -EACCES;
280 }
281 #if 0
282 if (rc == pcmk_ok
283 && stand_alone == FALSE
284 && global_update == FALSE
285 && (call_options & cib_quorum_override) == 0 && cib_server_ops[call_type].needs_quorum) {
286 return -pcmk_err_no_quorum;
287 }
288 #endif
289 return pcmk_ok;
290 }
291
292 int
cib_op_prepare(int call_type,xmlNode * request,xmlNode ** input,const char ** section)293 cib_op_prepare(int call_type, xmlNode * request, xmlNode ** input, const char **section)
294 {
295 crm_trace("Prepare %d", call_type);
296 return cib_server_ops[call_type].prepare(request, input, section);
297 }
298
299 int
cib_op_cleanup(int call_type,int options,xmlNode ** input,xmlNode ** output)300 cib_op_cleanup(int call_type, int options, xmlNode ** input, xmlNode ** output)
301 {
302 crm_trace("Cleanup %d", call_type);
303 return cib_server_ops[call_type].cleanup(options, input, output);
304 }
305