]> git.proxmox.com Git - pve-cluster.git/blame - data/src/logger.c
cleanup format strings for cfs_* messages
[pve-cluster.git] / data / src / logger.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#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif /* HAVE_CONFIG_H */
24
25#define _XOPEN_SOURCE /* glibc2 needs this */
26#include <time.h> /* for strptime */
27
28#include <unistd.h>
29#include <stdio.h>
e5a5a3ea 30#include <inttypes.h>
fe000966
DM
31#include <stdint.h>
32#include <glib.h>
33#include <string.h>
34#include <ctype.h>
35#include <sys/types.h>
36#include <sys/stat.h>
37#include <fcntl.h>
38
39
40#define SYSLOG_MAX_LINE_LENGTH 8192
41
42#include "cfs-utils.h"
43#include "logger.h"
44
45/*
46 * 64 bit FNV-1a non-zero initial basis
47 */
48#define FNV1A_64_INIT ((uint64_t) 0xcbf29ce484222325ULL)
49/*
50 * 64 bit Fowler/Noll/Vo FNV-1a hash code
51 * (copied from sheepdog sources)
52 */
53static inline uint64_t fnv_64a_buf(const void *buf, size_t len, uint64_t hval)
54{
55 unsigned char *bp = (unsigned char *) buf;
56 unsigned char *be = bp + len;
57 while (bp < be) {
58 hval ^= (uint64_t) *bp++;
59 hval += (hval << 1) + (hval << 4) + (hval << 5) +
60 (hval << 7) + (hval << 8) + (hval << 40);
61 }
62 return hval;
63}
64
65static uint32_t uid_counter = 0;
66
67struct clog_base {
68 uint32_t size;
69 uint32_t cpos;
70 char data[];
71};
72
73typedef struct {
74 uint64_t node_digest;
75 uint32_t uid;
76 uint32_t time;
77} dedup_entry_t;
78
79static clog_entry_t *
80clog_alloc_entry(
81 clog_base_t *clog,
82 uint32_t size)
83{
84 g_return_val_if_fail(clog != NULL, NULL);
85 g_return_val_if_fail(size > sizeof(clog_entry_t), NULL);
86 g_return_val_if_fail(size <= CLOG_MAX_ENTRY_SIZE, NULL);
87
88 uint32_t realsize = ((size + 7) & 0xfffffff8);
89
90 uint32_t newpos;
91
92 if (!clog->cpos) {
93 newpos = sizeof(clog_base_t);
94 } else {
95 clog_entry_t *cur = (clog_entry_t *)((char *)clog + clog->cpos);
96 newpos = cur->next;
97 if ((newpos + realsize) >= clog->size) {
98 newpos = sizeof(clog_base_t);
99 }
100 }
101
102 clog_entry_t *entry = (clog_entry_t *)((char *)clog + newpos);
103
104 entry->prev = clog->cpos;
105 clog->cpos = newpos;
106 entry->next = newpos + realsize;
107
108 return entry;
109}
110
111static void
112clog_dump_entry(clog_entry_t *cur, uint32_t cpos)
113{
114 g_return_if_fail(cur != NULL);
115
116 char *node = cur->data;
117 char *ident = node + cur->node_len;
118 char *tag = ident + cur->ident_len;
119 char *msg = tag + cur->tag_len;
120
121 time_t lt = cur->time;
122 char tbuf[256];
123 strftime(tbuf, sizeof(tbuf), "%F %T", localtime(&lt));
124 printf("cpos %05d %08x %s", cpos, cur->uid, tbuf);
e5a5a3ea 125 printf(" %s{%016" PRIX64 "} %s[%s{%016" PRIX64 "}]: %s\n", node, cur->node_digest, tag, ident, cur->ident_digest, msg);
fe000966
DM
126
127}
128
129void
130clog_dump(clog_base_t *clog)
131{
132 g_return_if_fail(clog != NULL);
133
134 uint32_t cpos = clog->cpos;
135
136 while (cpos && (cpos <= clog->cpos || cpos > (clog->cpos + CLOG_MAX_ENTRY_SIZE))) {
137 clog_entry_t *cur = (clog_entry_t *)((char *)clog + cpos);
138 clog_dump_entry(cur, cpos);
139 cpos = cur->prev;
140 }
141}
142
143void
144clog_dump_json(
145 clog_base_t *clog,
146 GString *str,
147 const char *ident,
148 guint max_entries)
149{
150 g_return_if_fail(clog != NULL);
151 g_return_if_fail(str != NULL);
152
153 guint64 ident_digest = 0;
154
155 if (ident && ident[0]) {
156 ident_digest = fnv_64a_buf(ident, strlen(ident) + 1, FNV1A_64_INIT);
157 }
158
159 uint32_t cpos = clog->cpos;
160
161 g_string_append_printf(str, "{\n");
162
163 g_string_append_printf(str, "\"data\": [\n");
164
165 guint count = 0;
166 while (cpos && (cpos <= clog->cpos || cpos > (clog->cpos + CLOG_MAX_ENTRY_SIZE))) {
167 clog_entry_t *cur = (clog_entry_t *)((char *)clog + cpos);
168 cpos = cur->prev;
169
170 if (count >= max_entries)
171 break;
172
173 if (ident_digest && ident_digest != cur->ident_digest)
174 continue;
175
176 char *node = cur->data;
177 char *ident = node + cur->node_len;
178 char *tag = ident + cur->ident_len;
179 char *msg = tag + cur->tag_len;
180
181 if (count)
182 g_string_append_printf(str, ",\n");
183
184 g_string_append_printf(str, "{\"uid\": %u, \"time\": %u, \"pri\": %d, \"tag\": \"%s\", "
185 "\"pid\": %u, \"node\": \"%s\", \"user\": \"%s\", "
186 "\"msg\": \"%s\"}", cur->uid, cur->time, cur->priority, tag,
187 cur->pid, node, ident, msg);
188
189 count++;
190
191 }
192
193 if (count)
194 g_string_append_printf(str, "\n");
195
196 g_string_append_printf(str, "]\n");
197 g_string_append_printf(str, "}\n");
198
199}
200
201uint32_t
202clog_entry_size(const clog_entry_t *entry)
203{
204 g_return_val_if_fail(entry != NULL, 0);
205
206 return sizeof(clog_entry_t) + entry->node_len +
207 entry->ident_len + entry->tag_len + entry->msg_len;
208}
209
210void
211clog_copy(
212 clog_base_t *clog,
213 const clog_entry_t *entry)
214{
215 g_return_if_fail(clog != NULL);
216 g_return_if_fail(entry != NULL);
217
218 uint32_t size = clog_entry_size(entry);
219
220 clog_entry_t *new;
221 if ((new = clog_alloc_entry(clog, size)))
222 memcpy((char *)new + 8, (char *)entry + 8, size - 8);
223};
224
225uint32_t
226clog_pack(
227 clog_entry_t *entry,
228 const char *node,
229 const char *ident,
230 const char *tag,
231 uint32_t pid,
232 time_t logtime,
233 uint8_t priority,
234 const char *msg)
235{
236 g_return_val_if_fail(entry != NULL, 0);
237 g_return_val_if_fail(ident != NULL, 0);
238 g_return_val_if_fail(tag != NULL, 0);
239 g_return_val_if_fail(msg != NULL, 0);
240 g_return_val_if_fail(priority >= 0, 0);
241 g_return_val_if_fail(priority < 8, 0);
242
243 uint8_t node_len = CFS_MIN(strlen(node) + 1, 255);
244 uint8_t ident_len = CFS_MIN(strlen(ident) + 1, 255);
245 uint8_t tag_len = CFS_MIN(strlen(tag) + 1, 255);
246
247 char *msg_start = entry->data + node_len + ident_len + tag_len;
248 *msg_start = 0;
249
250 int buf_len = CLOG_MAX_ENTRY_SIZE - (msg_start - (char *)entry);
251 utf8_to_ascii(msg_start, buf_len, msg, TRUE);
252
253 uint32_t msg_len = strlen(msg_start) + 1;
254
255 uint32_t size = sizeof(clog_entry_t) + node_len + ident_len +
256 tag_len + msg_len;
257
258 if (size > CLOG_MAX_ENTRY_SIZE) {
259 int diff = size - CLOG_MAX_ENTRY_SIZE;
260 msg_len -= diff;
261 size = CLOG_MAX_ENTRY_SIZE;
262 }
263
264 entry->prev = 0;
265 entry->next = 0;
266 entry->uid = ++uid_counter;
267 entry->time = logtime;
268 entry->node_digest = fnv_64a_buf(node, node_len, FNV1A_64_INIT);
269 entry->ident_digest = fnv_64a_buf(ident, ident_len, FNV1A_64_INIT);
270 entry->pid = pid;
271 entry->priority = priority;
272 entry->node_len = node_len;
273 entry->ident_len = ident_len;
274 entry->tag_len = tag_len;
275 entry->msg_len = msg_len;
276
277 char *p = entry->data;
278 g_strlcpy(p, node, node_len);
279 p = p + node_len;
280 g_strlcpy(p, ident, ident_len);
281 p = p + ident_len;
282 g_strlcpy(p, tag, tag_len);
283
284 return size;
285}
286
287clog_base_t *
288clog_new(uint32_t size)
289{
290 g_return_val_if_fail(sizeof(clog_base_t) == 8, NULL);
291
292 if (!size)
293 size = CLOG_DEFAULT_SIZE;
294
295 g_return_val_if_fail(size >= (CLOG_MAX_ENTRY_SIZE*10), NULL);
296
297
298 clog_base_t *clog = (clog_base_t *)g_malloc0(size);
299 if (clog) {
300 clog->size = size;
301 }
302
303 return clog;
304}
305
306static gint
307clog_entry_sort_fn(
308 gconstpointer v1,
309 gconstpointer v2,
310 gpointer user_data)
311{
312 clog_entry_t *entry1 = (clog_entry_t *)v1;
313 clog_entry_t *entry2 = (clog_entry_t *)v2;
314
315 if (entry1->time != entry2->time)
316 return entry1->time - entry2->time;
317
318 if (entry1->node_digest != entry2->node_digest)
319 return entry1->node_digest - entry2->node_digest;
320
321 return entry1->uid - entry2->uid;
322}
323
324static gboolean
325clog_tree_foreach_fn(
326 gpointer key,
327 gpointer value,
328 gpointer data)
329{
330 clog_entry_t *entry = (clog_entry_t *)value;
331 clog_base_t *clog = (clog_base_t *)data;
332
333 clog_copy(clog, entry);
334
335 return FALSE;
336}
337
338clog_base_t *
339clog_sort(clog_base_t *clog)
340{
341 g_return_val_if_fail(clog != NULL, NULL);
342 g_return_val_if_fail(clog->cpos != 0, NULL);
343
344 clog_base_t *res = clog_new(clog->size);
345 if (!res)
346 return NULL;
347
348 GTree *tree = g_tree_new_with_data(clog_entry_sort_fn, NULL);
349 if (!tree) {
350 g_free(res);
351 return NULL;
352 }
353
354 uint32_t cpos = clog->cpos;
355
356 while (cpos && (cpos <= clog->cpos || cpos > (clog->cpos + CLOG_MAX_ENTRY_SIZE))) {
357 clog_entry_t *cur = (clog_entry_t *)((char *)clog + cpos);
358
359 g_tree_insert(tree, cur, cur);
360
361 cpos = cur->prev;
362 }
363
364 g_tree_foreach(tree, clog_tree_foreach_fn, res);
365 g_tree_destroy(tree);
366
367 return res;
368}
369
370uint32_t
371clog_size(clog_base_t *clog)
372{
373 g_return_val_if_fail(clog != NULL, 0);
374 return clog->size;
375}
376
377static gboolean
378dedup_lookup(
379 GHashTable *dedup,
380 const clog_entry_t *entry)
381{
382 g_return_val_if_fail(dedup != NULL, FALSE);
383 g_return_val_if_fail(entry != NULL, FALSE);
384
385 dedup_entry_t *dd = g_hash_table_lookup(dedup, &entry->node_digest);
386 if (!dd) {
387 if (!(dd = g_new0(dedup_entry_t, 1)))
388 return FALSE;
389
390 dd->node_digest = entry->node_digest;
391 dd->time = entry->time;
392 dd->uid = entry->uid;
393
394 g_hash_table_insert(dedup, dd, dd);
395
396 return TRUE;
397 }
398
399 if (entry->time > dd->time ||
400 (entry->time == dd->time && entry->uid > dd->uid)) {
401 dd->time = entry->time;
402 dd->uid = entry->uid;
403 return TRUE;
404 }
405
406 return FALSE;
407}
408
409struct clusterlog {
410 GHashTable *dedup;
89fde9ac 411 GMutex mutex;
fe000966
DM
412 clog_base_t *base;
413};
414
415void
416clusterlog_dump(
417 clusterlog_t *cl,
418 GString *str,
419 const char *user,
420 guint max_entries)
421{
422 g_return_if_fail(cl != NULL);
423 g_return_if_fail(str != NULL);
424
89fde9ac 425 g_mutex_lock(&cl->mutex);
fe000966 426 clog_dump_json(cl->base, str, user, max_entries);
89fde9ac 427 g_mutex_unlock(&cl->mutex);
fe000966
DM
428}
429
430clog_base_t *
431clusterlog_merge(
432 clusterlog_t *cl,
433 clog_base_t **clog,
434 int count,
435 int local_index)
436{
437 g_return_val_if_fail(cl != NULL, NULL);
438 g_return_val_if_fail(clog != NULL, NULL);
439 g_return_val_if_fail(count >= 2, NULL);
440 g_return_val_if_fail(local_index >= 0, NULL);
441 g_return_val_if_fail(local_index < count, NULL);
442
443 uint32_t cpos[count];
444 uint32_t maxsize = 0;
445
446 GHashTable *dedup;
447 if (!(dedup = g_hash_table_new_full(g_int64_hash, g_int64_equal, NULL, g_free)))
448 return NULL;
449
450 GTree *tree = g_tree_new_with_data(clog_entry_sort_fn, NULL);
451 if (!tree) {
452 g_hash_table_destroy(dedup);
453 return NULL;
454 }
455
456 clog_base_t *res = clog_new(maxsize);
457 if (!res) {
458 g_hash_table_destroy(dedup);
459 g_tree_destroy(tree);
460 return NULL;
461 }
462
89fde9ac 463 g_mutex_lock(&cl->mutex);
fe000966
DM
464
465 for (int i = 0; i < count; i++) {
466 if (i == local_index)
467 clog[i] = cl->base;
468
469 if (!clog[i]) {
470 cfs_critical("log pointer is NULL!");
471 cpos[i] = 0;
472 continue;
473 }
474 cpos[i] = clog[i]->cpos;
475 if (clog[i]->size > maxsize)
476 maxsize = clog[i]->size;
477 }
478
479 size_t logsize = 0;
480 maxsize = res->size - sizeof(clog_base_t) - CLOG_MAX_ENTRY_SIZE;
481
482 int found = 0;
483 while (found >= 0) {
484
485 found = -1;
486 uint32_t last = 0;
487
488 /* select entry wit latest time */
489 for (int i = 0; i < count; i++) {
490 if (!cpos[i])
491 continue;
492 clog_entry_t *cur = (clog_entry_t *)((char *)clog[i] + cpos[i]);
493 if (cur->time > last) {
494 last = cur->time;
495 found = i;
496 }
497 }
498
499 if (found < 0)
500 break;
501
502 clog_entry_t *cur = (clog_entry_t *)((char *)clog[found] + cpos[found]);
503
504 if (!g_tree_lookup(tree, cur)) {
505 g_tree_insert(tree, cur, cur);
506 dedup_lookup(dedup, cur); /* just to record versions */
507 logsize += cur->next - cpos[found];
508 if (logsize >= maxsize)
509 break;
510 }
511
512 if (!cur->prev) {
513 cpos[found] = 0;
514 } else {
515 cpos[found] = cur->prev;
516 if (!(cpos[found] <= clog[found]->cpos ||
517 cpos[found] > (clog[found]->cpos + CLOG_MAX_ENTRY_SIZE))) {
518 cpos[found] = 0;
519 }
520 }
521 }
522
523 g_tree_foreach(tree, clog_tree_foreach_fn, res);
524 g_tree_destroy(tree);
525
526 g_hash_table_destroy(cl->dedup);
527 cl->dedup = dedup;
528
529 g_free(cl->base);
530 cl->base = res;
531
89fde9ac 532 g_mutex_unlock(&cl->mutex);
fe000966
DM
533
534 return res;
535}
536
537void
538clusterlog_destroy(clusterlog_t *cl)
539{
540 g_return_if_fail(cl != NULL);
541
89fde9ac 542 g_mutex_clear(&cl->mutex);
fe000966
DM
543
544 if (cl->base)
545 g_free(cl->base);
546
547 if (cl->dedup)
548 g_hash_table_destroy(cl->dedup);
549
550 g_free(cl);
551}
552
553clusterlog_t *
554clusterlog_new(void)
555{
556 clusterlog_t *cl = g_new0(clusterlog_t, 1);
557 if (!cl)
558 return NULL;
559
89fde9ac 560 g_mutex_init(&cl->mutex);
fe000966
DM
561
562 if (!(cl->base = clog_new(0)))
563 goto fail;
564
565 if (!(cl->dedup = g_hash_table_new_full(g_int64_hash, g_int64_equal, NULL, g_free)))
566 goto fail;
567
568 return cl;
569
570fail:
571 clusterlog_destroy(cl);
572 return NULL;
573}
574
575gpointer
576clusterlog_get_state(
577 clusterlog_t *cl,
578 unsigned int *res_len)
579{
580 g_return_val_if_fail(cl != NULL, NULL);
581 g_return_val_if_fail(res_len != NULL, NULL);
582
89fde9ac 583 g_mutex_lock(&cl->mutex);
fe000966
DM
584
585 clog_base_t *new;
586 if ((new = clog_sort(cl->base))) {
587 g_free(cl->base);
588 cl->base = new;
589 }
590
591 *res_len = clog_size(cl->base);
592 gpointer msg = g_memdup(cl->base, *res_len);
593
89fde9ac 594 g_mutex_unlock(&cl->mutex);
fe000966
DM
595
596 return msg;
597}
598
599void
600clusterlog_insert(
601 clusterlog_t *cl,
602 const clog_entry_t *entry)
603{
604 g_return_if_fail(cl != NULL);
605 g_return_if_fail(entry != NULL);
606
89fde9ac 607 g_mutex_lock(&cl->mutex);
fe000966
DM
608
609 if (dedup_lookup(cl->dedup, entry)) {
610 clog_copy(cl->base, entry);
611 } else {
612 cfs_message("ignore duplicate"); // fixme remove
613 }
614
89fde9ac 615 g_mutex_unlock(&cl->mutex);
fe000966
DM
616};
617
618void
619clusterlog_add(
620 clusterlog_t *cl,
621 const char *ident,
622 const char *tag,
623 uint32_t pid,
624 uint8_t priority,
625 const gchar *format,
626 ...)
627{
628 g_return_if_fail(cl != NULL);
629 g_return_if_fail(format != NULL);
630
631 va_list args;
632 va_start (args, format);
633 char *msg = g_strdup_vprintf (format, args);
634 va_end (args);
635
636 time_t ctime = time(NULL);
637 clog_entry_t *entry = (clog_entry_t *)alloca(CLOG_MAX_ENTRY_SIZE);
638 uint32_t size = clog_pack(entry, cfs.nodename, ident, tag, pid, ctime, priority, msg);
639 g_free(msg);
640
641 if (!size)
642 return;
643
644 clusterlog_insert(cl, entry);
645}
646