/* * Copyright (c) 2002-2012 Balabit * Copyright (c) 1998-2012 Balázs Scheidler * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * */ #ifndef PAYLOAD_H_INCLUDED #define PAYLOAD_H_INCLUDED #include "syslog-ng.h" #include "nvhandle-descriptors.h" typedef struct _NVTable NVTable; typedef struct _NVRegistry NVRegistry; typedef struct _NVIndexEntry NVIndexEntry; typedef struct _NVEntry NVEntry; typedef guint32 NVHandle; typedef gboolean (*NVTableForeachFunc)(NVHandle handle, const gchar *name, const gchar *value, gssize value_len, gpointer user_data); typedef gboolean (*NVTableForeachEntryFunc)(NVHandle handle, NVEntry *entry, NVIndexEntry *index_entry, gpointer user_data); #define NVHANDLE_MAX_VALUE ((NVHandle)-1) /* NVIndexEntry * this represents an entry in the handle based lookup index, embedded in an NVTable. * * NOTE: * The deserialization code (at least version 26) assumes that this can be * represented by a pair of guint32 instances. It is reading the entire * array back as such. Should you need to change the types here, please * ensure that you also update the nvtable deserialization code. */ struct _NVIndexEntry { NVHandle handle; guint32 ofs; }; struct _NVRegistry { /* number of static names that are statically allocated in each payload */ gint num_static_names; NVHandleDescArray *names; GHashTable *name_map; guint32 nvhandle_max_value; }; extern const gchar *null_string; void nv_registry_add_alias(NVRegistry *self, NVHandle handle, const gchar *alias); NVHandle nv_registry_get_handle(NVRegistry *self, const gchar *name); NVHandle nv_registry_alloc_handle(NVRegistry *self, const gchar *name); void nv_registry_set_handle_flags(NVRegistry *self, NVHandle handle, guint16 flags); void nv_registry_foreach(NVRegistry *self, GHFunc callback, gpointer user_data); NVRegistry *nv_registry_new(const gchar **static_names, guint32 nvhandle_max_value); void nv_registry_free(NVRegistry *self); static inline guint16 nv_registry_get_handle_flags(NVRegistry *self, NVHandle handle) { NVHandleDesc *stored; if (G_UNLIKELY(!handle)) return 0; stored = &nvhandle_desc_array_index(self->names, handle - 1); return stored->flags; } static inline const gchar * nv_registry_get_handle_name(NVRegistry *self, NVHandle handle, gssize *length) { NVHandleDesc *stored; if (G_UNLIKELY(!handle)) { if (length) *length = 4; return "None"; } if (handle - 1 >= self->names->len) { if (length) *length = 0; return NULL; } stored = &nvhandle_desc_array_index(self->names, handle - 1); if (G_LIKELY(length)) *length = stored->name_len; return stored->name; } typedef struct _NVReferencedSlice { NVHandle handle; guint32 ofs; guint32 len; guint8 type; gchar name[0]; } NVReferencedSlice; /* * Contains a name-value pair. */ struct _NVEntry { /* negative offset, counting from string table top, e.g. start of the string is at @top + ofs */ union { struct { /* make sure you don't exceed 8 bits here. So if you want to add new * bits, decrease the size of __bit_padding below */ guint8 indirect:1, referenced:1, unset:1, __bit_padding:5; }; guint8 flags; }; guint8 name_len; guint32 alloc_len; union { struct { guint32 value_len; /* variable data, first the name of this entry, then the value, both are NUL terminated */ gchar data[]; } vdirect; NVReferencedSlice vindirect; }; }; #define NV_ENTRY_DIRECT_HDR ((gsize) (&((NVEntry *) NULL)->vdirect.data)) #define NV_ENTRY_DIRECT_SIZE(name_len, value_len) ((value_len) + NV_ENTRY_DIRECT_HDR + (name_len) + 2) #define NV_ENTRY_INDIRECT_HDR (sizeof(NVEntry)) #define NV_ENTRY_INDIRECT_SIZE(name_len) (NV_ENTRY_INDIRECT_HDR + name_len + 1) static inline const gchar * nv_entry_get_name(NVEntry *self) { if (self->indirect) return self->vindirect.name; else return self->vdirect.data; } /* * Contains a set of ordered name-value pairs. * * This struct is used to track a set of name-value pairs that make up * a LogMessage structure. The storage layout is as concise as * possible to make it possible to serialize this payload as a single * writev() operation. * * Memory layout: * ============= * * || struct || static value offsets || dynamic value (id, offset) pairs || || stored (name, value) || * * Name value area: * - the name-value area grows down (e.g. lower addresses) from the end of the struct * - name-value pairs are referenced by the offset counting down from the end of the struct * - all NV pairs are positioned at 4 bytes boundary, so 32 bit variables in NVEntry * can be accessed in an aligned manner * * Static value offsets: * - a fixed size of guint32 array, containing 32 bit offsets for statically allocated entries * - the handles for static values have a low value and they match the index in this array * * Dynamic values: * - a dynamically sized NVIndexEntry array (contains ID + offset) * - dynamic values are sorted by the global ID to make handle->entry lookups fast * * Memory allocation * ================= * - the memory used by NVTable is managed by the caller, sometimes it is * allocated inside an existing data structure (we preallocate space * with LogMessage) * * - when the structure needs to grow the instance pointer itself needs to * be changed. In order to avoid doing that in all the API calls, a * separate nv_table_realloc() call is provided. * * - NVTable instances are reference counted, but the reference counts are * not thread safe (and accessing NVTable itself isn't either). When * reallocation is needed and multiple references exist, NVTable clones * itself and leaves the old copy be. * * - It is possible to clone an NVTable, which basically copies the * underlying memory contents. * * Limits * ====== * There might be various assumptions here and there in the code that fields * in this structure should be limited in values. These are as follows. * (the list is not necessarily comprehensive though, so please be careful * when changing types). * - index_size is used to allocate NVIndexEntry arrays on the stack, * so 2^16 * sizeof(NVIndexEntry) is allocated at most (512k). If you * however change this limit, please be careful to audit the * deserialization code. * */ struct _NVTable { /* byte order indication, etc. */ guint32 size; guint32 used; /* this used to be called num_dyn_entries in earlier versions, it matches * the type of the original type, so it is compatible with earlier * versions, but index_size is a more descriptive name */ guint16 index_size; guint8 num_static_entries; guint8 ref_cnt:7, borrowed:1; /* specifies if the memory used by NVTable was borrowed from the container struct */ /* variable data, see memory layout in the comment above */ union { guint32 __dummy_for_alignment; guint32 static_entries[0]; gchar data[0]; }; }; #define NV_TABLE_BOUND(x) (((x) + 0x3) & ~0x3) #define NV_TABLE_ADDR(self, x) ((gchar *) ((self)) + ((gssize)(x))) /* 256MB, this is an artificial limit, but must be less than MAX_GUINT32 as * we want to compare a guint32 to this variable without overflow. */ #define NV_TABLE_MAX_BYTES (256*1024*1024) /* this has to be large enough to hold the NVTable struct above and the * static values */ #define NV_TABLE_MIN_BYTES 128 gboolean nv_table_add_value(NVTable *self, NVHandle handle, const gchar *name, gsize name_len, const gchar *value, gsize value_len, gboolean *new_entry); gboolean nv_table_unset_value(NVTable *self, NVHandle handle); gboolean nv_table_add_value_indirect(NVTable *self, NVHandle handle, const gchar *name, gsize name_len, NVReferencedSlice *referenced_slice, gboolean *new_entry); gboolean nv_table_foreach(NVTable *self, NVRegistry *registry, NVTableForeachFunc func, gpointer user_data); gboolean nv_table_foreach_entry(NVTable *self, NVTableForeachEntryFunc func, gpointer user_data); NVTable *nv_table_new(gint num_static_values, gint index_size_hint, gint init_length); NVTable *nv_table_init_borrowed(gpointer space, gsize space_len, gint num_static_entries); gboolean nv_table_realloc(NVTable *self, NVTable **new); NVTable *nv_table_compact(NVTable *self); NVTable *nv_table_clone(NVTable *self, gint additional_space); NVTable *nv_table_ref(NVTable *self); void nv_table_unref(NVTable *self); static inline gboolean nv_table_is_handle_static(NVTable *self, NVHandle handle) { return (handle <= self->num_static_entries); } static inline gsize nv_table_get_alloc_size(gint num_static_entries, gint index_size_hint, gint init_length) { NVTable *self G_GNUC_UNUSED = NULL; gsize size; size = NV_TABLE_BOUND(init_length) + NV_TABLE_BOUND(sizeof(NVTable) + num_static_entries * sizeof( self->static_entries[0]) + index_size_hint * sizeof(NVIndexEntry)); if (size < NV_TABLE_MIN_BYTES) return NV_TABLE_MIN_BYTES; if (size > NV_TABLE_MAX_BYTES) size = NV_TABLE_MAX_BYTES; return size; } static inline gchar * nv_table_get_top(NVTable *self) { return NV_TABLE_ADDR(self, self->size); } static inline gchar * nv_table_get_bottom(NVTable *self) { return nv_table_get_top(self) - self->used; } static inline gchar * nv_table_get_ofs_table_top(NVTable *self) { return (gchar *) &self->data[self->num_static_entries * sizeof(self->static_entries[0]) + self->index_size * sizeof(NVIndexEntry)]; } static inline gboolean nv_table_alloc_check(NVTable *self, gsize alloc_size) { if (nv_table_get_bottom(self) - nv_table_get_ofs_table_top(self) < alloc_size) return FALSE; return TRUE; } /* private declarations for inline functions */ NVEntry *nv_table_get_entry_slow(NVTable *self, NVHandle handle, NVIndexEntry **index_entry, NVIndexEntry **index_slot); const gchar *nv_table_resolve_indirect(NVTable *self, NVEntry *entry, gssize *len); static inline NVEntry * __nv_table_get_entry(NVTable *self, NVHandle handle, guint16 num_static_entries, NVIndexEntry **index_entry, NVIndexEntry **index_slot) { guint32 ofs; NVIndexEntry *t1, *t2; if (!index_entry) index_entry = &t1; if (!index_slot) index_slot = &t2; if (G_UNLIKELY(!handle)) { *index_entry = NULL; *index_slot = NULL; return NULL; } if (G_LIKELY(nv_table_is_handle_static(self, handle))) { ofs = self->static_entries[handle - 1]; *index_entry = NULL; *index_slot = NULL; if (G_UNLIKELY(!ofs)) return NULL; return (NVEntry *) (nv_table_get_top(self) - ofs); } else { return nv_table_get_entry_slow(self, handle, index_entry, index_slot); } } static inline NVEntry * nv_table_get_entry(NVTable *self, NVHandle handle, NVIndexEntry **index_entry, NVIndexEntry **index_slot) { return __nv_table_get_entry(self, handle, self->num_static_entries, index_entry, index_slot); } static inline gboolean nv_table_is_value_set(NVTable *self, NVHandle handle) { return nv_table_get_entry(self, handle, NULL, NULL) != NULL; } static inline const gchar * nv_table_get_value_if_set(NVTable *self, NVHandle handle, gssize *length) { NVEntry *entry; entry = nv_table_get_entry(self, handle, NULL, NULL); if (!entry || entry->unset) { if (length) *length = 0; return NULL; } if (!entry->indirect) { if (length) *length = entry->vdirect.value_len; return entry->vdirect.data + entry->name_len + 1; } return nv_table_resolve_indirect(self, entry, length); } static inline const gchar * nv_table_get_value(NVTable *self, NVHandle handle, gssize *length) { const gchar *value = nv_table_get_value_if_set(self, handle, length); if (!value) return null_string; return value; } static inline NVIndexEntry * nv_table_get_index(NVTable *self) { return (NVIndexEntry *)&self->static_entries[self->num_static_entries]; } static inline NVEntry * nv_table_get_entry_at_ofs(NVTable *self, guint32 ofs) { if (!ofs) return NULL; return (NVEntry *)(nv_table_get_top(self) - ofs); } static inline guint32 nv_table_get_ofs_for_an_entry(NVTable *self, NVEntry *entry) { return (nv_table_get_top(self) - (gchar *) entry); } static inline gssize nv_table_get_memory_consumption(NVTable *self) { return sizeof(*self)+ self->num_static_entries*sizeof(self->static_entries[0])+ self->used; } #endif