]> git.proxmox.com Git - pve-cluster.git/blob - data/src/confdb.c
imported from svn 'pve-cluster/trunk'
[pve-cluster.git] / data / src / confdb.c
1 /*
2 Copyright (C) 2010 Proxmox Server Solutions GmbH
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Affero General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Affero General Public License for more details.
13
14 You should have received a copy of the GNU Affero General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17 Author: Dietmar Maurer <dietmar@proxmox.com>
18
19 */
20
21 #define G_LOG_DOMAIN "confdb"
22
23 #define CLUSTER_KEY "cluster"
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif /* HAVE_CONFIG_H */
28
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <glib.h>
34
35 #include <corosync/confdb.h>
36
37 #include "cfs-utils.h"
38 #include "loop.h"
39 #include "status.h"
40
41 typedef struct {
42 confdb_handle_t handle;
43 gboolean changes;
44 } cs_private_t;
45
46 static int
47 confdb_get_int(
48 confdb_handle_t handle,
49 hdb_handle_t parent,
50 const char *key,
51 unsigned int default_value)
52 {
53 char value[512];
54 value[0] = 0;
55 size_t value_len = sizeof(value);
56 if (confdb_key_get(handle, parent, key, strlen(key),
57 value, &value_len) == CS_OK) {
58 return atoi(value);
59 }
60
61 return default_value;
62 }
63
64 static char *
65 confdb_get_string(
66 confdb_handle_t handle,
67 hdb_handle_t parent,
68 const char *key,
69 const char *default_value)
70 {
71 char value[512];
72 value[0] = 0;
73
74 size_t value_len = sizeof(value);
75 if (confdb_key_get(handle, parent, key, strlen(key),
76 value, &value_len) == CS_OK) {
77 return g_strdup(value);
78 }
79
80 if (default_value)
81 return g_strdup(default_value);
82
83 return NULL;
84 }
85
86 static cs_error_t
87 cman_read_clusternodes(
88 confdb_handle_t handle,
89 hdb_handle_t nodes_handle,
90 cfs_clinfo_t *clinfo)
91 {
92 cs_error_t result;
93
94 result = confdb_object_find_start(handle, nodes_handle);
95 if (result != CS_OK) {
96 cfs_critical("confdb_object_find_start failed %d", result);
97 return result;
98 }
99
100 hdb_handle_t obj_handle = 0;
101 while((result = confdb_object_find(handle, nodes_handle, "clusternode",
102 strlen("clusternode"), &obj_handle)) == CS_OK) {
103 uint32_t nodeid = confdb_get_int(handle, obj_handle, "nodeid", 0);
104 uint32_t votes = confdb_get_int(handle, obj_handle, "votes", 0);
105 char *name = confdb_get_string(handle, obj_handle, "name", NULL);
106
107 if (name && nodeid) {
108 cfs_clnode_t *clnode = cfs_clnode_new(name, nodeid, votes);
109 cfs_clinfo_add_node(clinfo, clnode);
110 }
111 if (name)
112 g_free(name);
113 }
114
115 if (result == CS_ERR_ACCESS)
116 result = CS_OK;
117
118 confdb_object_find_destroy(handle, nodes_handle);
119
120 return result;
121 }
122
123 static cs_error_t
124 cman_read_cluster(
125 confdb_handle_t handle,
126 hdb_handle_t cluster_parent_handle)
127 {
128 cs_error_t result;
129
130
131 uint32_t cman_version = confdb_get_int(handle, cluster_parent_handle, "config_version", 0);
132
133 char *clustername = confdb_get_string(handle, cluster_parent_handle, "name", "unknown");
134
135 cfs_clinfo_t *clinfo = cfs_clinfo_new(clustername, cman_version);
136
137 g_free(clustername);
138
139 result = confdb_object_find_start(handle, cluster_parent_handle);
140 if (result != CS_OK) {
141 cfs_critical("confdb_object_find_start failed %d", result);
142 cfs_clinfo_destroy(clinfo);
143 return result;
144 }
145
146 hdb_handle_t nodes_handle = 0;
147 result = confdb_object_find(handle, cluster_parent_handle, "clusternodes",
148 strlen("clusternodes"), &nodes_handle);
149 if (result == CS_OK) {
150 cman_read_clusternodes(handle, nodes_handle, clinfo);
151 cfs_status_set_clinfo(clinfo);
152 } else {
153 cfs_clinfo_destroy(clinfo);
154 cfs_critical("cant find clusternodes object %d", result);
155 }
156
157 confdb_object_find_destroy(handle, cluster_parent_handle);
158
159 return result;
160 }
161
162 static cs_error_t
163 cman_read_config(confdb_handle_t handle)
164 {
165 cs_error_t result;
166
167 result = confdb_object_find_start(handle, OBJECT_PARENT_HANDLE);
168 if (result != CS_OK) {
169 cfs_critical("confdb_object_find_start failed %d", result);
170 return result;
171 }
172
173 hdb_handle_t cluster_parent_handle = 0;
174 result = confdb_object_find(handle, OBJECT_PARENT_HANDLE, CLUSTER_KEY,
175 strlen(CLUSTER_KEY), &cluster_parent_handle);
176 if (result == CS_OK) {
177 result = cman_read_cluster(handle, cluster_parent_handle);
178 } else {
179 cfs_critical("cant find cluster object %d", result);
180 }
181
182 confdb_object_find_destroy(handle, OBJECT_PARENT_HANDLE);
183
184 return result;
185 }
186
187 static cs_error_t
188 track_object(confdb_handle_t handle)
189 {
190
191 hdb_handle_t obj_handle = 0;
192 cs_error_t result;
193
194 result = confdb_object_find_start(handle, OBJECT_PARENT_HANDLE);
195 if (result != CS_OK) {
196 cfs_critical("confdb_object_find_start failed %d", result);
197 return result;
198 }
199
200 result = confdb_object_find(handle, OBJECT_PARENT_HANDLE, CLUSTER_KEY,
201 strlen(CLUSTER_KEY), &obj_handle);
202 if (result != CS_OK) {
203 cfs_critical("cant find cluster object %d", result);
204 return result;
205 }
206
207 result = confdb_object_find_destroy(handle, OBJECT_PARENT_HANDLE);
208 if (result != CS_OK) {
209 cfs_critical("confdb_object_find_destroy failed %d", result);
210 return result;
211 }
212
213 result = confdb_track_changes(handle, obj_handle, CONFDB_TRACK_DEPTH_RECURSIVE);
214
215 return result;
216 }
217
218 static void
219 confdb_key_change_notify(
220 confdb_handle_t handle,
221 confdb_change_type_t change_type,
222 hdb_handle_t parent_object_handle,
223 hdb_handle_t object_handle,
224 const void *object_name,
225 size_t object_name_len,
226 const void *key_name,
227 size_t key_name_len,
228 const void *key_value,
229 size_t key_value_len)
230 {
231 cs_error_t result;
232 cs_private_t *private = NULL;
233
234 result = confdb_context_get(handle, (gconstpointer *)&private);
235 if (result != CS_OK || !private) {
236 cfs_critical("confdb_context_get error: %d (%p)", result, private);
237 return;
238 }
239
240 private->changes = TRUE;
241 }
242
243 static void
244 confdb_object_create_notify(
245 confdb_handle_t handle,
246 hdb_handle_t parent_object_handle,
247 hdb_handle_t object_handle,
248 const void *name_pt,
249 size_t name_len)
250 {
251 cs_error_t result;
252 cs_private_t *private = NULL;
253
254 result = confdb_context_get(handle, (gconstpointer *)&private);
255 if (result != CS_OK || !private) {
256 cfs_critical("confdb_context_get error: %d (%p)", result, private);
257 return;
258 }
259
260 private->changes = TRUE;
261 }
262
263 static void
264 confdb_object_delete_notify(
265 confdb_handle_t handle,
266 hdb_handle_t parent_object_handle,
267 const void *name_pt,
268 size_t name_len)
269 {
270 cs_error_t result;
271 cs_private_t *private = NULL;
272
273 result = confdb_context_get(handle, (gconstpointer *)&private);
274 if (result != CS_OK || !private) {
275 cfs_critical("confdb_context_get error: %d (%p)", result, private);
276 return;
277 }
278
279 if (name_len == strlen(CLUSTER_KEY) &&
280 !strncmp(name_pt, CLUSTER_KEY, name_len))
281 track_object(handle);
282
283 private->changes = TRUE;
284 }
285
286 /* this does not work with current corosync - seems a bug
287 * that is why we listen to delete/change/create events instead
288 */
289 static void
290 confdb_reload_notify(
291 confdb_handle_t handle,
292 confdb_reload_type_t type)
293 {
294 cs_error_t result;
295
296 cs_private_t *private = NULL;
297
298 result = confdb_context_get(handle, (gconstpointer *)&private);
299 if (result != CS_OK || !private) {
300 cfs_critical("confdb_context_get error: %d (%p)", result, private);
301 return;
302 }
303
304 private->changes = TRUE;
305 }
306
307 static confdb_callbacks_t confdb_callbacks = {
308 .confdb_key_change_notify_fn = confdb_key_change_notify,
309 .confdb_object_create_change_notify_fn = confdb_object_create_notify,
310 .confdb_object_delete_change_notify_fn = confdb_object_delete_notify,
311 .confdb_reload_notify_fn = confdb_reload_notify,
312 };
313
314 static gboolean
315 service_confdb_finalize(
316 cfs_service_t *service,
317 gpointer context)
318 {
319 g_return_val_if_fail(service != NULL, FALSE);
320 g_return_val_if_fail(context != NULL, FALSE);
321
322 cs_private_t *private = (cs_private_t *)context;
323 confdb_handle_t handle = private->handle;
324
325 cs_error_t result;
326
327 result = confdb_finalize(handle);
328 private->handle = 0;
329 if (result != CS_OK) {
330 cfs_critical("confdb_finalize failed: %d", result);
331 return FALSE;
332 }
333
334 return TRUE;
335 }
336
337 static int
338 service_confdb_initialize(
339 cfs_service_t *service,
340 gpointer context)
341 {
342 g_return_val_if_fail(service != NULL, FALSE);
343 g_return_val_if_fail(context != NULL, FALSE);
344
345 cs_private_t *private = (cs_private_t *)context;
346
347 confdb_handle_t handle = private->handle;
348 cs_error_t result;
349
350 if (!private->handle) {
351
352 result = confdb_initialize(&handle, &confdb_callbacks);
353 if (result != CS_OK) {
354 cfs_critical("confdb_initialize failed: %d", result);
355 private->handle = 0;
356 return -1;
357 }
358
359 result = confdb_context_set(handle, private);
360 if (result != CS_OK) {
361 cfs_critical("confdb_context_set failed: %d", result);
362 confdb_finalize(handle);
363 private->handle = 0;
364 return -1;
365 }
366
367 private->handle = handle;
368 }
369
370 result = track_object(handle);
371 if (result == CS_ERR_LIBRARY || result == CS_ERR_BAD_HANDLE) {
372 cfs_critical("confdb_track_changes failed: %d - closing handle", result);
373 confdb_finalize(handle);
374 private->handle = 0;
375 return -1;
376 } else if (result != CS_OK) {
377 cfs_critical("confdb_track_changes failed: %d - trying again", result);
378 return -1;
379 }
380
381 int confdb_fd = -1;
382 if ((result = confdb_fd_get(handle, &confdb_fd)) != CS_OK) {
383 cfs_critical("confdb_fd_get failed %d - trying again", result);
384 return -1;
385 }
386
387 cman_read_config(handle);
388
389 return confdb_fd;
390 }
391
392 static gboolean
393 service_confdb_dispatch(
394 cfs_service_t *service,
395 gpointer context)
396 {
397 g_return_val_if_fail(service != NULL, FALSE);
398 g_return_val_if_fail(context != NULL, FALSE);
399
400 cs_private_t *private = (cs_private_t *)context;
401 confdb_handle_t handle = private->handle;
402
403 cs_error_t result;
404
405 private->changes = FALSE;
406 int retries = 0;
407 loop:
408 result = confdb_dispatch(handle, CS_DISPATCH_ALL);
409 if (result == CS_ERR_TRY_AGAIN) {
410 usleep(100000);
411 ++retries;
412 if ((retries % 100) == 0)
413 cfs_message("confdb_dispatch retry %d", retries);
414 goto loop;
415 }
416
417
418 if (result == CS_OK || result == CS_ERR_TRY_AGAIN) {
419
420 if (private->changes) {
421 result = cman_read_config(handle);
422 if (result == CS_OK)
423 return TRUE;
424 }
425 } else {
426 cfs_critical("confdb_dispatch failed: %d", result);
427 }
428
429 confdb_finalize(handle);
430 private->handle = 0;
431 return FALSE;
432 }
433
434 static cfs_service_callbacks_t cfs_confdb_callbacks = {
435 .cfs_service_initialize_fn = service_confdb_initialize,
436 .cfs_service_finalize_fn = service_confdb_finalize,
437 .cfs_service_dispatch_fn = service_confdb_dispatch,
438 };
439
440 cfs_service_t *
441 service_confdb_new(void)
442 {
443 cfs_service_t *service;
444
445 cs_private_t *private = g_new0(cs_private_t, 1);
446 if (!private)
447 return NULL;
448
449 service = cfs_service_new(&cfs_confdb_callbacks, G_LOG_DOMAIN, private);
450
451 return service;
452 }
453
454 void
455 service_confdb_destroy(cfs_service_t *service)
456 {
457 g_return_if_fail(service != NULL);
458
459 cs_private_t *private =
460 (cs_private_t *)cfs_service_get_context(service);
461
462 g_free(private);
463 g_free(service);
464 }