1 /*
2 * Copyright (c) 2002-2012 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 #ifndef PAYLOAD_H_INCLUDED
26 #define PAYLOAD_H_INCLUDED
27
28 #include "syslog-ng.h"
29 #include "nvhandle-descriptors.h"
30
31 typedef struct _NVTable NVTable;
32 typedef struct _NVRegistry NVRegistry;
33 typedef struct _NVIndexEntry NVIndexEntry;
34 typedef struct _NVEntry NVEntry;
35 typedef guint32 NVHandle;
36 typedef gboolean (*NVTableForeachFunc)(NVHandle handle, const gchar *name, const gchar *value, gssize value_len,
37 gpointer user_data);
38 typedef gboolean (*NVTableForeachEntryFunc)(NVHandle handle, NVEntry *entry, NVIndexEntry *index_entry,
39 gpointer user_data);
40
41 #define NVHANDLE_MAX_VALUE ((NVHandle)-1)
42
43 /* NVIndexEntry
44 * this represents an entry in the handle based lookup index, embedded in an NVTable.
45 *
46 * NOTE:
47 * The deserialization code (at least version 26) assumes that this can be
48 * represented by a pair of guint32 instances. It is reading the entire
49 * array back as such. Should you need to change the types here, please
50 * ensure that you also update the nvtable deserialization code.
51 */
52 struct _NVIndexEntry
53 {
54 NVHandle handle;
55 guint32 ofs;
56 };
57
58 struct _NVRegistry
59 {
60 /* number of static names that are statically allocated in each payload */
61 gint num_static_names;
62 NVHandleDescArray *names;
63 GHashTable *name_map;
64 guint32 nvhandle_max_value;
65 };
66
67 extern const gchar *null_string;
68
69 void nv_registry_add_alias(NVRegistry *self, NVHandle handle, const gchar *alias);
70 NVHandle nv_registry_get_handle(NVRegistry *self, const gchar *name);
71 NVHandle nv_registry_alloc_handle(NVRegistry *self, const gchar *name);
72 void nv_registry_set_handle_flags(NVRegistry *self, NVHandle handle, guint16 flags);
73 void nv_registry_foreach(NVRegistry *self, GHFunc callback, gpointer user_data);
74 NVRegistry *nv_registry_new(const gchar **static_names, guint32 nvhandle_max_value);
75 void nv_registry_free(NVRegistry *self);
76
77 static inline guint16
nv_registry_get_handle_flags(NVRegistry * self,NVHandle handle)78 nv_registry_get_handle_flags(NVRegistry *self, NVHandle handle)
79 {
80 NVHandleDesc *stored;
81
82 if (G_UNLIKELY(!handle))
83 return 0;
84
85 stored = &nvhandle_desc_array_index(self->names, handle - 1);
86 return stored->flags;
87 }
88
89 static inline const gchar *
nv_registry_get_handle_name(NVRegistry * self,NVHandle handle,gssize * length)90 nv_registry_get_handle_name(NVRegistry *self, NVHandle handle, gssize *length)
91 {
92 NVHandleDesc *stored;
93
94 if (G_UNLIKELY(!handle))
95 {
96 if (length)
97 *length = 4;
98 return "None";
99 }
100
101 if (handle - 1 >= self->names->len)
102 {
103 if (length)
104 *length = 0;
105 return NULL;
106 }
107
108 stored = &nvhandle_desc_array_index(self->names, handle - 1);
109 if (G_LIKELY(length))
110 *length = stored->name_len;
111 return stored->name;
112 }
113
114 typedef struct _NVReferencedSlice
115 {
116 NVHandle handle;
117 guint32 ofs;
118 guint32 len;
119 guint8 type;
120
121 gchar name[0];
122 } NVReferencedSlice;
123
124 /*
125 * Contains a name-value pair.
126 */
127 struct _NVEntry
128 {
129 /* negative offset, counting from string table top, e.g. start of the string is at @top + ofs */
130 union
131 {
132 struct
133 {
134 /* make sure you don't exceed 8 bits here. So if you want to add new
135 * bits, decrease the size of __bit_padding below */
136 guint8 indirect:1,
137 referenced:1,
138 unset:1,
139 __bit_padding:5;
140 };
141 guint8 flags;
142 };
143 guint8 name_len;
144 guint32 alloc_len;
145 union
146 {
147 struct
148 {
149 guint32 value_len;
150 /* variable data, first the name of this entry, then the value, both are NUL terminated */
151 gchar data[];
152 } vdirect;
153
154 NVReferencedSlice vindirect;
155 };
156 };
157
158 #define NV_ENTRY_DIRECT_HDR ((gsize) (&((NVEntry *) NULL)->vdirect.data))
159 #define NV_ENTRY_DIRECT_SIZE(name_len, value_len) ((value_len) + NV_ENTRY_DIRECT_HDR + (name_len) + 2)
160 #define NV_ENTRY_INDIRECT_HDR (sizeof(NVEntry))
161 #define NV_ENTRY_INDIRECT_SIZE(name_len) (NV_ENTRY_INDIRECT_HDR + name_len + 1)
162
163 static inline const gchar *
nv_entry_get_name(NVEntry * self)164 nv_entry_get_name(NVEntry *self)
165 {
166 if (self->indirect)
167 return self->vindirect.name;
168 else
169 return self->vdirect.data;
170 }
171
172 /*
173 * Contains a set of ordered name-value pairs.
174 *
175 * This struct is used to track a set of name-value pairs that make up
176 * a LogMessage structure. The storage layout is as concise as
177 * possible to make it possible to serialize this payload as a single
178 * writev() operation.
179 *
180 * Memory layout:
181 * =============
182 *
183 * || struct || static value offsets || dynamic value (id, offset) pairs || <free space> || stored (name, value) ||
184 *
185 * Name value area:
186 * - the name-value area grows down (e.g. lower addresses) from the end of the struct
187 * - name-value pairs are referenced by the offset counting down from the end of the struct
188 * - all NV pairs are positioned at 4 bytes boundary, so 32 bit variables in NVEntry
189 * can be accessed in an aligned manner
190 *
191 * Static value offsets:
192 * - a fixed size of guint32 array, containing 32 bit offsets for statically allocated entries
193 * - the handles for static values have a low value and they match the index in this array
194 *
195 * Dynamic values:
196 * - a dynamically sized NVIndexEntry array (contains ID + offset)
197 * - dynamic values are sorted by the global ID to make handle->entry lookups fast
198 *
199 * Memory allocation
200 * =================
201 * - the memory used by NVTable is managed by the caller, sometimes it is
202 * allocated inside an existing data structure (we preallocate space
203 * with LogMessage)
204 *
205 * - when the structure needs to grow the instance pointer itself needs to
206 * be changed. In order to avoid doing that in all the API calls, a
207 * separate nv_table_realloc() call is provided.
208 *
209 * - NVTable instances are reference counted, but the reference counts are
210 * not thread safe (and accessing NVTable itself isn't either). When
211 * reallocation is needed and multiple references exist, NVTable clones
212 * itself and leaves the old copy be.
213 *
214 * - It is possible to clone an NVTable, which basically copies the
215 * underlying memory contents.
216 *
217 * Limits
218 * ======
219 * There might be various assumptions here and there in the code that fields
220 * in this structure should be limited in values. These are as follows.
221 * (the list is not necessarily comprehensive though, so please be careful
222 * when changing types).
223 * - index_size is used to allocate NVIndexEntry arrays on the stack,
224 * so 2^16 * sizeof(NVIndexEntry) is allocated at most (512k). If you
225 * however change this limit, please be careful to audit the
226 * deserialization code.
227 *
228 */
229 struct _NVTable
230 {
231 /* byte order indication, etc. */
232 guint32 size;
233 guint32 used;
234
235 /* this used to be called num_dyn_entries in earlier versions, it matches
236 * the type of the original type, so it is compatible with earlier
237 * versions, but index_size is a more descriptive name */
238 guint16 index_size;
239 guint8 num_static_entries;
240 guint8 ref_cnt:7,
241 borrowed:1; /* specifies if the memory used by NVTable was borrowed from the container struct */
242
243 /* variable data, see memory layout in the comment above */
244 union
245 {
246 guint32 __dummy_for_alignment;
247 guint32 static_entries[0];
248 gchar data[0];
249 };
250 };
251
252 #define NV_TABLE_BOUND(x) (((x) + 0x3) & ~0x3)
253 #define NV_TABLE_ADDR(self, x) ((gchar *) ((self)) + ((gssize)(x)))
254
255 /* 256MB, this is an artificial limit, but must be less than MAX_GUINT32 as
256 * we want to compare a guint32 to this variable without overflow. */
257 #define NV_TABLE_MAX_BYTES (256*1024*1024)
258
259 /* this has to be large enough to hold the NVTable struct above and the
260 * static values */
261 #define NV_TABLE_MIN_BYTES 128
262
263 gboolean nv_table_add_value(NVTable *self, NVHandle handle, const gchar *name, gsize name_len, const gchar *value,
264 gsize value_len, gboolean *new_entry);
265 gboolean nv_table_unset_value(NVTable *self, NVHandle handle);
266 gboolean nv_table_add_value_indirect(NVTable *self, NVHandle handle, const gchar *name, gsize name_len,
267 NVReferencedSlice *referenced_slice, gboolean *new_entry);
268
269 gboolean nv_table_foreach(NVTable *self, NVRegistry *registry, NVTableForeachFunc func, gpointer user_data);
270 gboolean nv_table_foreach_entry(NVTable *self, NVTableForeachEntryFunc func, gpointer user_data);
271
272 NVTable *nv_table_new(gint num_static_values, gint index_size_hint, gint init_length);
273 NVTable *nv_table_init_borrowed(gpointer space, gsize space_len, gint num_static_entries);
274 gboolean nv_table_realloc(NVTable *self, NVTable **new);
275 NVTable *nv_table_compact(NVTable *self);
276 NVTable *nv_table_clone(NVTable *self, gint additional_space);
277 NVTable *nv_table_ref(NVTable *self);
278 void nv_table_unref(NVTable *self);
279
280 static inline gboolean
nv_table_is_handle_static(NVTable * self,NVHandle handle)281 nv_table_is_handle_static(NVTable *self, NVHandle handle)
282 {
283 return (handle <= self->num_static_entries);
284 }
285
286 static inline gsize
nv_table_get_alloc_size(gint num_static_entries,gint index_size_hint,gint init_length)287 nv_table_get_alloc_size(gint num_static_entries, gint index_size_hint, gint init_length)
288 {
289 NVTable *self G_GNUC_UNUSED = NULL;
290 gsize size;
291
292 size = NV_TABLE_BOUND(init_length) + NV_TABLE_BOUND(sizeof(NVTable) + num_static_entries * sizeof(
293 self->static_entries[0]) + index_size_hint * sizeof(NVIndexEntry));
294 if (size < NV_TABLE_MIN_BYTES)
295 return NV_TABLE_MIN_BYTES;
296 if (size > NV_TABLE_MAX_BYTES)
297 size = NV_TABLE_MAX_BYTES;
298 return size;
299 }
300
301 static inline gchar *
nv_table_get_top(NVTable * self)302 nv_table_get_top(NVTable *self)
303 {
304 return NV_TABLE_ADDR(self, self->size);
305 }
306
307 static inline gchar *
nv_table_get_bottom(NVTable * self)308 nv_table_get_bottom(NVTable *self)
309 {
310 return nv_table_get_top(self) - self->used;
311 }
312
313 static inline gchar *
nv_table_get_ofs_table_top(NVTable * self)314 nv_table_get_ofs_table_top(NVTable *self)
315 {
316 return (gchar *) &self->data[self->num_static_entries * sizeof(self->static_entries[0]) +
317 self->index_size * sizeof(NVIndexEntry)];
318 }
319
320 static inline gboolean
nv_table_alloc_check(NVTable * self,gsize alloc_size)321 nv_table_alloc_check(NVTable *self, gsize alloc_size)
322 {
323 if (nv_table_get_bottom(self) - nv_table_get_ofs_table_top(self) < alloc_size)
324 return FALSE;
325 return TRUE;
326 }
327
328 /* private declarations for inline functions */
329 NVEntry *nv_table_get_entry_slow(NVTable *self, NVHandle handle, NVIndexEntry **index_entry, NVIndexEntry **index_slot);
330 const gchar *nv_table_resolve_indirect(NVTable *self, NVEntry *entry, gssize *len);
331
332
333 static inline NVEntry *
__nv_table_get_entry(NVTable * self,NVHandle handle,guint16 num_static_entries,NVIndexEntry ** index_entry,NVIndexEntry ** index_slot)334 __nv_table_get_entry(NVTable *self, NVHandle handle, guint16 num_static_entries, NVIndexEntry **index_entry,
335 NVIndexEntry **index_slot)
336 {
337 guint32 ofs;
338 NVIndexEntry *t1, *t2;
339
340 if (!index_entry)
341 index_entry = &t1;
342 if (!index_slot)
343 index_slot = &t2;
344
345 if (G_UNLIKELY(!handle))
346 {
347 *index_entry = NULL;
348 *index_slot = NULL;
349 return NULL;
350 }
351
352 if (G_LIKELY(nv_table_is_handle_static(self, handle)))
353 {
354 ofs = self->static_entries[handle - 1];
355 *index_entry = NULL;
356 *index_slot = NULL;
357 if (G_UNLIKELY(!ofs))
358 return NULL;
359 return (NVEntry *) (nv_table_get_top(self) - ofs);
360 }
361 else
362 {
363 return nv_table_get_entry_slow(self, handle, index_entry, index_slot);
364 }
365 }
366
367 static inline NVEntry *
nv_table_get_entry(NVTable * self,NVHandle handle,NVIndexEntry ** index_entry,NVIndexEntry ** index_slot)368 nv_table_get_entry(NVTable *self, NVHandle handle, NVIndexEntry **index_entry, NVIndexEntry **index_slot)
369 {
370 return __nv_table_get_entry(self, handle, self->num_static_entries, index_entry, index_slot);
371 }
372
373 static inline gboolean
nv_table_is_value_set(NVTable * self,NVHandle handle)374 nv_table_is_value_set(NVTable *self, NVHandle handle)
375 {
376 return nv_table_get_entry(self, handle, NULL, NULL) != NULL;
377 }
378
379 static inline const gchar *
nv_table_get_value_if_set(NVTable * self,NVHandle handle,gssize * length)380 nv_table_get_value_if_set(NVTable *self, NVHandle handle, gssize *length)
381 {
382 NVEntry *entry;
383
384 entry = nv_table_get_entry(self, handle, NULL, NULL);
385 if (!entry || entry->unset)
386 {
387 if (length)
388 *length = 0;
389 return NULL;
390 }
391
392 if (!entry->indirect)
393 {
394 if (length)
395 *length = entry->vdirect.value_len;
396 return entry->vdirect.data + entry->name_len + 1;
397 }
398 return nv_table_resolve_indirect(self, entry, length);
399 }
400
401 static inline const gchar *
nv_table_get_value(NVTable * self,NVHandle handle,gssize * length)402 nv_table_get_value(NVTable *self, NVHandle handle, gssize *length)
403 {
404 const gchar *value = nv_table_get_value_if_set(self, handle, length);
405
406 if (!value)
407 return null_string;
408 return value;
409 }
410
411 static inline NVIndexEntry *
nv_table_get_index(NVTable * self)412 nv_table_get_index(NVTable *self)
413 {
414 return (NVIndexEntry *)&self->static_entries[self->num_static_entries];
415 }
416
417 static inline NVEntry *
nv_table_get_entry_at_ofs(NVTable * self,guint32 ofs)418 nv_table_get_entry_at_ofs(NVTable *self, guint32 ofs)
419 {
420 if (!ofs)
421 return NULL;
422 return (NVEntry *)(nv_table_get_top(self) - ofs);
423 }
424
425 static inline guint32
nv_table_get_ofs_for_an_entry(NVTable * self,NVEntry * entry)426 nv_table_get_ofs_for_an_entry(NVTable *self, NVEntry *entry)
427 {
428 return (nv_table_get_top(self) - (gchar *) entry);
429 }
430
431 static inline gssize
nv_table_get_memory_consumption(NVTable * self)432 nv_table_get_memory_consumption(NVTable *self)
433 {
434 return sizeof(*self)+
435 self->num_static_entries*sizeof(self->static_entries[0])+
436 self->used;
437 }
438
439 #endif
440