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