Merge pull request #2453 from poettering/journalctl-f

journalctl --fields logic
This commit is contained in:
Tom Gundersen
2016-02-03 15:36:06 +01:00
10 changed files with 432 additions and 38 deletions
+15 -6
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;
+28 -2
View File
@@ -138,6 +138,8 @@ static enum {
ACTION_SYNC,
ACTION_ROTATE,
ACTION_VACUUM,
ACTION_LIST_FIELDS,
ACTION_LIST_FIELD_NAMES,
} arg_action = ACTION_SHOW;
typedef struct BootId {
@@ -320,6 +322,7 @@ static void help(void) {
"\nCommands:\n"
" -h --help Show this help text\n"
" --version Show package version\n"
" -N --fields List all field names currently used\n"
" -F --field=FIELD List all values that a specified field takes\n"
" --disk-usage Show total disk usage of all journal files\n"
" --vacuum-size=BYTES Reduce disk usage below specified size\n"
@@ -416,6 +419,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "unit", required_argument, NULL, 'u' },
{ "user-unit", required_argument, NULL, ARG_USER_UNIT },
{ "field", required_argument, NULL, 'F' },
{ "fields", no_argument, NULL, 'N' },
{ "catalog", no_argument, NULL, 'x' },
{ "list-catalog", no_argument, NULL, ARG_LIST_CATALOG },
{ "dump-catalog", no_argument, NULL, ARG_DUMP_CATALOG },
@@ -437,7 +441,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "hefo:aln::qmb::kD:p:c:S:U:t:u:F:xrM:", options, NULL)) >= 0)
while ((c = getopt_long(argc, argv, "hefo:aln::qmb::kD:p:c:S:U:t:u:NF:xrM:", options, NULL)) >= 0)
switch (c) {
@@ -774,9 +778,14 @@ static int parse_argv(int argc, char *argv[]) {
break;
case 'F':
arg_action = ACTION_LIST_FIELDS;
arg_field = optarg;
break;
case 'N':
arg_action = ACTION_LIST_FIELD_NAMES;
break;
case 'x':
arg_catalog = true;
break;
@@ -2081,6 +2090,8 @@ int main(int argc, char *argv[]) {
case ACTION_DISK_USAGE:
case ACTION_LIST_BOOTS:
case ACTION_VACUUM:
case ACTION_LIST_FIELDS:
case ACTION_LIST_FIELD_NAMES:
/* These ones require access to the journal files, continue below. */
break;
@@ -2163,7 +2174,20 @@ int main(int argc, char *argv[]) {
goto finish;
}
case ACTION_LIST_FIELD_NAMES: {
const char *field;
SD_JOURNAL_FOREACH_FIELD(j, field) {
printf("%s\n", field);
n_shown ++;
}
r = 0;
goto finish;
}
case ACTION_SHOW:
case ACTION_LIST_FIELDS:
break;
default:
@@ -2217,10 +2241,12 @@ int main(int argc, char *argv[]) {
log_debug("Journal filter: %s", filter);
}
if (arg_field) {
if (arg_action == ACTION_LIST_FIELDS) {
const void *data;
size_t size;
assert(arg_field);
r = sd_journal_set_data_threshold(j, 0);
if (r < 0) {
log_error_errno(r, "Failed to unset data size threshold: %m");
+162 -10
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);
}
@@ -2512,24 +2520,20 @@ _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_
* traversed files. */
found = false;
ORDERED_HASHMAP_FOREACH(of, j->files, i) {
Object *oo;
uint64_t op;
if (of == j->unique_file)
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)
/* 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_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), &oo, &op);
r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), NULL, NULL);
if (r < 0)
return r;
if (r > 0)
if (r > 0) {
found = true;
break;
}
}
if (found)
@@ -2552,6 +2556,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);
+8
View File
@@ -481,3 +481,11 @@ global:
sd_bus_path_encode_many;
sd_listen_fds_with_names;
} LIBSYSTEMD_226;
LIBSYSTEMD_229 {
global:
sd_journal_has_runtime_files;
sd_journal_has_persistent_files;
sd_journal_enumerate_fields;
sd_journal_restart_fields;
} LIBSYSTEMD_227;
+11 -2
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;