This adds two new calls to get the list of all journal fields names currently in use.
This is the low-level support to implement the feature requested in #2176 in a more optimized way.
man/sd_id128_to_string.3 \
man/sd_is_fifo.3 \
man/sd_journal_add_match.3 \
+ man/sd_journal_enumerate_fields.3 \
man/sd_journal_get_catalog.3 \
man/sd_journal_get_cursor.3 \
man/sd_journal_get_cutoff_realtime_usec.3 \
man/SD_JOURNAL_FOREACH.3 \
man/SD_JOURNAL_FOREACH_BACKWARDS.3 \
man/SD_JOURNAL_FOREACH_DATA.3 \
+ man/SD_JOURNAL_FOREACH_FIELD.3 \
man/SD_JOURNAL_FOREACH_UNIQUE.3 \
man/SD_JOURNAL_INVALIDATE.3 \
man/SD_JOURNAL_LOCAL_ONLY.3 \
man/sd_journal_process.3 \
man/sd_journal_reliable_fd.3 \
man/sd_journal_restart_data.3 \
+ man/sd_journal_restart_fields.3 \
man/sd_journal_restart_unique.3 \
man/sd_journal_seek_cursor.3 \
man/sd_journal_seek_monotonic_usec.3 \
man/SD_JOURNAL_FOREACH.3: man/sd_journal_next.3
man/SD_JOURNAL_FOREACH_BACKWARDS.3: man/sd_journal_next.3
man/SD_JOURNAL_FOREACH_DATA.3: man/sd_journal_get_data.3
+man/SD_JOURNAL_FOREACH_FIELD.3: man/sd_journal_enumerate_fields.3
man/SD_JOURNAL_FOREACH_UNIQUE.3: man/sd_journal_query_unique.3
man/SD_JOURNAL_INVALIDATE.3: man/sd_journal_get_fd.3
man/SD_JOURNAL_LOCAL_ONLY.3: man/sd_journal_open.3
man/sd_journal_process.3: man/sd_journal_get_fd.3
man/sd_journal_reliable_fd.3: man/sd_journal_get_fd.3
man/sd_journal_restart_data.3: man/sd_journal_get_data.3
+man/sd_journal_restart_fields.3: man/sd_journal_enumerate_fields.3
man/sd_journal_restart_unique.3: man/sd_journal_query_unique.3
man/sd_journal_seek_cursor.3: man/sd_journal_seek_head.3
man/sd_journal_seek_monotonic_usec.3: man/sd_journal_seek_head.3
man/SD_JOURNAL_FOREACH_DATA.html: man/sd_journal_get_data.html
$(html-alias)
+man/SD_JOURNAL_FOREACH_FIELD.html: man/sd_journal_enumerate_fields.html
+ $(html-alias)
+
man/SD_JOURNAL_FOREACH_UNIQUE.html: man/sd_journal_query_unique.html
$(html-alias)
man/sd_journal_restart_data.html: man/sd_journal_get_data.html
$(html-alias)
+man/sd_journal_restart_fields.html: man/sd_journal_enumerate_fields.html
+ $(html-alias)
+
man/sd_journal_restart_unique.html: man/sd_journal_query_unique.html
$(html-alias)
man/sd_id128_to_string.xml \
man/sd_is_fifo.xml \
man/sd_journal_add_match.xml \
+ man/sd_journal_enumerate_fields.xml \
man/sd_journal_get_catalog.xml \
man/sd_journal_get_cursor.xml \
man/sd_journal_get_cutoff_realtime_usec.xml \
<citerefentry><refentrytitle>sd_journal_get_realtime_usec</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_journal_add_match</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_journal_seek_head</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_journal_query_enumerate</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_journal_enumerate_fields</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_journal_get_cursor</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_journal_get_cutoff_realtime_usec</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_journal_get_cutoff_monotonic_usec</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_journal_get_realtime_usec</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_journal_add_match</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_journal_seek_head</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_journal_query_enumerate</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_journal_enumerate_fields</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_journal_get_cursor</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_journal_get_cutoff_realtime_usec</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_journal_get_cutoff_monotonic_usec</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
--- /dev/null
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd 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.
+
+ systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+-->
+
+<refentry id="sd_journal_enumerate_fields">
+
+ <refentryinfo>
+ <title>sd_journal_enumerate_fields</title>
+ <productname>systemd</productname>
+
+ <authorgroup>
+ <author>
+ <contrib>Developer</contrib>
+ <firstname>Lennart</firstname>
+ <surname>Poettering</surname>
+ <email>lennart@poettering.net</email>
+ </author>
+ </authorgroup>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>sd_journal_enumerate_fields</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd_journal_enumerate_fields</refname>
+ <refname>sd_journal_restart_fields</refname>
+ <refname>SD_JOURNAL_FOREACH_FIELD</refname>
+ <refpurpose>Read used field names from the journal</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include <systemd/sd-journal.h></funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>int <function>sd_journal_enumerate_fields</function></funcdef>
+ <paramdef>sd_journal *<parameter>j</parameter></paramdef>
+ <paramdef>const char **<parameter>field</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>sd_journal_restart_fields</function></funcdef>
+ <paramdef>sd_journal *<parameter>j</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef><function>SD_JOURNAL_FOREACH_FIELD</function></funcdef>
+ <paramdef>sd_journal *<parameter>j</parameter></paramdef>
+ <paramdef>const char *<parameter>field</parameter></paramdef>
+ </funcprototype>
+
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><function>sd_journal_enumerate_fields()</function> may be used to iterate through all field names used in the
+ opened journal files. On each invocation the next field name is returned. The order of the returned field names is
+ not defined. It takes two arguments: the journal context object, plus a pointer to a constant string pointer where
+ the field name is stored in. The returned data is in a read-only memory map and is only valid until the next
+ invocation of <function>sd_journal_enumerate_fields()</function>. Note that this call is subject to the data field
+ size threshold as controlled by <function>sd_journal_set_data_threshold()</function>.</para>
+
+ <para><function>sd_journal_restart_fields()</function> resets the field name enumeration index to the beginning of
+ the list. The next invocation of <function>sd_journal_enumerate_fields()</function> will return the first field
+ name again.</para>
+
+ <para>The <function>SD_JOURNAL_FOREACH_FIELD()</function> macro may be used as a handy wrapper around
+ <function>sd_journal_restart_fields()</function> and <function>sd_journal_enumerate_fields()</function>.</para>
+
+ <para>These functions currently are not influenced by matches set with <function>sd_journal_add_match()</function>
+ but this might change in a later version of this software.</para>
+
+ <para>To retrieve the possible values a specific field can take use
+ <citerefentry><refentrytitle>sd_journal_query_unique</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para><function>sd_journal_enumerate_fields()</function> returns a
+ positive integer if the next field name has been read, 0 when no
+ more field names are known, or a negative errno-style error code.
+ <function>sd_journal_restart_fields()</function> returns
+ nothing.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Notes</title>
+
+ <para>The <function>sd_journal_enumerate_fields()</function> and <function>sd_journal_restart_fields()</function>
+ interfaces are available as a shared library, which can be compiled and linked to with the
+ <constant>libsystemd</constant> <citerefentry
+ project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry> file.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>Use the <function>SD_JOURNAL_FOREACH_FIELD</function> macro to iterate through all field names in use in the
+ current journal.</para>
+
+ <programlisting>#include <stdio.h>
+#include <string.h>
+#include <systemd/sd-journal.h>
+
+int main(int argc, char *argv[]) {
+ sd_journal *j;
+ const char *field;
+ int r;
+
+ r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
+ if (r < 0) {
+ fprintf(stderr, "Failed to open journal: %s\n", strerror(-r));
+ return 1;
+ }
+ SD_JOURNAL_FOREACH_FIELD(j, field)
+ printf("%s\n", field);
+ sd_journal_close(j);
+ return 0;
+}</programlisting>
+
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd.journal-fields</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd-journal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_journal_open</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_journal_query_unique</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_journal_get_data</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_journal_add_match</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
<para>Note that these functions currently are not influenced by
matches set with <function>sd_journal_add_match()</function> but
this might change in a later version of this software.</para>
+
+ <para>To enumerate all field names currently in use (and thus all suitable field parameters for
+ <function>sd_journal_query_unique()</function>), use the
+ <citerefentry><refentrytitle>sd_journal_enumerate_fields</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ call.</para>
</refsect1>
<refsect1>
#include <systemd/sd-journal.h>
int main(int argc, char *argv[]) {
- sd_journal *j;
- const void *d;
- size_t l;
- int r;
-
- r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
- if (r < 0) {
- fprintf(stderr, "Failed to open journal: %s\n", strerror(-r));
- return 1;
- }
- r = sd_journal_query_unique(j, "_SYSTEMD_UNIT");
- if (r < 0) {
- fprintf(stderr, "Failed to query journal: %s\n", strerror(-r));
- return 1;
- }
- SD_JOURNAL_FOREACH_UNIQUE(j, d, l)
- printf("%.*s\n", (int) l, (const char*) d);
- sd_journal_close(j);
- return 0;
+ sd_journal *j;
+ const void *d;
+ size_t l;
+ int r;
+
+ r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
+ if (r < 0) {
+ fprintf(stderr, "Failed to open journal: %s\n", strerror(-r));
+ return 1;
+ }
+ r = sd_journal_query_unique(j, "_SYSTEMD_UNIT");
+ if (r < 0) {
+ fprintf(stderr, "Failed to query journal: %s\n", strerror(-r));
+ return 1;
+ }
+ SD_JOURNAL_FOREACH_UNIQUE(j, d, l)
+ printf("%.*s\n", (int) l, (const char*) d);
+ sd_journal_close(j);
+ return 0;
}</programlisting>
</refsect1>
<citerefentry><refentrytitle>systemd.journal-fields</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd-journal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_journal_open</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd_journal_enumerate_fields</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_journal_get_data</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_journal_add_match</refentrytitle><manvolnum>3</manvolnum></citerefentry>
</para>
unsigned current_invalidate_counter, last_invalidate_counter;
usec_t last_process_usec;
+ /* Iterating through unique fields and their data values */
char *unique_field;
JournalFile *unique_file;
uint64_t unique_offset;
+ /* Iterating through known fields */
+ JournalFile *fields_file;
+ uint64_t fields_offset;
+ uint64_t fields_hash_table_index;
+ char *fields_buffer;
+ size_t fields_buffer_allocated;
+
int flags;
- bool on_network;
- bool no_new_files;
- bool unique_file_lost; /* File we were iterating over got
- removed, and there were no more
- files, so sd_j_enumerate_unique
- will return a value equal to 0. */
+ bool on_network:1;
+ bool no_new_files:1;
+ bool unique_file_lost:1; /* File we were iterating over got
+ removed, and there were no more
+ files, so sd_j_enumerate_unique
+ will return a value equal to 0. */
+ bool fields_file_lost:1;
bool has_runtime_files:1;
bool has_persistent_files:1;
j->unique_file_lost = true;
}
+ if (j->fields_file == f) {
+ j->fields_file = ordered_hashmap_next(j->files, j->fields_file->path);
+ j->fields_offset = 0;
+ if (!j->fields_file)
+ j->fields_file_lost = true;
+ }
+
journal_file_close(f);
j->current_invalidate_counter ++;
free(j->path);
free(j->prefix);
free(j->unique_field);
+ free(j->fields_buffer);
free(j);
}
j->unique_file_lost = false;
}
+_public_ int sd_journal_enumerate_fields(sd_journal *j, const char **field) {
+ int r;
+
+ assert_return(j, -EINVAL);
+ assert_return(!journal_pid_changed(j), -ECHILD);
+ assert_return(field, -EINVAL);
+
+ if (!j->fields_file) {
+ if (j->fields_file_lost)
+ return 0;
+
+ j->fields_file = ordered_hashmap_first(j->files);
+ if (!j->fields_file)
+ return 0;
+
+ j->fields_hash_table_index = 0;
+ j->fields_offset = 0;
+ }
+
+ for (;;) {
+ JournalFile *f, *of;
+ Iterator i;
+ uint64_t m;
+ Object *o;
+ size_t sz;
+ bool found;
+
+ f = j->fields_file;
+
+ if (j->fields_offset == 0) {
+ bool eof = false;
+
+ /* We are not yet positioned at any field. Let's pick the first one */
+ r = journal_file_map_field_hash_table(f);
+ if (r < 0)
+ return r;
+
+ m = le64toh(f->header->field_hash_table_size) / sizeof(HashItem);
+ for (;;) {
+ if (j->fields_hash_table_index >= m) {
+ /* Reached the end of the hash table, go to the next file. */
+ eof = true;
+ break;
+ }
+
+ j->fields_offset = le64toh(f->field_hash_table[j->fields_hash_table_index].head_hash_offset);
+
+ if (j->fields_offset != 0)
+ break;
+
+ /* Empty hash table bucket, go to next one */
+ j->fields_hash_table_index++;
+ }
+
+ if (eof) {
+ /* Proceed with next file */
+ j->fields_file = ordered_hashmap_next(j->files, f->path);
+ if (!j->fields_file) {
+ *field = NULL;
+ return 0;
+ }
+
+ j->fields_offset = 0;
+ j->fields_hash_table_index = 0;
+ continue;
+ }
+
+ } else {
+ /* We are already positioned at a field. If so, let's figure out the next field from it */
+
+ r = journal_file_move_to_object(f, OBJECT_FIELD, j->fields_offset, &o);
+ if (r < 0)
+ return r;
+
+ j->fields_offset = le64toh(o->field.next_hash_offset);
+ if (j->fields_offset == 0) {
+ /* Reached the end of the hash table chain */
+ j->fields_hash_table_index++;
+ continue;
+ }
+ }
+
+ /* We use OBJECT_UNUSED here, so that the iteator below doesn't remove our mmap window */
+ r = journal_file_move_to_object(f, OBJECT_UNUSED, j->fields_offset, &o);
+ if (r < 0)
+ return r;
+
+ /* Because we used OBJECT_UNUSED above, we need to do our type check manually */
+ if (o->object.type != OBJECT_FIELD) {
+ log_debug("%s:offset " OFSfmt ": object has type %i, expected %i", f->path, j->fields_offset, o->object.type, OBJECT_FIELD);
+ return -EBADMSG;
+ }
+
+ sz = le64toh(o->object.size) - offsetof(Object, field.payload);
+
+ /* Let's see if we already returned this field name before. */
+ found = false;
+ ORDERED_HASHMAP_FOREACH(of, j->files, i) {
+ if (of == f)
+ break;
+
+ /* Skip this file it didn't have any fields indexed */
+ if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) && le64toh(of->header->n_fields) <= 0)
+ continue;
+
+ r = journal_file_find_field_object_with_hash(of, o->field.payload, sz, le64toh(o->field.hash), NULL, NULL);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found)
+ continue;
+
+ /* Check if this is really a valid string containing no NUL byte */
+ if (memchr(o->field.payload, 0, sz))
+ return -EBADMSG;
+
+ if (sz > j->data_threshold)
+ sz = j->data_threshold;
+
+ if (!GREEDY_REALLOC(j->fields_buffer, j->fields_buffer_allocated, sz + 1))
+ return -ENOMEM;
+
+ memcpy(j->fields_buffer, o->field.payload, sz);
+ j->fields_buffer[sz] = 0;
+
+ if (!field_is_valid(j->fields_buffer))
+ return -EBADMSG;
+
+ *field = j->fields_buffer;
+ return 1;
+ }
+}
+
+_public_ void sd_journal_restart_fields(sd_journal *j) {
+ if (!j)
+ return;
+
+ j->fields_file = NULL;
+ j->fields_hash_table_index = 0;
+ j->fields_offset = 0;
+ j->fields_file_lost = false;
+}
+
_public_ int sd_journal_reliable_fd(sd_journal *j) {
assert_return(j, -EINVAL);
assert_return(!journal_pid_changed(j), -ECHILD);
sd_bus_path_encode_many;
sd_listen_fds_with_names;
} LIBSYSTEMD_226;
+
+LIBSYSTEMD_229 {
+global:
+ sd_journal_enumerate_fields;
+ sd_journal_restart_fields;
+} LIBSYSTEMD_227;
int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l);
void sd_journal_restart_unique(sd_journal *j);
+int sd_journal_enumerate_fields(sd_journal *j, const char **field);
+void sd_journal_restart_fields(sd_journal *j);
+
int sd_journal_get_fd(sd_journal *j);
int sd_journal_get_events(sd_journal *j);
int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec);
int sd_journal_has_runtime_files(sd_journal *j);
int sd_journal_has_persistent_files(sd_journal *j);
-/* the inverse condition avoids ambiguity of danling 'else' after the macro */
+/* The inverse condition avoids ambiguity of dangling 'else' after the macro */
#define SD_JOURNAL_FOREACH(j) \
if (sd_journal_seek_head(j) < 0) { } \
else while (sd_journal_next(j) > 0)
-/* the inverse condition avoids ambiguity of danling 'else' after the macro */
+/* The inverse condition avoids ambiguity of dangling 'else' after the macro */
#define SD_JOURNAL_FOREACH_BACKWARDS(j) \
if (sd_journal_seek_tail(j) < 0) { } \
else while (sd_journal_previous(j) > 0)
+/* Iterate through the data fields of the current journal entry */
#define SD_JOURNAL_FOREACH_DATA(j, data, l) \
for (sd_journal_restart_data(j); sd_journal_enumerate_data((j), &(data), &(l)) > 0; )
+/* Iterate through the all known values of a specific field */
#define SD_JOURNAL_FOREACH_UNIQUE(j, data, l) \
for (sd_journal_restart_unique(j); sd_journal_enumerate_unique((j), &(data), &(l)) > 0; )
+/* Iterate through all known field names */
+#define SD_JOURNAL_FOREACH_FIELD(j, field) \
+ for (sd_journal_restart_fields(j); sd_journal_enumerate_fields((j), &(field)) > 0; )
+
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_journal, sd_journal_close);
_SD_END_DECLARATIONS;