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