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