]> git.proxmox.com Git - pve-cluster.git/blame - data/src/status.c
pmxcfs: get config property: escape double quote and backslash
[pve-cluster.git] / data / src / status.c
CommitLineData
fe000966
DM
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 "status"
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif /* HAVE_CONFIG_H */
26
27#include <stdio.h>
28#include <stdint.h>
29#include <string.h>
30#include <errno.h>
31#include <glib.h>
32#include <sys/syslog.h>
33#include <rrd.h>
34#include <rrd_client.h>
35#include <time.h>
cf1b19d9 36#include <ctype.h>
fe000966
DM
37
38#include "cfs-utils.h"
39#include "status.h"
cf1b19d9 40#include "memdb.h"
fe000966
DM
41#include "logger.h"
42
43#define KVSTORE_CPG_GROUP_NAME "pve_kvstore_v1"
44
45typedef enum {
46 KVSTORE_MESSAGE_UPDATE = 1,
47 KVSTORE_MESSAGE_UPDATE_COMPLETE = 2,
48 KVSTORE_MESSAGE_LOG = 3,
49} kvstore_message_t;
50
51static uint32_t vminfo_version_counter;
52
53typedef struct {
54 uint32_t vmid;
55 char *nodename;
56 int vmtype;
57 uint32_t version;
58} vminfo_t;
59
60typedef struct {
61 char *key;
62 gpointer data;
63 size_t len;
64 uint32_t version;
65} kventry_t;
66
67typedef struct {
68 char *key;
69 gpointer data;
70 size_t len;
71 uint32_t time;
72} rrdentry_t;
73
74typedef struct {
75 char *path;
76 uint32_t version;
77} memdb_change_t;
78
79static memdb_change_t memdb_change_array[] = {
2113d031
DM
80 { .path = "corosync.conf" },
81 { .path = "corosync.conf.new" },
fe000966
DM
82 { .path = "storage.cfg" },
83 { .path = "user.cfg" },
84 { .path = "domains.cfg" },
85 { .path = "priv/shadow.cfg" },
8a9456dc 86 { .path = "priv/tfa.cfg" },
fe000966 87 { .path = "datacenter.cfg" },
e1735a61 88 { .path = "vzdump.cron" },
5a5417e6
DM
89 { .path = "ha/crm_commands" },
90 { .path = "ha/manager_status" },
91 { .path = "ha/resources.cfg" },
92 { .path = "ha/groups.cfg" },
e9af3eb7 93 { .path = "ha/fence.cfg" },
9d4f69ff 94 { .path = "status.cfg" },
f6de131a 95 { .path = "replication.cfg" },
22e2ed76 96 { .path = "ceph.conf" },
fe000966
DM
97};
98
89fde9ac 99static GMutex mutex;
fe000966
DM
100
101typedef struct {
102 time_t start_time;
103
104 uint32_t quorate;
105
106 cfs_clinfo_t *clinfo;
107 uint32_t clinfo_version;
108
109 GHashTable *vmlist;
110 uint32_t vmlist_version;
111
112 dfsm_t *kvstore;
113 GHashTable *kvhash;
114 GHashTable *rrdhash;
115 GHashTable *iphash;
116
117 GHashTable *memdb_changes;
118
119 clusterlog_t *clusterlog;
120} cfs_status_t;
121
122static cfs_status_t cfs_status;
123
124struct cfs_clnode {
125 char *name;
126 uint32_t nodeid;
127 uint32_t votes;
128 gboolean online;
129 GHashTable *kvhash;
130};
131
132struct cfs_clinfo {
133 char *cluster_name;
134 uint32_t cman_version;
135
136 GHashTable *nodes_byid;
137 GHashTable *nodes_byname;
138};
139
140static guint
141g_int32_hash (gconstpointer v)
142{
143 return *(const uint32_t *) v;
144}
145
146static gboolean
147g_int32_equal (gconstpointer v1,
148 gconstpointer v2)
149{
150 return *((const uint32_t*) v1) == *((const uint32_t*) v2);
151}
152
153static void vminfo_free(vminfo_t *vminfo)
154{
155 g_return_if_fail(vminfo != NULL);
156
157 if (vminfo->nodename)
158 g_free(vminfo->nodename);
159
160
161 g_free(vminfo);
162}
163
99abbd31
TL
164static const char *vminfo_type_to_string(vminfo_t *vminfo)
165{
166 if (vminfo->vmtype == VMTYPE_QEMU) {
167 return "qemu";
168 } else if (vminfo->vmtype == VMTYPE_OPENVZ) {
169 return "openvz";
170 } else if (vminfo->vmtype == VMTYPE_LXC) {
171 return "lxc";
172 } else {
173 return "unknown";
174 }
c26ca647
TL
175}
176
cf1b19d9
TL
177static const char *vminfo_type_to_path_type(vminfo_t *vminfo)
178{
179 if (vminfo->vmtype == VMTYPE_QEMU) {
180 return "qemu-server"; // special case..
181 } else {
182 return vminfo_type_to_string(vminfo);
183 }
184}
185
186int vminfo_to_path(vminfo_t *vminfo, GString *path)
187{
188 g_return_val_if_fail(vminfo != NULL, -1);
189 g_return_val_if_fail(path != NULL, -1);
190
191 if (!vminfo->nodename)
192 return 0;
193
194 const char *type = vminfo_type_to_path_type(vminfo);
195 g_string_printf(path, "/nodes/%s/%s/%u.conf", vminfo->nodename, type, vminfo->vmid);
196
197 return 1;
198}
199
fe000966
DM
200void cfs_clnode_destroy(
201 cfs_clnode_t *clnode)
202{
203 g_return_if_fail(clnode != NULL);
204
205 if (clnode->kvhash)
206 g_hash_table_destroy(clnode->kvhash);
207
208 if (clnode->name)
209 g_free(clnode->name);
210
211 g_free(clnode);
212}
213
214cfs_clnode_t *cfs_clnode_new(
215 const char *name,
216 uint32_t nodeid,
217 uint32_t votes)
218{
219 g_return_val_if_fail(name != NULL, NULL);
220
221 cfs_clnode_t *clnode = g_new0(cfs_clnode_t, 1);
222 if (!clnode)
223 return NULL;
224
225 clnode->name = g_strdup(name);
226 clnode->nodeid = nodeid;
227 clnode->votes = votes;
228
229 return clnode;
230}
231
232gboolean cfs_clinfo_destroy(
233 cfs_clinfo_t *clinfo)
234{
235 g_return_val_if_fail(clinfo != NULL, FALSE);
236
237 if (clinfo->cluster_name)
238 g_free(clinfo->cluster_name);
239
240 if (clinfo->nodes_byname)
241 g_hash_table_destroy(clinfo->nodes_byname);
242
243 if (clinfo->nodes_byid)
244 g_hash_table_destroy(clinfo->nodes_byid);
245
246 g_free(clinfo);
247
248 return TRUE;
249}
250
251cfs_clinfo_t *cfs_clinfo_new(
252 const char *cluster_name,
253 uint32_t cman_version)
254{
255 g_return_val_if_fail(cluster_name != NULL, NULL);
256
257 cfs_clinfo_t *clinfo = g_new0(cfs_clinfo_t, 1);
258 if (!clinfo)
259 return NULL;
260
261 clinfo->cluster_name = g_strdup(cluster_name);
262 clinfo->cman_version = cman_version;
263
264 if (!(clinfo->nodes_byid = g_hash_table_new_full(
265 g_int32_hash, g_int32_equal, NULL,
266 (GDestroyNotify)cfs_clnode_destroy)))
267 goto fail;
268
269 if (!(clinfo->nodes_byname = g_hash_table_new(g_str_hash, g_str_equal)))
270 goto fail;
271
272 return clinfo;
273
274fail:
275 cfs_clinfo_destroy(clinfo);
276
277 return NULL;
278}
279
280gboolean cfs_clinfo_add_node(
281 cfs_clinfo_t *clinfo,
282 cfs_clnode_t *clnode)
283{
284 g_return_val_if_fail(clinfo != NULL, FALSE);
285 g_return_val_if_fail(clnode != NULL, FALSE);
286
287 g_hash_table_replace(clinfo->nodes_byid, &clnode->nodeid, clnode);
288 g_hash_table_replace(clinfo->nodes_byname, clnode->name, clnode);
289
290 return TRUE;
291}
292
293int
294cfs_create_memberlist_msg(
295 GString *str)
296{
297 g_return_val_if_fail(str != NULL, -EINVAL);
298
89fde9ac 299 g_mutex_lock (&mutex);
fe000966
DM
300
301 g_string_append_printf(str,"{\n");
302
303 guint nodecount = 0;
304
305 cfs_clinfo_t *clinfo = cfs_status.clinfo;
306
307 if (clinfo && clinfo->nodes_byid)
308 nodecount = g_hash_table_size(clinfo->nodes_byid);
309
310 if (nodecount) {
311 g_string_append_printf(str, "\"nodename\": \"%s\",\n", cfs.nodename);
312 g_string_append_printf(str, "\"version\": %u,\n", cfs_status.clinfo_version);
313
314 g_string_append_printf(str, "\"cluster\": { ");
315 g_string_append_printf(str, "\"name\": \"%s\", \"version\": %d, "
316 "\"nodes\": %d, \"quorate\": %d ",
317 clinfo->cluster_name, clinfo->cman_version,
318 nodecount, cfs_status.quorate);
319
320 g_string_append_printf(str,"},\n");
321 g_string_append_printf(str,"\"nodelist\": {\n");
322
323 GHashTable *ht = clinfo->nodes_byid;
324 GHashTableIter iter;
325 gpointer key, value;
326
327 g_hash_table_iter_init (&iter, ht);
328
329 int i = 0;
330 while (g_hash_table_iter_next (&iter, &key, &value)) {
331 cfs_clnode_t *node = (cfs_clnode_t *)value;
332 if (i) g_string_append_printf(str, ",\n");
333 i++;
334
335 g_string_append_printf(str, " \"%s\": { \"id\": %d, \"online\": %d",
336 node->name, node->nodeid, node->online);
337
338
339 char *ip = (char *)g_hash_table_lookup(cfs_status.iphash, node->name);
340 if (ip) {
341 g_string_append_printf(str, ", \"ip\": \"%s\"", ip);
342 }
343
344 g_string_append_printf(str, "}");
345
346 }
347 g_string_append_printf(str,"\n }\n");
348 } else {
349 g_string_append_printf(str, "\"nodename\": \"%s\",\n", cfs.nodename);
350 g_string_append_printf(str, "\"version\": %u\n", cfs_status.clinfo_version);
351 }
352
353 g_string_append_printf(str,"}\n");
354
89fde9ac 355 g_mutex_unlock (&mutex);
fe000966
DM
356
357 return 0;
358}
359
360static void
361kventry_free(kventry_t *entry)
362{
363 g_return_if_fail(entry != NULL);
364
365 g_free(entry->key);
366 g_free(entry->data);
367 g_free(entry);
368}
369
370static GHashTable *
371kventry_hash_new(void)
372{
373 return g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
374 (GDestroyNotify)kventry_free);
375}
376
377static void
378rrdentry_free(rrdentry_t *entry)
379{
380 g_return_if_fail(entry != NULL);
381
382 g_free(entry->key);
383 g_free(entry->data);
384 g_free(entry);
385}
386
387static GHashTable *
388rrdentry_hash_new(void)
389{
390 return g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
391 (GDestroyNotify)rrdentry_free);
392}
393
394void
395cfs_cluster_log_dump(GString *str, const char *user, guint max_entries)
396{
397 clusterlog_dump(cfs_status.clusterlog, str, user, max_entries);
398}
399
400void
401cfs_cluster_log(clog_entry_t *entry)
402{
403 g_return_if_fail(entry != NULL);
404
405 clusterlog_insert(cfs_status.clusterlog, entry);
406
407 if (cfs_status.kvstore) {
408 struct iovec iov[1];
409 iov[0].iov_base = (char *)entry;
410 iov[0].iov_len = clog_entry_size(entry);
411
af2e9dd4
DM
412 if (dfsm_is_initialized(cfs_status.kvstore))
413 dfsm_send_message(cfs_status.kvstore, KVSTORE_MESSAGE_LOG, iov, 1);
fe000966
DM
414 }
415}
416
417void cfs_status_init(void)
418{
89fde9ac 419 g_mutex_lock (&mutex);
fe000966
DM
420
421 cfs_status.start_time = time(NULL);
422
423 cfs_status.vmlist = vmlist_hash_new();
424
425 cfs_status.kvhash = kventry_hash_new();
426
427 cfs_status.rrdhash = rrdentry_hash_new();
428
429 cfs_status.iphash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
430
431 cfs_status.memdb_changes = g_hash_table_new(g_str_hash, g_str_equal);
432
433 for (int i = 0; i < G_N_ELEMENTS(memdb_change_array); i++) {
434 g_hash_table_replace(cfs_status.memdb_changes,
435 memdb_change_array[i].path,
436 &memdb_change_array[i]);
437 }
438
439 cfs_status.clusterlog = clusterlog_new();
440
441 // fixme:
442 clusterlog_add(cfs_status.clusterlog, "root", "cluster", getpid(),
443 LOG_INFO, "starting cluster log");
444
89fde9ac 445 g_mutex_unlock (&mutex);
fe000966
DM
446}
447
448void cfs_status_cleanup(void)
449{
89fde9ac 450 g_mutex_lock (&mutex);
fe000966
DM
451
452 cfs_status.clinfo_version++;
453
454 if (cfs_status.clinfo) {
455 cfs_clinfo_destroy(cfs_status.clinfo);
456 cfs_status.clinfo = NULL;
457 }
458
459 if (cfs_status.vmlist) {
460 g_hash_table_destroy(cfs_status.vmlist);
461 cfs_status.vmlist = NULL;
462 }
463
464 if (cfs_status.kvhash) {
465 g_hash_table_destroy(cfs_status.kvhash);
466 cfs_status.kvhash = NULL;
467 }
468
469 if (cfs_status.rrdhash) {
470 g_hash_table_destroy(cfs_status.rrdhash);
471 cfs_status.rrdhash = NULL;
472 }
473
474 if (cfs_status.iphash) {
475 g_hash_table_destroy(cfs_status.iphash);
476 cfs_status.iphash = NULL;
477 }
478
479 if (cfs_status.clusterlog)
480 clusterlog_destroy(cfs_status.clusterlog);
481
89fde9ac 482 g_mutex_unlock (&mutex);
fe000966
DM
483}
484
485void cfs_status_set_clinfo(
486 cfs_clinfo_t *clinfo)
487{
488 g_return_if_fail(clinfo != NULL);
489
89fde9ac 490 g_mutex_lock (&mutex);
fe000966
DM
491
492 cfs_status.clinfo_version++;
493
494 cfs_clinfo_t *old = cfs_status.clinfo;
495
496 cfs_status.clinfo = clinfo;
497
498 cfs_message("update cluster info (cluster name %s, version = %d)",
499 clinfo->cluster_name, clinfo->cman_version);
500
501
502 if (old && old->nodes_byid && clinfo->nodes_byid) {
503 /* copy kvstore */
504 GHashTable *ht = clinfo->nodes_byid;
505 GHashTableIter iter;
506 gpointer key, value;
507
508 g_hash_table_iter_init (&iter, ht);
509
510 while (g_hash_table_iter_next (&iter, &key, &value)) {
511 cfs_clnode_t *node = (cfs_clnode_t *)value;
512 cfs_clnode_t *oldnode;
469d3acb 513 if ((oldnode = g_hash_table_lookup(old->nodes_byid, key))) {
fe000966
DM
514 node->online = oldnode->online;
515 node->kvhash = oldnode->kvhash;
516 oldnode->kvhash = NULL;
517 }
518 }
519
520 }
521
522 if (old)
523 cfs_clinfo_destroy(old);
524
525
89fde9ac 526 g_mutex_unlock (&mutex);
fe000966
DM
527}
528
529static void
530dump_kvstore_versions(
531 GString *str,
532 GHashTable *kvhash,
533 const char *nodename)
534{
535 g_return_if_fail(kvhash != NULL);
536 g_return_if_fail(str != NULL);
537 g_return_if_fail(nodename != NULL);
538
539 GHashTable *ht = kvhash;
540 GHashTableIter iter;
541 gpointer key, value;
542
543 g_string_append_printf(str, "\"%s\": {\n", nodename);
544
545 g_hash_table_iter_init (&iter, ht);
546
547 int i = 0;
548 while (g_hash_table_iter_next (&iter, &key, &value)) {
549 kventry_t *entry = (kventry_t *)value;
550 if (i) g_string_append_printf(str, ",\n");
551 i++;
552 g_string_append_printf(str,"\"%s\": %u", entry->key, entry->version);
553 }
554
555 g_string_append_printf(str, "}\n");
556}
557
558int
559cfs_create_version_msg(GString *str)
560{
561 g_return_val_if_fail(str != NULL, -EINVAL);
562
89fde9ac 563 g_mutex_lock (&mutex);
fe000966
DM
564
565 g_string_append_printf(str,"{\n");
566
567 g_string_append_printf(str, "\"starttime\": %lu,\n", (unsigned long)cfs_status.start_time);
568
569 g_string_append_printf(str, "\"clinfo\": %u,\n", cfs_status.clinfo_version);
570
571 g_string_append_printf(str, "\"vmlist\": %u,\n", cfs_status.vmlist_version);
572
573 for (int i = 0; i < G_N_ELEMENTS(memdb_change_array); i++) {
574 g_string_append_printf(str, "\"%s\": %u,\n",
575 memdb_change_array[i].path,
576 memdb_change_array[i].version);
577 }
578
579 g_string_append_printf(str, "\"kvstore\": {\n");
580
581 dump_kvstore_versions(str, cfs_status.kvhash, cfs.nodename);
582
583 cfs_clinfo_t *clinfo = cfs_status.clinfo;
584
585 if (clinfo && clinfo->nodes_byid) {
586 GHashTable *ht = clinfo->nodes_byid;
587 GHashTableIter iter;
588 gpointer key, value;
589
590 g_hash_table_iter_init (&iter, ht);
591
592 while (g_hash_table_iter_next (&iter, &key, &value)) {
593 cfs_clnode_t *node = (cfs_clnode_t *)value;
594 if (!node->kvhash)
595 continue;
596 g_string_append_printf(str, ",\n");
597 dump_kvstore_versions(str, node->kvhash, node->name);
598 }
599 }
600
601 g_string_append_printf(str,"}\n");
602
603 g_string_append_printf(str,"}\n");
604
89fde9ac 605 g_mutex_unlock (&mutex);
fe000966
DM
606
607 return 0;
608}
609
610GHashTable *
611vmlist_hash_new(void)
612{
613 return g_hash_table_new_full(g_int_hash, g_int_equal, NULL,
614 (GDestroyNotify)vminfo_free);
615}
616
617gboolean
618vmlist_hash_insert_vm(
619 GHashTable *vmlist,
620 int vmtype,
621 guint32 vmid,
622 const char *nodename,
623 gboolean replace)
624{
625 g_return_val_if_fail(vmlist != NULL, FALSE);
626 g_return_val_if_fail(nodename != NULL, FALSE);
627 g_return_val_if_fail(vmid != 0, FALSE);
7f66b436
DM
628 g_return_val_if_fail(vmtype == VMTYPE_QEMU || vmtype == VMTYPE_OPENVZ ||
629 vmtype == VMTYPE_LXC, FALSE);
fe000966
DM
630
631 if (!replace && g_hash_table_lookup(vmlist, &vmid)) {
632 cfs_critical("detected duplicate VMID %d", vmid);
633 return FALSE;
634 }
635
636 vminfo_t *vminfo = g_new0(vminfo_t, 1);
637
638 vminfo->vmid = vmid;
639 vminfo->vmtype = vmtype;
640 vminfo->nodename = g_strdup(nodename);
641
642 vminfo->version = ++vminfo_version_counter;
643
644 g_hash_table_replace(vmlist, &vminfo->vmid, vminfo);
645
646 return TRUE;
647}
648
649void
650vmlist_register_vm(
651 int vmtype,
652 guint32 vmid,
653 const char *nodename)
654{
655 g_return_if_fail(cfs_status.vmlist != NULL);
656 g_return_if_fail(nodename != NULL);
657 g_return_if_fail(vmid != 0);
7f66b436
DM
658 g_return_if_fail(vmtype == VMTYPE_QEMU || vmtype == VMTYPE_OPENVZ ||
659 vmtype == VMTYPE_LXC);
fe000966
DM
660
661 cfs_debug("vmlist_register_vm: %s/%u %d", nodename, vmid, vmtype);
662
89fde9ac 663 g_mutex_lock (&mutex);
fe000966
DM
664
665 cfs_status.vmlist_version++;
666
667 vmlist_hash_insert_vm(cfs_status.vmlist, vmtype, vmid, nodename, TRUE);
668
89fde9ac 669 g_mutex_unlock (&mutex);
fe000966
DM
670}
671
672gboolean
673vmlist_different_vm_exists(
674 int vmtype,
675 guint32 vmid,
676 const char *nodename)
677{
678 g_return_val_if_fail(cfs_status.vmlist != NULL, FALSE);
679 g_return_val_if_fail(vmid != 0, FALSE);
680
681 gboolean res = FALSE;
682
89fde9ac 683 g_mutex_lock (&mutex);
fe000966
DM
684
685 vminfo_t *vminfo;
686 if ((vminfo = (vminfo_t *)g_hash_table_lookup(cfs_status.vmlist, &vmid))) {
687 if (!(vminfo->vmtype == vmtype && strcmp(vminfo->nodename, nodename) == 0))
688 res = TRUE;
689 }
89fde9ac 690 g_mutex_unlock (&mutex);
fe000966
DM
691
692 return res;
693}
694
695gboolean
696vmlist_vm_exists(
697 guint32 vmid)
698{
699 g_return_val_if_fail(cfs_status.vmlist != NULL, FALSE);
700 g_return_val_if_fail(vmid != 0, FALSE);
701
89fde9ac 702 g_mutex_lock (&mutex);
fe000966
DM
703
704 gpointer res = g_hash_table_lookup(cfs_status.vmlist, &vmid);
705
89fde9ac 706 g_mutex_unlock (&mutex);
fe000966
DM
707
708 return res != NULL;
709}
710
711void
712vmlist_delete_vm(
713 guint32 vmid)
714{
715 g_return_if_fail(cfs_status.vmlist != NULL);
716 g_return_if_fail(vmid != 0);
717
89fde9ac 718 g_mutex_lock (&mutex);
fe000966
DM
719
720 cfs_status.vmlist_version++;
721
722 g_hash_table_remove(cfs_status.vmlist, &vmid);
723
89fde9ac 724 g_mutex_unlock (&mutex);
fe000966
DM
725}
726
727void cfs_status_set_vmlist(
728 GHashTable *vmlist)
729{
730 g_return_if_fail(vmlist != NULL);
731
89fde9ac 732 g_mutex_lock (&mutex);
fe000966
DM
733
734 cfs_status.vmlist_version++;
735
736 if (cfs_status.vmlist)
737 g_hash_table_destroy(cfs_status.vmlist);
738
739 cfs_status.vmlist = vmlist;
740
89fde9ac 741 g_mutex_unlock (&mutex);
fe000966
DM
742}
743
744int
745cfs_create_vmlist_msg(GString *str)
746{
747 g_return_val_if_fail(cfs_status.vmlist != NULL, -EINVAL);
748 g_return_val_if_fail(str != NULL, -EINVAL);
749
89fde9ac 750 g_mutex_lock (&mutex);
fe000966
DM
751
752 g_string_append_printf(str,"{\n");
753
754 GHashTable *ht = cfs_status.vmlist;
755
756 guint count = g_hash_table_size(ht);
757
758 if (!count) {
759 g_string_append_printf(str,"\"version\": %u\n", cfs_status.vmlist_version);
760 } else {
761 g_string_append_printf(str,"\"version\": %u,\n", cfs_status.vmlist_version);
762
763 g_string_append_printf(str,"\"ids\": {\n");
764
765 GHashTableIter iter;
766 gpointer key, value;
767
768 g_hash_table_iter_init (&iter, ht);
769
770 int first = 1;
771 while (g_hash_table_iter_next (&iter, &key, &value)) {
772 vminfo_t *vminfo = (vminfo_t *)value;
c26ca647 773 const char *type = vminfo_type_to_string(vminfo);
fe000966
DM
774
775 if (!first)
776 g_string_append_printf(str, ",\n");
777 first = 0;
778
779 g_string_append_printf(str,"\"%u\": { \"node\": \"%s\", \"type\": \"%s\", \"version\": %u }",
780 vminfo->vmid, vminfo->nodename, type, vminfo->version);
781 }
782
783 g_string_append_printf(str,"}\n");
784 }
785 g_string_append_printf(str,"\n}\n");
786
89fde9ac 787 g_mutex_unlock (&mutex);
fe000966
DM
788
789 return 0;
790}
791
cf1b19d9
TL
792// checks the conf for a line starting with '$prop:' and returns the value
793// afterwards, whitout initial whitespace(s), we only deal with the format
794// restricion imposed by our perl VM config parser, main reference is
795// PVE::QemuServer::parse_vm_config this allows to be very fast and still
796// relatively simple
797// main restrictions used for our advantage is the properties match reges:
798// ($line =~ m/^([a-z][a-z_]*\d*):\s*(.+?)\s*$/) from parse_vm_config
799// currently we only look at the current configuration in place, i.e., *no*
800// snapshort and *no* pending changes
801static char *
802_get_property_value(char *conf, const char *prop, int prop_len)
803{
804 char *line = NULL, *temp = NULL;
805
806 line = strtok_r(conf, "\n", &temp);
807 while (line != NULL) {
808 if (!line[0]) goto next;
809
810 // snapshot or pending section start and nothing found yet
811 if (line[0] == '[') return NULL;
812 // properties start with /^[a-z]/, so continue early if not
813 if (line[0] < 'a' || line[0] > 'z') goto next;
814
815 int line_len = strlen(line);
816 if (line_len <= prop_len + 1) goto next;
817
818 if (line[prop_len] == ':' && memcmp(line, prop, prop_len) == 0) { // found
819 char *v_start = &line[prop_len + 1];
820
821 // drop initial value whitespaces here already
822 while (*v_start && isspace(*v_start)) v_start++;
823
824 if (!*v_start) return NULL;
825
826 char *v_end = &line[line_len - 1];
827 while (v_end > v_start && isspace(*v_end)) v_end--;
828 v_end[1] = '\0';
829
830 return v_start;
831 }
832next:
833 line = strtok_r(NULL, "\n", &temp);
834 }
835
836 return NULL; // not found
837}
838
cb7cea80
TL
839static void
840_g_str_append_kv_jsonescaped(GString *str, const char *k, const char *v)
841{
842 g_string_append_printf(str, "\"%s\": \"", k);
843
844 for (; *v; v++) {
845 if (*v == '\\' || *v == '"') {
846 g_string_append_c(str, '\\');
847 }
848 g_string_append_c(str, *v);
849 }
850
851 g_string_append_c(str, '"');
852}
853
cf1b19d9
TL
854int
855cfs_create_guest_conf_property_msg(GString *str, memdb_t *memdb, const char *prop, uint32_t vmid)
856{
857 g_return_val_if_fail(cfs_status.vmlist != NULL, -EINVAL);
858 g_return_val_if_fail(str != NULL, -EINVAL);
859
860 int prop_len = strlen(prop);
861 int res = 0;
862 GString *path = NULL;
863
864 g_mutex_lock (&mutex);
865
866 g_string_printf(str,"{\n");
867
868 GHashTable *ht = cfs_status.vmlist;
869 gpointer tmp = NULL;
870 if (!g_hash_table_size(ht)) {
871 goto ret;
872 }
873
874 path = g_string_new_len(NULL, 256);
875 if (vmid >= 100) {
876 vminfo_t *vminfo = (vminfo_t *) g_hash_table_lookup(cfs_status.vmlist, &vmid);
877 if (vminfo == NULL) goto enoent;
878
879 if (!vminfo_to_path(vminfo, path)) goto err;
880
881 int size = memdb_read(memdb, path->str, &tmp);
882 if (tmp == NULL) goto err;
883 if (size <= prop_len) goto ret;
884
885 char *val = _get_property_value(tmp, prop, prop_len);
886 if (val == NULL) goto ret;
887
cb7cea80
TL
888 g_string_append_printf(str, "\"%u\":{", vmid);
889 _g_str_append_kv_jsonescaped(str, prop, val);
890 g_string_append_c(str, '}');
cf1b19d9
TL
891
892 } else {
893 GHashTableIter iter;
894 g_hash_table_iter_init (&iter, ht);
895
896 gpointer key, value;
897 int first = 1;
898 while (g_hash_table_iter_next (&iter, &key, &value)) {
899 vminfo_t *vminfo = (vminfo_t *)value;
900
901 if (!vminfo_to_path(vminfo, path)) goto err;
902
903 g_free(tmp); // no-op if already null
904 tmp = NULL;
905 int size = memdb_read(memdb, path->str, &tmp);
906 if (tmp == NULL || size <= prop_len) continue;
907
908 char *val = _get_property_value(tmp, prop, prop_len);
909 if (val == NULL) continue;
910
911 if (!first) g_string_append_printf(str, ",\n");
912 else first = 0;
913
cb7cea80
TL
914 g_string_append_printf(str, "\"%u\":{", vminfo->vmid);
915 _g_str_append_kv_jsonescaped(str, prop, val);
916 g_string_append_c(str, '}');
cf1b19d9
TL
917 }
918 }
919ret:
920 g_free(tmp);
921 g_string_free(path, TRUE);
922 g_string_append_printf(str,"\n}\n");
923 g_mutex_unlock (&mutex);
924
925 return res;
926err:
927 res = -EIO;
928 goto ret;
929enoent:
930 res = -ENOENT;
931 goto ret;
932}
933
fe000966
DM
934void
935record_memdb_change(const char *path)
936{
937 g_return_if_fail(cfs_status.memdb_changes != 0);
938
939 memdb_change_t *ce;
940
941 if ((ce = (memdb_change_t *)g_hash_table_lookup(cfs_status.memdb_changes, path))) {
942 ce->version++;
943 }
944}
945
946void
947record_memdb_reload(void)
948{
949 for (int i = 0; i < G_N_ELEMENTS(memdb_change_array); i++) {
950 memdb_change_array[i].version++;
951 }
952}
953
954static gboolean
955kventry_hash_set(
956 GHashTable *kvhash,
957 const char *key,
958 gconstpointer data,
959 size_t len)
960{
961 g_return_val_if_fail(kvhash != NULL, FALSE);
962 g_return_val_if_fail(key != NULL, FALSE);
963 g_return_val_if_fail(data != NULL, FALSE);
964
965 kventry_t *entry;
71cc17bc
DC
966 if (!len) {
967 g_hash_table_remove(kvhash, key);
968 } else if ((entry = (kventry_t *)g_hash_table_lookup(kvhash, key))) {
fe000966
DM
969 g_free(entry->data);
970 entry->data = g_memdup(data, len);
971 entry->len = len;
972 entry->version++;
973 } else {
974 kventry_t *entry = g_new0(kventry_t, 1);
975
976 entry->key = g_strdup(key);
977 entry->data = g_memdup(data, len);
978 entry->len = len;
979
980 g_hash_table_replace(kvhash, entry->key, entry);
981 }
982
983 return TRUE;
984}
985
986static const char *rrd_def_node[] = {
987 "DS:loadavg:GAUGE:120:0:U",
988 "DS:maxcpu:GAUGE:120:0:U",
989 "DS:cpu:GAUGE:120:0:U",
990 "DS:iowait:GAUGE:120:0:U",
991 "DS:memtotal:GAUGE:120:0:U",
992 "DS:memused:GAUGE:120:0:U",
993 "DS:swaptotal:GAUGE:120:0:U",
994 "DS:swapused:GAUGE:120:0:U",
995 "DS:roottotal:GAUGE:120:0:U",
996 "DS:rootused:GAUGE:120:0:U",
764296f1
DM
997 "DS:netin:DERIVE:120:0:U",
998 "DS:netout:DERIVE:120:0:U",
fe000966
DM
999
1000 "RRA:AVERAGE:0.5:1:70", // 1 min avg - one hour
1001 "RRA:AVERAGE:0.5:30:70", // 30 min avg - one day
1002 "RRA:AVERAGE:0.5:180:70", // 3 hour avg - one week
1003 "RRA:AVERAGE:0.5:720:70", // 12 hour avg - one month
1004 "RRA:AVERAGE:0.5:10080:70", // 7 day avg - ony year
1005
1006 "RRA:MAX:0.5:1:70", // 1 min max - one hour
1007 "RRA:MAX:0.5:30:70", // 30 min max - one day
1008 "RRA:MAX:0.5:180:70", // 3 hour max - one week
1009 "RRA:MAX:0.5:720:70", // 12 hour max - one month
1010 "RRA:MAX:0.5:10080:70", // 7 day max - ony year
1011 NULL,
1012};
1013
1014static const char *rrd_def_vm[] = {
1015 "DS:maxcpu:GAUGE:120:0:U",
1016 "DS:cpu:GAUGE:120:0:U",
1017 "DS:maxmem:GAUGE:120:0:U",
1018 "DS:mem:GAUGE:120:0:U",
1019 "DS:maxdisk:GAUGE:120:0:U",
1020 "DS:disk:GAUGE:120:0:U",
764296f1
DM
1021 "DS:netin:DERIVE:120:0:U",
1022 "DS:netout:DERIVE:120:0:U",
1023 "DS:diskread:DERIVE:120:0:U",
1024 "DS:diskwrite:DERIVE:120:0:U",
fe000966
DM
1025
1026 "RRA:AVERAGE:0.5:1:70", // 1 min avg - one hour
1027 "RRA:AVERAGE:0.5:30:70", // 30 min avg - one day
1028 "RRA:AVERAGE:0.5:180:70", // 3 hour avg - one week
1029 "RRA:AVERAGE:0.5:720:70", // 12 hour avg - one month
1030 "RRA:AVERAGE:0.5:10080:70", // 7 day avg - ony year
1031
1032 "RRA:MAX:0.5:1:70", // 1 min max - one hour
1033 "RRA:MAX:0.5:30:70", // 30 min max - one day
1034 "RRA:MAX:0.5:180:70", // 3 hour max - one week
1035 "RRA:MAX:0.5:720:70", // 12 hour max - one month
1036 "RRA:MAX:0.5:10080:70", // 7 day max - ony year
1037 NULL,
1038};
1039
1040static const char *rrd_def_storage[] = {
1041 "DS:total:GAUGE:120:0:U",
1042 "DS:used:GAUGE:120:0:U",
1043
1044 "RRA:AVERAGE:0.5:1:70", // 1 min avg - one hour
1045 "RRA:AVERAGE:0.5:30:70", // 30 min avg - one day
1046 "RRA:AVERAGE:0.5:180:70", // 3 hour avg - one week
1047 "RRA:AVERAGE:0.5:720:70", // 12 hour avg - one month
1048 "RRA:AVERAGE:0.5:10080:70", // 7 day avg - ony year
1049
1050 "RRA:MAX:0.5:1:70", // 1 min max - one hour
1051 "RRA:MAX:0.5:30:70", // 30 min max - one day
1052 "RRA:MAX:0.5:180:70", // 3 hour max - one week
1053 "RRA:MAX:0.5:720:70", // 12 hour max - one month
1054 "RRA:MAX:0.5:10080:70", // 7 day max - ony year
1055 NULL,
1056};
1057
1058#define RRDDIR "/var/lib/rrdcached/db"
1059
1060static void
1061create_rrd_file(
1062 const char *filename,
1063 int argcount,
1064 const char *rrddef[])
1065{
1066 /* start at day boundary */
1067 time_t ctime;
1068 time(&ctime);
1069 struct tm *ltm = localtime(&ctime);
1070 ltm->tm_sec = 0;
1071 ltm->tm_min = 0;
1072 ltm->tm_hour = 0;
1073
1074 rrd_clear_error();
1075 if (rrd_create_r(filename, 60, timelocal(ltm), argcount, rrddef)) {
1076 cfs_message("RRD create error %s: %s", filename, rrd_get_error());
1077 }
1078}
1079
1080static inline const char *
1081rrd_skip_data(
1082 const char *data,
1083 int count)
1084{
1085 int found = 0;
1086 while (*data && found < count) {
1087 if (*data++ == ':')
1088 found++;
1089 }
1090 return data;
1091}
1092
1093static void
1094update_rrd_data(
1095 const char *key,
1096 gconstpointer data,
1097 size_t len)
1098{
1099 g_return_if_fail(key != NULL);
1100 g_return_if_fail(data != NULL);
1101 g_return_if_fail(len > 0);
1102 g_return_if_fail(len < 4096);
1103
1104 static const char *rrdcsock = "unix:/var/run/rrdcached.sock";
1105
1106 int use_daemon = 1;
1107 if (rrdc_connect(rrdcsock) != 0)
1108 use_daemon = 0;
1109
ba9dcfc1 1110 char *filename = NULL;
fe000966
DM
1111
1112 int skip = 0;
1113
1114 if (strncmp(key, "pve2-node/", 10) == 0) {
1115 const char *node = key + 10;
1116
f9c865a8 1117 skip = 2;
fe000966
DM
1118
1119 if (strchr(node, '/') != NULL)
1120 goto keyerror;
1121
1122 if (strlen(node) < 1)
1123 goto keyerror;
1124
ba9dcfc1
DM
1125 filename = g_strdup_printf(RRDDIR "/%s", key);
1126
fe000966
DM
1127 if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
1128
1129 mkdir(RRDDIR "/pve2-node", 0755);
1130 int argcount = sizeof(rrd_def_node)/sizeof(void*) - 1;
1131 create_rrd_file(filename, argcount, rrd_def_node);
1132 }
1133
ba9dcfc1
DM
1134 } else if ((strncmp(key, "pve2-vm/", 8) == 0) ||
1135 (strncmp(key, "pve2.3-vm/", 10) == 0)) {
1136 const char *vmid;
fe000966 1137
ba9dcfc1
DM
1138 if (strncmp(key, "pve2-vm/", 8) == 0) {
1139 vmid = key + 8;
1140 skip = 2;
1141 } else {
1142 vmid = key + 10;
94e4cba3 1143 skip = 4;
ba9dcfc1 1144 }
fe000966
DM
1145
1146 if (strchr(vmid, '/') != NULL)
1147 goto keyerror;
1148
1149 if (strlen(vmid) < 1)
1150 goto keyerror;
1151
ba9dcfc1
DM
1152 filename = g_strdup_printf(RRDDIR "/%s/%s", "pve2-vm", vmid);
1153
fe000966
DM
1154 if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
1155
1156 mkdir(RRDDIR "/pve2-vm", 0755);
1157 int argcount = sizeof(rrd_def_vm)/sizeof(void*) - 1;
1158 create_rrd_file(filename, argcount, rrd_def_vm);
1159 }
1160
1161 } else if (strncmp(key, "pve2-storage/", 13) == 0) {
1162 const char *node = key + 13;
1163
1164 const char *storage = node;
1165 while (*storage && *storage != '/')
1166 storage++;
1167
1168 if (*storage != '/' || ((storage - node) < 1))
1169 goto keyerror;
1170
1171 storage++;
1172
1173 if (strchr(storage, '/') != NULL)
1174 goto keyerror;
1175
1176 if (strlen(storage) < 1)
1177 goto keyerror;
1178
ba9dcfc1
DM
1179 filename = g_strdup_printf(RRDDIR "/%s", key);
1180
fe000966
DM
1181 if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
1182
1183 mkdir(RRDDIR "/pve2-storage", 0755);
1184
1185 char *dir = g_path_get_dirname(filename);
1186 mkdir(dir, 0755);
1187 g_free(dir);
1188
1189 int argcount = sizeof(rrd_def_storage)/sizeof(void*) - 1;
1190 create_rrd_file(filename, argcount, rrd_def_storage);
1191 }
1192
1193 } else {
1194 goto keyerror;
1195 }
1196
1197 const char *dp = skip ? rrd_skip_data(data, skip) : data;
1198
1199 const char *update_args[] = { dp, NULL };
1200
1201 if (use_daemon) {
1202 int status;
1203 if ((status = rrdc_update(filename, 1, update_args)) != 0) {
1204 cfs_message("RRDC update error %s: %d", filename, status);
1205 rrdc_disconnect();
1206 rrd_clear_error();
1207 if (rrd_update_r(filename, NULL, 1, update_args) != 0) {
1208 cfs_message("RRD update error %s: %s", filename, rrd_get_error());
1209 }
1210 }
1211
1212 } else {
1213 rrd_clear_error();
1214 if (rrd_update_r(filename, NULL, 1, update_args) != 0) {
1215 cfs_message("RRD update error %s: %s", filename, rrd_get_error());
1216 }
1217 }
1218
1219ret:
ba9dcfc1
DM
1220 if (filename)
1221 g_free(filename);
1222
fe000966
DM
1223 return;
1224
1225keyerror:
1226 cfs_critical("RRD update error: unknown/wrong key %s", key);
1227 goto ret;
1228}
1229
1230static gboolean
1231rrd_entry_is_old(
1232 gpointer key,
1233 gpointer value,
1234 gpointer user_data)
1235{
1236 rrdentry_t *entry = (rrdentry_t *)value;
1237 uint32_t ctime = GPOINTER_TO_UINT(user_data);
1238
1239 int diff = ctime - entry->time;
1240
1241 /* remove everything older than 5 minutes */
1242 int expire = 60*5;
1243
1244 return (diff > expire) ? TRUE : FALSE;
1245}
1246
1247static char *rrd_dump_buf = NULL;
1248static time_t rrd_dump_last = 0;
1249
1250void
1251cfs_rrd_dump(GString *str)
1252{
1253 time_t ctime;
fe000966 1254
663896cf
TL
1255 g_mutex_lock (&mutex);
1256
1257 time(&ctime);
fe000966
DM
1258 if (rrd_dump_buf && (ctime - rrd_dump_last) < 2) {
1259 g_string_assign(str, rrd_dump_buf);
663896cf 1260 g_mutex_unlock (&mutex);
fe000966
DM
1261 return;
1262 }
1263
1264 /* remove old data */
1265 g_hash_table_foreach_remove(cfs_status.rrdhash, rrd_entry_is_old,
1266 GUINT_TO_POINTER(ctime));
1267
1268 g_string_set_size(str, 0);
1269
1270 GHashTableIter iter;
1271 gpointer key, value;
1272
1273 g_hash_table_iter_init (&iter, cfs_status.rrdhash);
1274
1275 while (g_hash_table_iter_next (&iter, &key, &value)) {
1276 rrdentry_t *entry = (rrdentry_t *)value;
1277 g_string_append(str, key);
1278 g_string_append(str, ":");
1279 g_string_append(str, entry->data);
1280 g_string_append(str, "\n");
1281 }
1282
1283 g_string_append_c(str, 0); // never return undef
1284
1285 rrd_dump_last = ctime;
1286 if (rrd_dump_buf)
1287 g_free(rrd_dump_buf);
1288 rrd_dump_buf = g_strdup(str->str);
663896cf
TL
1289
1290 g_mutex_unlock (&mutex);
fe000966
DM
1291}
1292
1293static gboolean
1294nodeip_hash_set(
1295 GHashTable *iphash,
1296 const char *nodename,
1297 const char *ip,
1298 size_t len)
1299{
1300 g_return_val_if_fail(iphash != NULL, FALSE);
1301 g_return_val_if_fail(nodename != NULL, FALSE);
1302 g_return_val_if_fail(ip != NULL, FALSE);
1303 g_return_val_if_fail(len > 0, FALSE);
1304 g_return_val_if_fail(len < 256, FALSE);
1305 g_return_val_if_fail(ip[len-1] == 0, FALSE);
1306
1307 char *oldip = (char *)g_hash_table_lookup(iphash, nodename);
1308
1309 if (!oldip || (strcmp(oldip, ip) != 0)) {
1310 cfs_status.clinfo_version++;
1311 g_hash_table_replace(iphash, g_strdup(nodename), g_strdup(ip));
1312 }
1313
1314 return TRUE;
1315}
1316
1317static gboolean
1318rrdentry_hash_set(
1319 GHashTable *rrdhash,
1320 const char *key,
1321 const char *data,
1322 size_t len)
1323{
1324 g_return_val_if_fail(rrdhash != NULL, FALSE);
1325 g_return_val_if_fail(key != NULL, FALSE);
1326 g_return_val_if_fail(data != NULL, FALSE);
1327 g_return_val_if_fail(len > 0, FALSE);
1328 g_return_val_if_fail(len < 4096, FALSE);
1329 g_return_val_if_fail(data[len-1] == 0, FALSE);
1330
1331 rrdentry_t *entry;
1332 if ((entry = (rrdentry_t *)g_hash_table_lookup(rrdhash, key))) {
1333 g_free(entry->data);
1334 entry->data = g_memdup(data, len);
1335 entry->len = len;
1336 entry->time = time(NULL);
1337 } else {
1338 rrdentry_t *entry = g_new0(rrdentry_t, 1);
1339
1340 entry->key = g_strdup(key);
1341 entry->data = g_memdup(data, len);
1342 entry->len = len;
1343 entry->time = time(NULL);
1344
1345 g_hash_table_replace(rrdhash, entry->key, entry);
1346 }
1347
1348 update_rrd_data(key, data, len);
1349
1350 return TRUE;
1351}
1352
1353static int
1354kvstore_send_update_message(
1355 dfsm_t *dfsm,
1356 const char *key,
1357 gpointer data,
1358 guint32 len)
1359{
af2e9dd4
DM
1360 if (!dfsm_is_initialized(dfsm))
1361 return -EACCES;
fe000966
DM
1362
1363 struct iovec iov[2];
1364
1365 char name[256];
1366 g_strlcpy(name, key, sizeof(name));
1367
1368 iov[0].iov_base = &name;
1369 iov[0].iov_len = sizeof(name);
1370
1371 iov[1].iov_base = (char *)data;
1372 iov[1].iov_len = len;
1373
1374 if (dfsm_send_message(dfsm, KVSTORE_MESSAGE_UPDATE, iov, 2) == CS_OK)
1375 return 0;
1376
1377 return -EACCES;
1378}
1379
1380static clog_entry_t *
1381kvstore_parse_log_message(
1382 const void *msg,
1383 size_t msg_len)
1384{
1385 g_return_val_if_fail(msg != NULL, NULL);
1386
1387 if (msg_len < sizeof(clog_entry_t)) {
e5a5a3ea 1388 cfs_critical("received short log message (%zu < %zu)", msg_len, sizeof(clog_entry_t));
fe000966
DM
1389 return NULL;
1390 }
1391
1392 clog_entry_t *entry = (clog_entry_t *)msg;
1393
1394 uint32_t size = sizeof(clog_entry_t) + entry->node_len +
1395 entry->ident_len + entry->tag_len + entry->msg_len;
1396
1397 if (msg_len != size) {
e5a5a3ea 1398 cfs_critical("received log message with wrong size (%zu != %u)", msg_len, size);
fe000966
DM
1399 return NULL;
1400 }
1401
1402 msg = entry->data;
1403
1404 if (*((char *)msg + entry->node_len - 1)) {
1405 cfs_critical("unterminated string in log message");
1406 return NULL;
1407 }
1408 msg += entry->node_len;
1409
1410 if (*((char *)msg + entry->ident_len - 1)) {
1411 cfs_critical("unterminated string in log message");
1412 return NULL;
1413 }
1414 msg += entry->ident_len;
1415
1416 if (*((char *)msg + entry->tag_len - 1)) {
1417 cfs_critical("unterminated string in log message");
1418 return NULL;
1419 }
1420 msg += entry->tag_len;
1421
1422 if (*((char *)msg + entry->msg_len - 1)) {
1423 cfs_critical("unterminated string in log message");
1424 return NULL;
1425 }
1426
1427 return entry;
1428}
1429
1430static gboolean
1431kvstore_parse_update_message(
1432 const void *msg,
1433 size_t msg_len,
1434 const char **key,
1435 gconstpointer *data,
1436 guint32 *len)
1437{
1438 g_return_val_if_fail(msg != NULL, FALSE);
1439 g_return_val_if_fail(key != NULL, FALSE);
1440 g_return_val_if_fail(data != NULL, FALSE);
1441 g_return_val_if_fail(len != NULL, FALSE);
1442
1443 if (msg_len < 256) {
e5a5a3ea 1444 cfs_critical("received short kvstore message (%zu < 256)", msg_len);
fe000966
DM
1445 return FALSE;
1446 }
1447
1448 /* test if key is null terminated */
1449 int i = 0;
1450 for (i = 0; i < 256; i++)
1451 if (((char *)msg)[i] == 0)
1452 break;
1453
1454 if (i == 256)
1455 return FALSE;
1456
1457
1458 *len = msg_len - 256;
1459 *key = msg;
1460 *data = msg + 256;
1461
1462 return TRUE;
1463}
1464
1465int
1466cfs_create_status_msg(
1467 GString *str,
1468 const char *nodename,
1469 const char *key)
1470{
1471 g_return_val_if_fail(str != NULL, -EINVAL);
1472 g_return_val_if_fail(key != NULL, -EINVAL);
1473
1474 int res = -ENOENT;
1475
1476 GHashTable *kvhash = NULL;
1477
89fde9ac 1478 g_mutex_lock (&mutex);
fe000966
DM
1479
1480 if (!nodename || !nodename[0] || !strcmp(nodename, cfs.nodename)) {
1481 kvhash = cfs_status.kvhash;
043bbd8f 1482 } else if (cfs_status.clinfo && cfs_status.clinfo->nodes_byname) {
fe000966
DM
1483 cfs_clnode_t *clnode;
1484 if ((clnode = g_hash_table_lookup(cfs_status.clinfo->nodes_byname, nodename)))
1485 kvhash = clnode->kvhash;
1486 }
1487
1488 kventry_t *entry;
1489 if (kvhash && (entry = (kventry_t *)g_hash_table_lookup(kvhash, key))) {
1490 g_string_append_len(str, entry->data, entry->len);
1491 res = 0;
1492 }
1493
89fde9ac 1494 g_mutex_unlock (&mutex);
fe000966
DM
1495
1496 return res;
1497}
1498
1499int
1500cfs_status_set(
1501 const char *key,
1502 gpointer data,
1503 size_t len)
1504{
1505 g_return_val_if_fail(key != NULL, FALSE);
1506 g_return_val_if_fail(data != NULL, FALSE);
1507 g_return_val_if_fail(cfs_status.kvhash != NULL, FALSE);
1508
1509 if (len > CFS_MAX_STATUS_SIZE)
1510 return -EFBIG;
1511
89fde9ac 1512 g_mutex_lock (&mutex);
fe000966
DM
1513
1514 gboolean res;
1515
1516 if (strncmp(key, "rrd/", 4) == 0) {
1517 res = rrdentry_hash_set(cfs_status.rrdhash, key + 4, data, len);
1518 } else if (!strcmp(key, "nodeip")) {
1519 res = nodeip_hash_set(cfs_status.iphash, cfs.nodename, data, len);
1520 } else {
1521 res = kventry_hash_set(cfs_status.kvhash, key, data, len);
1522 }
89fde9ac 1523 g_mutex_unlock (&mutex);
fe000966
DM
1524
1525 if (cfs_status.kvstore)
1526 kvstore_send_update_message(cfs_status.kvstore, key, data, len);
1527
1528 return res ? 0 : -ENOMEM;
1529}
1530
1531gboolean
1532cfs_kvstore_node_set(
1533 uint32_t nodeid,
1534 const char *key,
1535 gconstpointer data,
1536 size_t len)
1537{
1538 g_return_val_if_fail(nodeid != 0, FALSE);
1539 g_return_val_if_fail(key != NULL, FALSE);
1540 g_return_val_if_fail(data != NULL, FALSE);
1541
89fde9ac 1542 g_mutex_lock (&mutex);
fe000966
DM
1543
1544 if (!cfs_status.clinfo || !cfs_status.clinfo->nodes_byid)
1545 goto ret; /* ignore */
1546
1547 cfs_clnode_t *clnode = g_hash_table_lookup(cfs_status.clinfo->nodes_byid, &nodeid);
1548 if (!clnode)
1549 goto ret; /* ignore */
1550
1551 cfs_debug("got node %d status update %s", nodeid, key);
1552
1553 if (strncmp(key, "rrd/", 4) == 0) {
1554 rrdentry_hash_set(cfs_status.rrdhash, key + 4, data, len);
1555 } else if (!strcmp(key, "nodeip")) {
1556 nodeip_hash_set(cfs_status.iphash, clnode->name, data, len);
1557 } else {
1558 if (!clnode->kvhash) {
1559 if (!(clnode->kvhash = kventry_hash_new())) {
1560 goto ret; /*ignore */
1561 }
1562 }
1563
1564 kventry_hash_set(clnode->kvhash, key, data, len);
1565
1566 }
1567ret:
89fde9ac 1568 g_mutex_unlock (&mutex);
fe000966
DM
1569
1570 return TRUE;
1571}
1572
1573static gboolean
1574cfs_kvstore_sync(void)
1575{
1576 g_return_val_if_fail(cfs_status.kvhash != NULL, FALSE);
1577 g_return_val_if_fail(cfs_status.kvstore != NULL, FALSE);
1578
1579 gboolean res = TRUE;
1580
89fde9ac 1581 g_mutex_lock (&mutex);
fe000966
DM
1582
1583 GHashTable *ht = cfs_status.kvhash;
1584 GHashTableIter iter;
1585 gpointer key, value;
1586
1587 g_hash_table_iter_init (&iter, ht);
1588
1589 while (g_hash_table_iter_next (&iter, &key, &value)) {
1590 kventry_t *entry = (kventry_t *)value;
1591 kvstore_send_update_message(cfs_status.kvstore, entry->key, entry->data, entry->len);
1592 }
1593
89fde9ac 1594 g_mutex_unlock (&mutex);
fe000966
DM
1595
1596 return res;
1597}
1598
1599static int
1600dfsm_deliver(
1601 dfsm_t *dfsm,
1602 gpointer data,
1603 int *res_ptr,
1604 uint32_t nodeid,
1605 uint32_t pid,
1606 uint16_t msg_type,
1607 uint32_t msg_time,
1608 const void *msg,
1609 size_t msg_len)
1610{
1611 g_return_val_if_fail(dfsm != NULL, -1);
1612 g_return_val_if_fail(msg != NULL, -1);
1613 g_return_val_if_fail(res_ptr != NULL, -1);
1614
1615 /* ignore message for ourself */
1616 if (dfsm_nodeid_is_local(dfsm, nodeid, pid))
1617 goto ret;
1618
1619 if (msg_type == KVSTORE_MESSAGE_UPDATE) {
1620 const char *key;
1621 gconstpointer data;
1622 guint32 len;
1623 if (kvstore_parse_update_message(msg, msg_len, &key, &data, &len)) {
1624 cfs_kvstore_node_set(nodeid, key, data, len);
1625 } else {
1626 cfs_critical("cant parse update message");
1627 }
1628 } else if (msg_type == KVSTORE_MESSAGE_LOG) {
1629 cfs_message("received log"); // fixme: remove
1630 const clog_entry_t *entry;
1631 if ((entry = kvstore_parse_log_message(msg, msg_len))) {
1632 clusterlog_insert(cfs_status.clusterlog, entry);
1633 } else {
1634 cfs_critical("cant parse log message");
1635 }
1636 } else {
1637 cfs_critical("received unknown message type %d\n", msg_type);
1638 goto fail;
1639 }
1640
1641ret:
1642 *res_ptr = 0;
1643 return 1;
1644
1645fail:
1646 *res_ptr = -EACCES;
1647 return 1;
1648}
1649
1650static void
1651dfsm_confchg(
1652 dfsm_t *dfsm,
1653 gpointer data,
1654 const struct cpg_address *member_list,
1655 size_t member_list_entries)
1656{
1657 g_return_if_fail(dfsm != NULL);
1658 g_return_if_fail(member_list != NULL);
1659
1660 cfs_debug("enter %s", __func__);
1661
89fde9ac 1662 g_mutex_lock (&mutex);
fe000966
DM
1663
1664 cfs_clinfo_t *clinfo = cfs_status.clinfo;
1665
1666 if (clinfo && clinfo->nodes_byid) {
1667
1668 GHashTable *ht = clinfo->nodes_byid;
1669 GHashTableIter iter;
1670 gpointer key, value;
1671
1672 g_hash_table_iter_init (&iter, ht);
1673
1674 while (g_hash_table_iter_next (&iter, &key, &value)) {
1675 cfs_clnode_t *node = (cfs_clnode_t *)value;
1676 node->online = FALSE;
1677 }
1678
1679 for (int i = 0; i < member_list_entries; i++) {
1680 cfs_clnode_t *node;
1681 if ((node = g_hash_table_lookup(clinfo->nodes_byid, &member_list[i].nodeid))) {
1682 node->online = TRUE;
1683 }
1684 }
1685
1686 cfs_status.clinfo_version++;
1687 }
1688
89fde9ac 1689 g_mutex_unlock (&mutex);
fe000966
DM
1690}
1691
1692static gpointer
1693dfsm_get_state(
1694 dfsm_t *dfsm,
1695 gpointer data,
1696 unsigned int *res_len)
1697{
1698 g_return_val_if_fail(dfsm != NULL, NULL);
1699
1700 gpointer msg = clusterlog_get_state(cfs_status.clusterlog, res_len);
1701
1702 return msg;
1703}
1704
1705static int
1706dfsm_process_update(
1707 dfsm_t *dfsm,
1708 gpointer data,
1709 dfsm_sync_info_t *syncinfo,
1710 uint32_t nodeid,
1711 uint32_t pid,
1712 const void *msg,
1713 size_t msg_len)
1714{
1715 cfs_critical("%s: received unexpected update message", __func__);
1716
1717 return -1;
1718}
1719
1720static int
1721dfsm_process_state_update(
1722 dfsm_t *dfsm,
1723 gpointer data,
1724 dfsm_sync_info_t *syncinfo)
1725{
1726 g_return_val_if_fail(dfsm != NULL, -1);
1727 g_return_val_if_fail(syncinfo != NULL, -1);
1728
1729 clog_base_t *clog[syncinfo->node_count];
1730
1731 int local_index = -1;
1732 for (int i = 0; i < syncinfo->node_count; i++) {
1733 dfsm_node_info_t *ni = &syncinfo->nodes[i];
1734 ni->synced = 1;
1735
1736 if (syncinfo->local == ni)
1737 local_index = i;
1738
1739 clog_base_t *base = (clog_base_t *)ni->state;
1740 if (ni->state_len > 8 && ni->state_len == clog_size(base)) {
1741 clog[i] = ni->state;
1742 } else {
1743 cfs_critical("received log with wrong size %u", ni->state_len);
1744 clog[i] = NULL;
1745 }
1746 }
1747
1748 if (!clusterlog_merge(cfs_status.clusterlog, clog, syncinfo->node_count, local_index)) {
1749 cfs_critical("unable to merge log files");
1750 }
1751
1752 cfs_kvstore_sync();
1753
1754 return 1;
1755}
1756
1757static int
1758dfsm_commit(
1759 dfsm_t *dfsm,
1760 gpointer data,
1761 dfsm_sync_info_t *syncinfo)
1762{
1763 g_return_val_if_fail(dfsm != NULL, -1);
1764 g_return_val_if_fail(syncinfo != NULL, -1);
1765
1766 return 1;
1767}
1768
1769static void
1770dfsm_synced(dfsm_t *dfsm)
1771{
1772 g_return_if_fail(dfsm != NULL);
1773
1774 char *ip = (char *)g_hash_table_lookup(cfs_status.iphash, cfs.nodename);
1775 if (!ip)
1776 ip = cfs.ip;
1777
1778 cfs_status_set("nodeip", ip, strlen(ip) + 1);
1779}
1780
1781static int
1782dfsm_cleanup(
1783 dfsm_t *dfsm,
1784 gpointer data,
1785 dfsm_sync_info_t *syncinfo)
1786{
1787 return 1;
1788}
1789
1790static dfsm_callbacks_t kvstore_dfsm_callbacks = {
1791 .dfsm_deliver_fn = dfsm_deliver,
1792 .dfsm_confchg_fn = dfsm_confchg,
1793
1794 .dfsm_get_state_fn = dfsm_get_state,
1795 .dfsm_process_state_update_fn = dfsm_process_state_update,
1796 .dfsm_process_update_fn = dfsm_process_update,
1797 .dfsm_commit_fn = dfsm_commit,
1798 .dfsm_cleanup_fn = dfsm_cleanup,
1799 .dfsm_synced_fn = dfsm_synced,
1800};
1801
1802dfsm_t *
1803cfs_status_dfsm_new(void)
1804{
89fde9ac 1805 g_mutex_lock (&mutex);
fe000966
DM
1806
1807 cfs_status.kvstore = dfsm_new(NULL, KVSTORE_CPG_GROUP_NAME, G_LOG_DOMAIN,
1808 0, &kvstore_dfsm_callbacks);
89fde9ac 1809 g_mutex_unlock (&mutex);
fe000966
DM
1810
1811 return cfs_status.kvstore;
1812}
1813
1814gboolean
1815cfs_is_quorate(void)
1816{
89fde9ac 1817 g_mutex_lock (&mutex);
fe000966 1818 gboolean res = cfs_status.quorate;
89fde9ac 1819 g_mutex_unlock (&mutex);
fe000966
DM
1820
1821 return res;
1822}
1823
1824void
1825cfs_set_quorate(
1826 uint32_t quorate,
1827 gboolean quiet)
1828{
89fde9ac 1829 g_mutex_lock (&mutex);
fe000966
DM
1830
1831 uint32_t prev_quorate = cfs_status.quorate;
1832 cfs_status.quorate = quorate;
1833
1834 if (!prev_quorate && cfs_status.quorate) {
1835 if (!quiet)
1836 cfs_message("node has quorum");
1837 }
1838
1839 if (prev_quorate && !cfs_status.quorate) {
1840 if (!quiet)
1841 cfs_message("node lost quorum");
1842 }
1843
89fde9ac 1844 g_mutex_unlock (&mutex);
fe000966 1845}