sd-journal: add an API to enumerate known field names of the journal

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.
This commit is contained in:
Lennart Poettering 2016-01-27 18:59:29 +01:00
parent b72190e90f
commit eb86030ec0
8 changed files with 389 additions and 26 deletions

View File

@ -72,6 +72,7 @@ MANPAGES += \
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 \
@ -237,6 +238,7 @@ MANPAGES_ALIAS += \
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 \
@ -397,6 +399,7 @@ MANPAGES_ALIAS += \
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 \
@ -565,6 +568,7 @@ man/SD_JOURNAL_CURRENT_USER.3: man/sd_journal_open.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
@ -725,6 +729,7 @@ man/sd_journal_printv.3: man/sd_journal_print.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
@ -1017,6 +1022,9 @@ man/SD_JOURNAL_FOREACH_BACKWARDS.html: man/sd_journal_next.html
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)
@ -1497,6 +1505,9 @@ man/sd_journal_reliable_fd.html: man/sd_journal_get_fd.html
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)
@ -2534,6 +2545,7 @@ EXTRA_DIST += \
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 \

View File

@ -77,6 +77,8 @@
<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>,
@ -111,6 +113,8 @@
<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>,

View File

@ -0,0 +1,161 @@
<?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 &lt;systemd/sd-journal.h&gt;</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 &lt;stdio.h&gt;
#include &lt;string.h&gt;
#include &lt;systemd/sd-journal.h&gt;
int main(int argc, char *argv[]) {
sd_journal *j;
const char *field;
int r;
r = sd_journal_open(&amp;j, SD_JOURNAL_LOCAL_ONLY);
if (r &lt; 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>

View File

@ -128,6 +128,11 @@
<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>
@ -167,25 +172,25 @@
#include &lt;systemd/sd-journal.h&gt;
int main(int argc, char *argv[]) {
sd_journal *j;
const void *d;
size_t l;
int r;
sd_journal *j;
const void *d;
size_t l;
int r;
r = sd_journal_open(&amp;j, SD_JOURNAL_LOCAL_ONLY);
if (r &lt; 0) {
fprintf(stderr, "Failed to open journal: %s\n", strerror(-r));
return 1;
}
r = sd_journal_query_unique(j, "_SYSTEMD_UNIT");
if (r &lt; 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;
r = sd_journal_open(&amp;j, SD_JOURNAL_LOCAL_ONLY);
if (r &lt; 0) {
fprintf(stderr, "Failed to open journal: %s\n", strerror(-r));
return 1;
}
r = sd_journal_query_unique(j, "_SYSTEMD_UNIT");
if (r &lt; 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>
@ -198,6 +203,7 @@ int main(int argc, char *argv[]) {
<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>

View File

@ -103,18 +103,27 @@ struct sd_journal {
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;

View File

@ -1338,6 +1338,13 @@ static void remove_file_real(sd_journal *j, JournalFile *f) {
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 ++;
@ -1806,6 +1813,7 @@ _public_ void sd_journal_close(sd_journal *j) {
free(j->path);
free(j->prefix);
free(j->unique_field);
free(j->fields_buffer);
free(j);
}
@ -2552,6 +2560,154 @@ _public_ void sd_journal_restart_unique(sd_journal *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);

View File

@ -481,3 +481,9 @@ global:
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;

View File

@ -129,6 +129,9 @@ int sd_journal_query_unique(sd_journal *j, const char *field);
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);
@ -142,22 +145,28 @@ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **text);
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;