1 /*
2 * Copyright (c) 2002-2013 Balabit
3 * Copyright (c) 1998-2012 Balázs Scheidler
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 * As an additional exemption you are allowed to compile & link against the
20 * OpenSSL libraries as published by the OpenSSL project. See the file
21 * COPYING for details.
22 *
23 */
24
25 #include "persist-state.h"
26 #include "serialize.h"
27 #include "messages.h"
28 #include "fdhelpers.h"
29
30 #include <sys/types.h>
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <sys/mman.h>
34 #include <errno.h>
35 #include <string.h>
36 #include <signal.h>
37
38 #define PERSIST_FILE_INITIAL_SIZE 16384
39 #define PERSIST_STATE_KEY_BLOCK_SIZE 4096
40 #define PERSIST_FILE_MAX_ENTRY_SIZE 8448
41
42 /*
43 * The syslog-ng persistent state is a set of name-value pairs,
44 * updated atomically during syslog-ng runtime. When syslog-ng
45 * initializes its configuration it allocates chunks of memory to
46 * represent its own state which gets mmapped from a file.
47 *
48 * Whenever syslog-ng writes to the state memory, it gets atomically
49 * written to the persistent file.
50 *
51 * Allocated blocks have a name in order to make it possible to fetch
52 * the same state even between syslog-ng restarts.
53 *
54 * Thus PersistState has two memory areas to represent the information
55 * - the key store containing names and the associated offset
56 * information basically in a sequential file
57 * - the state area containing the values
58 *
59 * When a new NV pair is registered, syslog-ng allocates the requested
60 * area in the state, and writes a new record to the key store
61 * file containing the name of the state entry and its length
62 * information. If the same name is reused, it is appended to the
63 * key store, never updated in-line.
64 *
65 * If syslog-ng crashes then both the state and the keystore should be
66 * left where it was. The information processed the following way:
67 * - the key store file is read into memory and a new key store file is
68 * produced (to ensure that unused state entries are dropped)
69 * - a new state file is produced, again unused data is dropped
70 *
71 * Old persist files can be converted the same way.
72 *
73 * We're using a trick to represent both areas in the same file: some
74 * space is allocated initially for the key store and once that fills
75 * up, syslog-ng allocates another key store area and chains these
76 * areas up using a next pointer.
77 *
78 * Value blocks are prefixed with an 8 byte header, containing the
79 * following information:
80 * - block size (4 bytes)
81 * - format version (1 byte)
82 * - whether the block is in use (1 byte)
83 *
84 * Cleaning up:
85 * ------------
86 *
87 * It can be seen that no explicit deallocation is performed on the
88 * persistent file, in effect it could grow indefinitely. There's a
89 * simple cleanup procedure though:
90 *
91 * - on every startup, the persist file is rewritten, entries with an
92 * in_use bit set are copied to the new one, with the in_use bit cleared
93 * - whenever syslog-ng looks up (e.g. uses) an entry, its in_use bit is set again
94 *
95 * This way unused entries in the persist file are reaped when
96 * syslog-ng restarts.
97 *
98 * Trusts:
99 * -------
100 *
101 * We don't trust the on-disk file when following a reference
102 * (e.g. offset, or object size) however we do trust the internal hash
103 * table built after validating the disk contents. This means that if
104 * you look up a key in the hashtable you can use the returned offset
105 * blindly without checking. However when reading the same value from
106 * the file you need to check it whether it is inside the mapped file.
107 *
108 */
109
110 /* everything is big-endian */
111 typedef struct _PersistValueHeader
112 {
113 guint32 size;
114 guint8 in_use;
115 guint8 version;
116 guint16 __padding;
117 } PersistValueHeader;
118
119 /* lowest layer, "store" functions manage the file on disk */
120
121 static void
_wait_until_map_release(PersistState * self)122 _wait_until_map_release(PersistState *self)
123 {
124 g_mutex_lock(&self->mapped_lock);
125 while (self->mapped_counter)
126 g_cond_wait(&self->mapped_release_cond, &self->mapped_lock);
127 }
128
129 static gboolean
_increase_file_size(PersistState * self,guint32 new_size)130 _increase_file_size(PersistState *self, guint32 new_size)
131 {
132 gboolean result = TRUE;
133 ssize_t length = new_size - self->current_size;
134 gchar *pad_buffer = g_new0(gchar, length);
135 ssize_t rc = pwrite(self->fd, pad_buffer, length, self->current_size);
136 if (rc != length)
137 {
138 msg_error("Can't grow the persist file",
139 evt_tag_int("old_size", self->current_size),
140 evt_tag_int("new_size", new_size),
141 evt_tag_str("error", rc < 0 ? g_strerror(errno) : "short write"));
142 result = FALSE;
143 }
144 g_free(pad_buffer);
145 return result;
146 }
147
148 static gboolean
_grow_store(PersistState * self,guint32 new_size)149 _grow_store(PersistState *self, guint32 new_size)
150 {
151 int pgsize = getpagesize();
152 gboolean result = FALSE;
153
154 _wait_until_map_release(self);
155
156 if ((new_size & (pgsize-1)) != 0)
157 {
158 new_size = ((new_size / pgsize) + 1) * pgsize;
159 }
160
161 if (new_size > self->current_size)
162 {
163 if (!_increase_file_size(self, new_size))
164 goto exit;
165
166 if (self->current_map)
167 munmap(self->current_map, self->current_size);
168 self->current_size = new_size;
169 self->current_map = mmap(NULL, self->current_size, PROT_READ | PROT_WRITE, MAP_SHARED, self->fd, 0);
170 if (self->current_map == MAP_FAILED)
171 {
172 self->current_map = NULL;
173 goto exit;
174 }
175 self->header = (PersistFileHeader *) self->current_map;
176 memcpy(&self->header->magic, "SLP4", 4);
177 }
178 result = TRUE;
179 exit:
180 g_mutex_unlock(&self->mapped_lock);
181 return result;
182 }
183
184 static gboolean
_create_store(PersistState * self)185 _create_store(PersistState *self)
186 {
187 self->fd = open(self->temp_filename, O_RDWR | O_CREAT | O_TRUNC, 0600);
188 if (self->fd < 0)
189 {
190 msg_error("Error creating persistent state file",
191 evt_tag_str("filename", self->temp_filename),
192 evt_tag_error("error"));
193 return FALSE;
194 }
195 g_fd_set_cloexec(self->fd, TRUE);
196 self->current_key_block = offsetof(PersistFileHeader, initial_key_store);
197 self->current_key_ofs = 0;
198 self->current_key_size = sizeof((((PersistFileHeader *) NULL))->initial_key_store);
199 return _grow_store(self, PERSIST_FILE_INITIAL_SIZE);
200 }
201
202 static gboolean
_commit_store(PersistState * self)203 _commit_store(PersistState *self)
204 {
205 /* NOTE: we don't need to remap the file in case it is renamed */
206 return rename(self->temp_filename, self->committed_filename) >= 0;
207 }
208
209 static gboolean
_check_watermark(PersistState * self)210 _check_watermark(PersistState *self)
211 {
212 return (self->current_ofs + PERSIST_FILE_MAX_ENTRY_SIZE < self->current_size);
213 }
214
215 static inline gboolean
_check_free_space(PersistState * self,guint32 size)216 _check_free_space(PersistState *self, guint32 size)
217 {
218 return (size + sizeof(PersistValueHeader) + self->current_ofs) <= self->current_size;
219 }
220
221 static void
persist_state_run_error_handler(PersistState * self)222 persist_state_run_error_handler(PersistState *self)
223 {
224 if (self->error_handler.handler)
225 self->error_handler.handler(self->error_handler.cookie);
226 }
227
228 /* "value" layer that handles memory block allocation in the file, without working with keys */
229
230 static inline void
_check_max_entry_size(guint32 size)231 _check_max_entry_size(guint32 size)
232 {
233 g_assert(size + sizeof(PersistValueHeader) <= PERSIST_FILE_MAX_ENTRY_SIZE);
234 }
235
236 static PersistEntryHandle
_alloc_value(PersistState * self,guint32 orig_size,gboolean in_use,guint8 version)237 _alloc_value(PersistState *self, guint32 orig_size, gboolean in_use, guint8 version)
238 {
239 PersistEntryHandle result;
240 PersistValueHeader *header;
241 guint32 size = orig_size;
242
243 /* round up size to 8 bytes boundary */
244 if ((size & 0x7))
245 size = ((size >> 3) + 1) << 3;
246
247 _check_max_entry_size(size);
248
249 if (!_check_free_space(self, size))
250 {
251 msg_error("No more free space exhausted in persist file");
252 return 0;
253 }
254
255 result = self->current_ofs + sizeof(PersistValueHeader);
256
257 /* fill value header */
258 header = (PersistValueHeader *) persist_state_map_entry(self, self->current_ofs);
259 header->size = GUINT32_TO_BE(orig_size);
260 header->in_use = in_use;
261 header->version = version;
262 persist_state_unmap_entry(self, self->current_ofs);
263
264 self->current_ofs += size + sizeof(PersistValueHeader);
265
266 if (!_check_watermark(self) && !_grow_store(self, self->current_size + PERSIST_FILE_INITIAL_SIZE))
267 {
268 msg_error("Can't preallocate space for persist file",
269 evt_tag_int("current", self->current_size),
270 evt_tag_int("new_size", self->current_size + PERSIST_FILE_INITIAL_SIZE));
271 persist_state_run_error_handler(self);
272 }
273
274 return result;
275 }
276
277 static PersistValueHeader *
_map_header_of_entry_from_handle(PersistState * self,PersistEntryHandle handle)278 _map_header_of_entry_from_handle(PersistState *self, PersistEntryHandle handle)
279 {
280 PersistValueHeader *header;
281
282 if (handle > self->current_size)
283 {
284 msg_error("Corrupted handle in persist_state_lookup_entry, handle value too large",
285 evt_tag_printf("handle", "%08x", handle));
286 return NULL;
287 }
288 header = (PersistValueHeader *) persist_state_map_entry(self, handle - sizeof(PersistValueHeader));
289 if (GUINT32_FROM_BE(header->size) + handle > self->current_size)
290 {
291 msg_error("Corrupted entry header found in persist_state_lookup_entry, size too large",
292 evt_tag_printf("handle", "%08x", handle),
293 evt_tag_int("size", GUINT32_FROM_BE(header->size)),
294 evt_tag_int("file_size", self->current_size));
295 return NULL;
296 }
297 return header;
298 }
299
300 static void
_free_value(PersistState * self,PersistEntryHandle handle)301 _free_value(PersistState *self, PersistEntryHandle handle)
302 {
303 if (handle)
304 {
305 PersistValueHeader *header;
306
307 header = _map_header_of_entry_from_handle(self, handle);
308 header->in_use = 0;
309 persist_state_unmap_entry(self, handle);
310 }
311 }
312
313 /* key management */
314
315 static gboolean
_persist_state_lookup_key(PersistState * self,const gchar * key,PersistEntryHandle * handle)316 _persist_state_lookup_key(PersistState *self, const gchar *key, PersistEntryHandle *handle)
317 {
318 PersistEntry *entry;
319
320 entry = g_hash_table_lookup(self->keys, key);
321 if (entry)
322 {
323 *handle = entry->ofs;
324 return TRUE;
325 }
326 return FALSE;
327 }
328
329 gboolean
persist_state_rename_entry(PersistState * self,const gchar * old_key,const gchar * new_key)330 persist_state_rename_entry(PersistState *self, const gchar *old_key, const gchar *new_key)
331 {
332 PersistEntry *entry;
333 gpointer old_orig_key;
334
335 if (g_hash_table_lookup_extended(self->keys, old_key, &old_orig_key, (gpointer *)&entry))
336 {
337 if (g_hash_table_steal(self->keys, old_key))
338 {
339 g_free(old_orig_key);
340 g_hash_table_insert(self->keys, g_strdup(new_key), entry);
341 return TRUE;
342 }
343 }
344 return FALSE;
345 }
346
347
348 static void
_persist_state_copy_entry(PersistState * self,const PersistEntryHandle from,PersistEntryHandle to,gsize size)349 _persist_state_copy_entry(PersistState *self, const PersistEntryHandle from, PersistEntryHandle to, gsize size)
350 {
351 gpointer entry_from = persist_state_map_entry(self, from);
352 gpointer entry_to = persist_state_map_entry(self, to);
353
354 memcpy(entry_to, entry_from, size);
355
356 persist_state_unmap_entry(self, from);
357 persist_state_unmap_entry(self, to);
358 }
359
360 gboolean
persist_state_move_entry(PersistState * self,const gchar * old_key,const gchar * new_key)361 persist_state_move_entry(PersistState *self, const gchar *old_key, const gchar *new_key)
362 {
363 gsize size;
364 guint8 version;
365 PersistEntryHandle old_handle = persist_state_lookup_entry(self, old_key, &size, &version);
366 if (!old_handle)
367 return FALSE;
368
369 PersistEntryHandle new_handle = persist_state_alloc_entry(self, new_key, size);
370 if (!new_handle)
371 return FALSE;
372
373 _persist_state_copy_entry(self, old_handle, new_handle, size);
374 _free_value(self, old_handle);
375
376 msg_debug("Persistent entry moved",
377 evt_tag_str("from", old_key),
378 evt_tag_str("to", new_key));
379
380 return TRUE;
381 }
382
383 /*
384 * NOTE: can only be called from the main thread (e.g. log_pipe_init/deinit).
385 */
386 static gboolean
_add_key(PersistState * self,const gchar * key,PersistEntryHandle handle)387 _add_key(PersistState *self, const gchar *key, PersistEntryHandle handle)
388 {
389 PersistEntry *entry;
390 gboolean new_block_created = FALSE;
391 SerializeArchive *sa;
392
393 g_assert(key[0] != 0);
394
395 entry = g_new(PersistEntry, 1);
396 entry->ofs = handle;
397 g_hash_table_insert(self->keys, g_strdup(key), entry);
398
399 /* we try to insert the key into the current block first, then if it
400 doesn't fit, we create a new block */
401
402 while (1)
403 {
404 /* the size of the key block chain part, 4 byte for the empty string length, guint32 for the link to the next block */
405 guint32 chain_size = sizeof(guint32) + sizeof(guint32);
406 gboolean success;
407
408 gchar *key_area = persist_state_map_entry(self, self->current_key_block);
409
410 /* we reserve space for the next area pointer */
411 sa = serialize_buffer_archive_new(key_area + self->current_key_ofs,
412 self->current_key_size - self->current_key_ofs - chain_size);
413 sa->silent = TRUE;
414 success =
415 serialize_write_cstring(sa, key, -1) &&
416 serialize_write_uint32(sa, handle);
417
418 if (!success)
419 {
420 serialize_archive_free(sa);
421 if (!new_block_created)
422 {
423 PersistEntryHandle new_block;
424
425 /* we unmap the key_area as otherwise we can't grow because of the pending maps */
426 persist_state_unmap_entry(self, self->current_key_block);
427
428 /* ah, we couldn't fit into the current block, create a new one and link it off the old one */
429 new_block = _alloc_value(self, PERSIST_STATE_KEY_BLOCK_SIZE, TRUE, 0);
430 if (!new_block)
431 {
432 msg_error("Unable to allocate space in the persistent file for key store");
433 return FALSE;
434 }
435
436 key_area = persist_state_map_entry(self, self->current_key_block);
437 sa = serialize_buffer_archive_new(key_area + self->current_key_ofs, self->current_key_size - self->current_key_ofs);
438 if (!serialize_write_cstring(sa, "", 0) ||
439 !serialize_write_uint32(sa, new_block))
440 {
441 /* hmmm. now this is bad, we couldn't write the tail of the
442 block even though we always reserved space for it, this
443 is a programming error somewhere in this function. */
444 g_assert_not_reached();
445 }
446 serialize_archive_free(sa);
447 persist_state_unmap_entry(self, self->current_key_block);
448 self->current_key_block = new_block;
449 self->current_key_size = PERSIST_STATE_KEY_BLOCK_SIZE;
450 self->current_key_ofs = 0;
451 new_block_created = TRUE;
452 }
453 else
454 {
455 /* if this happens, that means that the current key
456 * entry won't fit even into a freshly initialized key
457 * block, this means that the key is too large. */
458 msg_error("Persistent key too large, it cannot be larger than somewhat less than 4k",
459 evt_tag_str("key", key));
460 persist_state_unmap_entry(self, self->current_key_block);
461 return FALSE;
462 }
463 }
464 else
465 {
466 const guint32 key_count = GUINT32_FROM_BE(self->header->key_count);
467 self->header->key_count = GUINT32_TO_BE(key_count + 1);
468 self->current_key_ofs += serialize_buffer_archive_get_pos(sa);
469 serialize_archive_free(sa);
470 persist_state_unmap_entry(self, self->current_key_block);
471 return TRUE;
472 }
473 }
474 g_assert_not_reached();
475 }
476
477 /* process an on-disk persist file into the current one */
478
479 /* function to load v2 and v3 format persistent files */
480 static gboolean
_load_v23(PersistState * self,gint version,SerializeArchive * sa)481 _load_v23(PersistState *self, gint version, SerializeArchive *sa)
482 {
483 gchar *key, *value;
484
485 while (serialize_read_cstring(sa, &key, NULL))
486 {
487 gsize len;
488 guint32 str_len;
489 if (key[0] && serialize_read_cstring(sa, &value, &len))
490 {
491 PersistEntryHandle new_handle;
492
493 /* add length of the string */
494 new_handle = _alloc_value(self, len + sizeof(str_len), FALSE, version);
495 gchar *new_block = persist_state_map_entry(self, new_handle);
496
497 /* NOTE: we add an extra length field to the old value, as our
498 * persist_state_lookup_string() needs that.
499 * persist_state_lookup_string is used to fetch disk queue file
500 * names. It could have been solved somewhat better, but now it
501 * doesn't justify a persist-state file format change.
502 */
503 str_len = GUINT32_TO_BE(len);
504 memcpy(new_block, &str_len, sizeof(str_len));
505 memcpy(new_block + sizeof(str_len), value, len);
506 persist_state_unmap_entry(self, new_handle);
507 /* add key to the current file */
508 _add_key(self, key, new_handle);
509 g_free(value);
510 g_free(key);
511 }
512 else
513 {
514 g_free(key);
515 break;
516 }
517 }
518 return TRUE;
519 }
520
521 static gboolean
_load_v4(PersistState * self,gboolean load_all_entries)522 _load_v4(PersistState *self, gboolean load_all_entries)
523 {
524 gint fd;
525 gint64 file_size;
526 gpointer map;
527 gpointer key_block;
528 guint32 key_size;
529 PersistFileHeader *header;
530 gint key_count, i;
531
532 fd = open(self->committed_filename, O_RDONLY);
533 if (fd < 0)
534 {
535 /* no previous data found */
536 return TRUE;
537 }
538
539 file_size = lseek(fd, 0, SEEK_END);
540 if (file_size > ((1LL << 31) - 1))
541 {
542 msg_error("Persistent file too large",
543 evt_tag_str("filename", self->committed_filename),
544 evt_tag_printf("size", "%" G_GINT64_FORMAT, file_size));
545 close(fd);
546 return FALSE;
547 }
548 map = mmap(NULL, file_size, PROT_READ, MAP_SHARED, fd, 0);
549 close(fd);
550 if (map == MAP_FAILED)
551 {
552 msg_error("Error mapping persistent file into memory",
553 evt_tag_str("filename", self->committed_filename),
554 evt_tag_error("error"));
555 return FALSE;
556 }
557 header = (PersistFileHeader *) map;
558
559 key_block = ((gchar *) map) + offsetof(PersistFileHeader, initial_key_store);
560 key_size = sizeof((((PersistFileHeader *) NULL))->initial_key_store);
561
562 key_count = GUINT32_FROM_BE(header->key_count);
563 i = 0;
564 while (i < key_count)
565 {
566 gchar *name;
567 guint32 entry_ofs, chain_ofs;
568 SerializeArchive *sa;
569
570 sa = serialize_buffer_archive_new(key_block, key_size);
571 while (i < key_count)
572 {
573 if (!serialize_read_cstring(sa, &name, NULL))
574 {
575 serialize_archive_free(sa);
576 msg_error("Persistent file format error, unable to fetch key name");
577 goto free_and_exit;
578 }
579 if (name[0])
580 {
581 if (serialize_read_uint32(sa, &entry_ofs))
582 {
583 PersistValueHeader *value_header;
584 i++;
585
586 if (entry_ofs < sizeof(PersistFileHeader) || entry_ofs > file_size)
587 {
588 serialize_archive_free(sa);
589 g_free(name);
590 msg_error("Persistent file format error, entry offset is out of bounds");
591 goto free_and_exit;
592 }
593
594 value_header = (PersistValueHeader *) ((gchar *) map + entry_ofs - sizeof(PersistValueHeader));
595 if ((value_header->in_use) || load_all_entries)
596 {
597 gpointer new_block;
598 PersistEntryHandle new_handle;
599
600 new_handle = _alloc_value(self, GUINT32_FROM_BE(value_header->size), FALSE, value_header->version);
601 new_block = persist_state_map_entry(self, new_handle);
602 memcpy(new_block, value_header + 1, GUINT32_FROM_BE(value_header->size));
603 persist_state_unmap_entry(self, new_handle);
604 /* add key to the current file */
605 _add_key(self, name, new_handle);
606 }
607 g_free(name);
608 }
609 else
610 {
611 /* bad format */
612 serialize_archive_free(sa);
613 g_free(name);
614 msg_error("Persistent file format error, unable to fetch key name");
615 goto free_and_exit;
616 }
617 }
618 else
619 {
620 g_free(name);
621 if (serialize_read_uint32(sa, &chain_ofs))
622 {
623 /* end of block, chain to the next one */
624 if (chain_ofs == 0 || chain_ofs > file_size)
625 {
626 msg_error("Persistent file format error, key block chain offset is too large or zero",
627 evt_tag_printf("key_block", "%08lx", (gulong) ((gchar *) key_block - (gchar *) map)),
628 evt_tag_printf("key_size", "%d", key_size),
629 evt_tag_int("ofs", chain_ofs));
630 serialize_archive_free(sa);
631 goto free_and_exit;
632 }
633 key_block = ((gchar *) map) + chain_ofs;
634 key_size = GUINT32_FROM_BE(*(guint32 *) (((gchar *) key_block) - sizeof(PersistValueHeader)));
635 if (chain_ofs + key_size > file_size)
636 {
637 msg_error("Persistent file format error, key block size is too large",
638 evt_tag_int("key_size", key_size));
639 serialize_archive_free(sa);
640 goto free_and_exit;
641 }
642 break;
643 }
644 else
645 {
646 serialize_archive_free(sa);
647 msg_error("Persistent file format error, unable to fetch chained key block offset");
648 goto free_and_exit;
649 }
650 }
651 }
652 serialize_archive_free(sa);
653 }
654 free_and_exit:
655 munmap(map, file_size);
656 return TRUE;
657 }
658
659 static gboolean
_load(PersistState * self,gboolean all_errors_are_fatal,gboolean load_all_entries)660 _load(PersistState *self, gboolean all_errors_are_fatal, gboolean load_all_entries)
661 {
662 FILE *persist_file;
663 gboolean success = FALSE;
664
665 persist_file = fopen(self->committed_filename, "r");
666 if (persist_file)
667 {
668 SerializeArchive *sa;
669 gchar magic[4];
670 gint version;
671
672 sa = serialize_file_archive_new(persist_file);
673 serialize_read_blob(sa, magic, 4);
674 if (memcmp(magic, "SLP", 3) != 0)
675 {
676 msg_error("Persistent configuration file is in invalid format, ignoring");
677 success = all_errors_are_fatal ? FALSE : TRUE;
678 goto close_and_exit;
679 }
680 version = magic[3] - '0';
681 if (version >= 2 && version <= 3)
682 {
683 success = _load_v23(self, version, sa);
684 }
685 else if (version == 4)
686 {
687 success = _load_v4(self, load_all_entries);
688 }
689 else
690 {
691 msg_error("Persistent configuration file has an unsupported major version, ignoring",
692 evt_tag_int("version", version));
693 success = TRUE;
694 }
695 close_and_exit:
696 fclose(persist_file);
697 serialize_archive_free(sa);
698 }
699 else
700 {
701 if (all_errors_are_fatal)
702 {
703 msg_error("Failed to open persist file!",
704 evt_tag_str("filename", self->committed_filename),
705 evt_tag_str("error", strerror(errno)));
706 success = FALSE;
707 }
708 else
709 success = TRUE;
710 }
711 return success;
712 }
713
714
715 /**
716 * All persistent data accesses must be guarded by a _map and _unmap
717 * call in order to get a dereferencable pointer. It is not safe to
718 * save this pointer for longer terms as the underlying mapping may
719 * change when the file grows.
720 *
721 * Threading NOTE: this can be called from any kind of threads.
722 *
723 * NOTE: it is not safe to keep an entry mapped while synchronizing with the
724 * main thread (e.g. mutexes, condvars, main_loop_call()), because
725 * map_entry() may block the main thread in _grow_store().
726 **/
727 gpointer
persist_state_map_entry(PersistState * self,PersistEntryHandle handle)728 persist_state_map_entry(PersistState *self, PersistEntryHandle handle)
729 {
730 /* we count the number of mapped entries in order to know if we're
731 * safe to remap the file region */
732 g_assert(handle);
733 g_mutex_lock(&self->mapped_lock);
734 self->mapped_counter++;
735 g_mutex_unlock(&self->mapped_lock);
736 return (gpointer) (((gchar *) self->current_map) + (guint32) handle);
737 }
738
739 /*
740 * Threading NOTE: this can be called from any kind of threads.
741 */
742 void
persist_state_unmap_entry(PersistState * self,PersistEntryHandle handle)743 persist_state_unmap_entry(PersistState *self, PersistEntryHandle handle)
744 {
745 g_mutex_lock(&self->mapped_lock);
746 g_assert(self->mapped_counter >= 1);
747 self->mapped_counter--;
748 if (self->mapped_counter == 0)
749 {
750 g_cond_signal(&self->mapped_release_cond);
751 }
752 g_mutex_unlock(&self->mapped_lock);
753 }
754
755 static PersistValueHeader *
_map_header_of_entry(PersistState * self,const gchar * persist_name,PersistEntryHandle * handle)756 _map_header_of_entry(PersistState *self, const gchar *persist_name, PersistEntryHandle *handle)
757 {
758 if (!_persist_state_lookup_key(self, persist_name, handle))
759 return NULL;
760
761 return _map_header_of_entry_from_handle(self, *handle);
762
763 }
764
765 PersistEntryHandle
persist_state_alloc_entry(PersistState * self,const gchar * persist_name,gsize alloc_size)766 persist_state_alloc_entry(PersistState *self, const gchar *persist_name, gsize alloc_size)
767 {
768 PersistEntryHandle handle;
769
770 persist_state_remove_entry(self, persist_name);
771
772 handle = _alloc_value(self, alloc_size, TRUE, self->version);
773 if (!handle)
774 return 0;
775
776 if (!_add_key(self, persist_name, handle))
777 {
778 _free_value(self, handle);
779 return 0;
780 }
781
782 return handle;
783 }
784
785 PersistEntryHandle
persist_state_lookup_entry(PersistState * self,const gchar * key,gsize * size,guint8 * version)786 persist_state_lookup_entry(PersistState *self, const gchar *key, gsize *size, guint8 *version)
787 {
788 PersistEntryHandle handle;
789 PersistValueHeader *header;
790
791 header = _map_header_of_entry(self, key, &handle);
792 if (header)
793 {
794 header->in_use = TRUE;
795 *size = GUINT32_FROM_BE(header->size);
796 *version = header->version;
797 persist_state_unmap_entry(self, handle);
798 return handle;
799 }
800 else
801 return 0;
802 }
803
804 gboolean
persist_state_entry_exists(PersistState * self,const gchar * persist_name)805 persist_state_entry_exists(PersistState *self, const gchar *persist_name)
806 {
807 PersistEntryHandle handle;
808 return _persist_state_lookup_key(self, persist_name, &handle);
809 }
810
811 gboolean
persist_state_remove_entry(PersistState * self,const gchar * key)812 persist_state_remove_entry(PersistState *self, const gchar *key)
813 {
814 PersistEntryHandle handle;
815
816 if (!_persist_state_lookup_key(self, key, &handle))
817 return FALSE;
818
819 _free_value(self, handle);
820 return TRUE;
821 }
822
823 typedef struct _PersistStateKeysForeachData
824 {
825 PersistStateForeachFunc func;
826 gpointer userdata;
827 PersistState *storage;
828 } PersistStateKeysForeachData;
829
830 static void
_foreach_entry_func(gpointer key,gpointer value,gpointer userdata)831 _foreach_entry_func(gpointer key, gpointer value, gpointer userdata)
832 {
833 PersistStateKeysForeachData *data = (PersistStateKeysForeachData *) userdata;
834 PersistEntry *entry = (PersistEntry *) value;
835 gchar *name = (gchar *) key;
836
837 PersistValueHeader *header = persist_state_map_entry(data->storage, entry->ofs - sizeof(PersistValueHeader));
838 gint size = GUINT32_FROM_BE(header->size);
839 persist_state_unmap_entry(data->storage, entry->ofs - sizeof(PersistValueHeader));
840
841 PersistEntryHandle original_handler = entry->ofs;
842 gpointer state = persist_state_map_entry(data->storage, entry->ofs);
843 data->func(name, size, state, data->userdata);
844 persist_state_unmap_entry(data->storage, original_handler);
845 }
846
847 void
persist_state_foreach_entry(PersistState * self,PersistStateForeachFunc func,gpointer userdata)848 persist_state_foreach_entry(PersistState *self, PersistStateForeachFunc func, gpointer userdata)
849 {
850 PersistStateKeysForeachData data;
851
852 data.func = func;
853 data.userdata = userdata;
854 data.storage = self;
855
856 g_hash_table_foreach(self->keys, _foreach_entry_func, &data);
857 }
858
859 /* easier to use string based interface */
860 gchar *
persist_state_lookup_string(PersistState * self,const gchar * key,gsize * length,guint8 * version)861 persist_state_lookup_string(PersistState *self, const gchar *key, gsize *length, guint8 *version)
862 {
863 PersistEntryHandle handle;
864 gpointer block;
865 SerializeArchive *sa;
866 gchar *result;
867 gsize result_len, size;
868 guint8 result_version;
869 gboolean success;
870
871 if (!(handle = persist_state_lookup_entry(self, key, &size, &result_version)))
872 return NULL;
873 block = persist_state_map_entry(self, handle);
874 sa = serialize_buffer_archive_new(block, size);
875 success = serialize_read_cstring(sa, &result, &result_len);
876 serialize_archive_free(sa);
877 persist_state_unmap_entry(self, handle);
878 if (!success)
879 return NULL;
880 if (length)
881 *length = result_len;
882 if (version)
883 *version = result_version;
884 return result;
885 }
886
887 void
persist_state_alloc_string(PersistState * self,const gchar * persist_name,const gchar * value,gssize len)888 persist_state_alloc_string(PersistState *self, const gchar *persist_name, const gchar *value, gssize len)
889 {
890 PersistEntryHandle handle;
891 SerializeArchive *sa;
892 GString *buf;
893 gboolean success;
894 gpointer block;
895
896 if (len < 0)
897 len = strlen(value);
898
899 buf = g_string_sized_new(len + 5);
900 sa = serialize_string_archive_new(buf);
901
902 success = serialize_write_cstring(sa, value, len);
903 g_assert(success == TRUE);
904
905 serialize_archive_free(sa);
906
907 handle = persist_state_alloc_entry(self, persist_name, buf->len);
908 block = persist_state_map_entry(self, handle);
909 memcpy(block, buf->str, buf->len);
910 persist_state_unmap_entry(self, handle);
911 g_string_free(buf, TRUE);
912 }
913
914 const gchar *
persist_state_get_filename(PersistState * self)915 persist_state_get_filename(PersistState *self)
916 {
917 return self->committed_filename;
918 }
919
920 gboolean
persist_state_start_dump(PersistState * self)921 persist_state_start_dump(PersistState *self)
922 {
923 if (!_create_store(self))
924 return FALSE;
925 if (!_load(self, TRUE, TRUE))
926 return FALSE;
927 return TRUE;
928 }
929
930 gboolean
persist_state_start_edit(PersistState * self)931 persist_state_start_edit(PersistState *self)
932 {
933 if (!_create_store(self))
934 return FALSE;
935 if (!_load(self, FALSE, TRUE))
936 return FALSE;
937 return TRUE;
938 }
939
940 gboolean
persist_state_start(PersistState * self)941 persist_state_start(PersistState *self)
942 {
943 if (!_create_store(self))
944 return FALSE;
945 if (!_load(self, FALSE, FALSE))
946 return FALSE;
947 return TRUE;
948 }
949
950 /*
951 * This function commits the current store as the "persistent" file by
952 * renaming the temp file we created to build the loaded
953 * information. Once this function returns, then the current
954 * persistent file will be visible to the next relaunch of syslog-ng,
955 * even if we crashed.
956 */
957 gboolean
persist_state_commit(PersistState * self)958 persist_state_commit(PersistState *self)
959 {
960 if (!_commit_store(self))
961 return FALSE;
962 return TRUE;
963 }
964
965 static void
_destroy(PersistState * self)966 _destroy(PersistState *self)
967 {
968 g_mutex_lock(&self->mapped_lock);
969 g_assert(self->mapped_counter == 0);
970 g_mutex_unlock(&self->mapped_lock);
971
972 if (self->fd >= 0)
973 close(self->fd);
974 if (self->current_map)
975 munmap(self->current_map, self->current_size);
976 unlink(self->temp_filename);
977
978 g_mutex_clear(&self->mapped_lock);
979 g_cond_clear(&self->mapped_release_cond);
980 g_free(self->temp_filename);
981 g_free(self->committed_filename);
982 g_hash_table_destroy(self->keys);
983 }
984
985 static void
_init(PersistState * self,gchar * committed_filename,gchar * temp_filename)986 _init(PersistState *self, gchar *committed_filename, gchar *temp_filename)
987 {
988 self->keys = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
989 self->current_ofs = sizeof(PersistFileHeader);
990 g_mutex_init(&self->mapped_lock);
991 g_cond_init(&self->mapped_release_cond);
992 self->version = 4;
993 self->fd = -1;
994 self->committed_filename = committed_filename;
995 self->temp_filename = temp_filename;
996 }
997
998 /*
999 * This routine should revert to the persist_state_new() state,
1000 * e.g. just like the PersistState object wasn't started yet.
1001 */
1002 void
persist_state_cancel(PersistState * self)1003 persist_state_cancel(PersistState *self)
1004 {
1005 gchar *committed_filename, *temp_filename;
1006 committed_filename = g_strdup(self->committed_filename);
1007 temp_filename = g_strdup(self->temp_filename);
1008
1009 _destroy(self);
1010
1011 memset(self, 0, sizeof(*self));
1012
1013 _init(self, committed_filename, temp_filename);
1014 }
1015
1016 PersistState *
persist_state_new(const gchar * filename)1017 persist_state_new(const gchar *filename)
1018 {
1019 PersistState *self = g_new0(PersistState, 1);
1020
1021 _init(self, g_strdup(filename), g_strdup_printf("%s-", filename));
1022 return self;
1023 }
1024
1025 void
persist_state_free(PersistState * self)1026 persist_state_free(PersistState *self)
1027 {
1028 _destroy(self);
1029 g_free(self);
1030 }
1031
1032 void
persist_state_set_global_error_handler(PersistState * self,void (* handler)(gpointer user_data),gpointer user_data)1033 persist_state_set_global_error_handler(PersistState *self, void (*handler)(gpointer user_data), gpointer user_data)
1034 {
1035 self->error_handler.handler = handler;
1036 self->error_handler.cookie = user_data;
1037 }
1038