diff options
-rw-r--r-- | src/journald/LMI_JournalMessageLogProvider.c | 117 | ||||
-rw-r--r-- | src/journald/instutil.c | 347 | ||||
-rw-r--r-- | src/journald/instutil.h | 11 |
3 files changed, 462 insertions, 13 deletions
diff --git a/src/journald/LMI_JournalMessageLogProvider.c b/src/journald/LMI_JournalMessageLogProvider.c index c4d9457..cde9a33 100644 --- a/src/journald/LMI_JournalMessageLogProvider.c +++ b/src/journald/LMI_JournalMessageLogProvider.c @@ -21,12 +21,19 @@ #include <konkret/konkret.h> #include "LMI_JournalMessageLog.h" +#include <glib.h> #include <systemd/sd-journal.h> #include "globals.h" #include "journal.h" +#include "instutil.h" +/* As defined in CIM_MessageLog schema */ +#define CIM_MESSAGELOG_ITERATOR_RESULT_SUCCESS 0 +#define CIM_MESSAGELOG_ITERATOR_RESULT_NOT_SUPPORTED 1 +#define CIM_MESSAGELOG_ITERATOR_RESULT_FAILED 2 + static const CMPIBroker* _cb = NULL; static void LMI_JournalMessageLogInitialize() @@ -204,6 +211,7 @@ KUint32 LMI_JournalMessageLog_ClearLog( KUint32 result = KUINT32_INIT; KSetStatus(status, ERR_NOT_SUPPORTED); + KUint32_Set(&result, CIM_MESSAGELOG_ITERATOR_RESULT_NOT_SUPPORTED); return result; } @@ -216,8 +224,19 @@ KUint32 LMI_JournalMessageLog_PositionToFirstRecord( CMPIStatus* status) { KUint32 result = KUINT32_INIT; + gchar *iter_id; + + KSetStatus(status, OK); + + if ((iter_id = journal_iter_new(NULL, NULL)) == NULL) { + KUint32_Set(&result, CIM_MESSAGELOG_ITERATOR_RESULT_FAILED); + return result; + } + + KString_Set(IterationIdentifier, _cb, iter_id); + KUint32_Set(&result, CIM_MESSAGELOG_ITERATOR_RESULT_SUCCESS); + g_free(iter_id); - KSetStatus(status, ERR_NOT_SUPPORTED); return result; } @@ -232,8 +251,39 @@ KUint32 LMI_JournalMessageLog_PositionAtRecord( CMPIStatus* status) { KUint32 result = KUINT32_INIT; + gchar *iter_id; + sd_journal *journal; - KSetStatus(status, ERR_NOT_SUPPORTED); + if (IterationIdentifier->null || ! IterationIdentifier->exists || + MoveAbsolute->null || ! MoveAbsolute->exists || + RecordNumber->null || ! RecordNumber->exists) { + KSetStatus(status, ERR_INVALID_PARAMETER); + KUint32_Set(&result, CIM_MESSAGELOG_ITERATOR_RESULT_FAILED); + return result; + } + + if (MoveAbsolute->value != 0) { + KSetStatus2(_cb, status, ERR_INVALID_PARAMETER, "Absolute movement not supported\n"); + KUint32_Set(&result, CIM_MESSAGELOG_ITERATOR_RESULT_FAILED); + return result; + } + + iter_id = g_strdup(IterationIdentifier->chars); + if (! journal_iter_validate_id(&iter_id, &journal, NULL, _cb, status)) { + g_free(iter_id); + KUint32_Set(&result, CIM_MESSAGELOG_ITERATOR_RESULT_FAILED); + return result; + } + + if (! journal_iter_seek(&iter_id, journal, RecordNumber->value)) { + g_free(iter_id); + KUint32_Set(&result, CIM_MESSAGELOG_ITERATOR_RESULT_FAILED); + return result; + } + + KString_Set(IterationIdentifier, _cb, iter_id); + KUint32_Set(&result, CIM_MESSAGELOG_ITERATOR_RESULT_SUCCESS); + g_free(iter_id); return result; } @@ -249,8 +299,44 @@ KUint32 LMI_JournalMessageLog_GetRecord( CMPIStatus* status) { KUint32 result = KUINT32_INIT; + gchar *iter_id; + sd_journal *journal; + char *data; + guint i; + + if (IterationIdentifier->null || ! IterationIdentifier->exists || + PositionToNext->null || ! PositionToNext->exists) { + KSetStatus(status, ERR_INVALID_PARAMETER); + KUint32_Set(&result, CIM_MESSAGELOG_ITERATOR_RESULT_FAILED); + return result; + } - KSetStatus(status, ERR_NOT_SUPPORTED); + iter_id = g_strdup(IterationIdentifier->chars); + if (! journal_iter_validate_id(&iter_id, &journal, NULL, _cb, status)) { + g_free(iter_id); + KUint32_Set(&result, CIM_MESSAGELOG_ITERATOR_RESULT_FAILED); + return result; + } + + data = journal_iter_get_data(&iter_id, journal, PositionToNext->value == 1); + if (! data) { + g_free(iter_id); + KUint32_Set(&result, CIM_MESSAGELOG_ITERATOR_RESULT_FAILED); + return result; + } + + KUint8A_Init(RecordData, _cb, strlen(data)); + /* FIXME: This will feed the array with unsigned chars, NOT terminated by zero. + * Ideally we would like to pass string instead but the model is given. */ + /* TODO: do the real signed vs. unsigned char conversion? */ + for (i = 0; i < strlen(data); i++) + KUint8A_Set(RecordData, i, (unsigned char) data[i]); + + g_free(data); + + KString_Set(IterationIdentifier, _cb, iter_id); + KUint32_Set(&result, CIM_MESSAGELOG_ITERATOR_RESULT_SUCCESS); + g_free(iter_id); return result; } @@ -268,6 +354,7 @@ KUint32 LMI_JournalMessageLog_DeleteRecord( KUint32 result = KUINT32_INIT; KSetStatus(status, ERR_NOT_SUPPORTED); + KUint32_Set(&result, CIM_MESSAGELOG_ITERATOR_RESULT_NOT_SUPPORTED); return result; } @@ -285,6 +372,7 @@ KUint32 LMI_JournalMessageLog_WriteRecord( KUint32 result = KUINT32_INIT; KSetStatus(status, ERR_NOT_SUPPORTED); + KUint32_Set(&result, CIM_MESSAGELOG_ITERATOR_RESULT_NOT_SUPPORTED); return result; } @@ -297,8 +385,27 @@ KUint32 LMI_JournalMessageLog_CancelIteration( CMPIStatus* status) { KUint32 result = KUINT32_INIT; + gchar *iter_id_short; - KSetStatus(status, ERR_NOT_SUPPORTED); + if (IterationIdentifier->null || ! IterationIdentifier->exists) { + KSetStatus(status, ERR_INVALID_PARAMETER); + KUint32_Set(&result, CIM_MESSAGELOG_ITERATOR_RESULT_FAILED); + return result; + } + + if (! journal_iter_parse_iterator_string(IterationIdentifier->chars, &iter_id_short, NULL, NULL)) { + KSetStatus2(_cb, status, ERR_INVALID_PARAMETER, "Malformed IterationIdentifier argument"); + KUint32_Set(&result, CIM_MESSAGELOG_ITERATOR_RESULT_FAILED); + return result; + } + + KSetStatus(status, OK); + if (journal_iter_cancel(iter_id_short)) + KUint32_Set(&result, CIM_MESSAGELOG_ITERATOR_RESULT_SUCCESS); + else + KUint32_Set(&result, CIM_MESSAGELOG_ITERATOR_RESULT_FAILED); + + g_free(iter_id_short); return result; } @@ -313,6 +420,7 @@ KUint32 LMI_JournalMessageLog_FreezeLog( KUint32 result = KUINT32_INIT; KSetStatus(status, ERR_NOT_SUPPORTED); + KUint32_Set(&result, CIM_MESSAGELOG_ITERATOR_RESULT_NOT_SUPPORTED); return result; } @@ -329,6 +437,7 @@ KUint32 LMI_JournalMessageLog_FlagRecordForOverwrite( KUint32 result = KUINT32_INIT; KSetStatus(status, ERR_NOT_SUPPORTED); + KUint32_Set(&result, CIM_MESSAGELOG_ITERATOR_RESULT_NOT_SUPPORTED); return result; } diff --git a/src/journald/instutil.c b/src/journald/instutil.c index ac9e768..a38202a 100644 --- a/src/journald/instutil.c +++ b/src/journald/instutil.c @@ -22,6 +22,9 @@ #include <syslog.h> #include <glib.h> +#include <konkret/konkret.h> +#include <cmpi/cmpimacs.h> + #include "instutil.h" #include "globals.h" #include "journal.h" @@ -33,6 +36,14 @@ /* Assuming no thread safety */ static sd_journal *ind_journal = NULL; +/* LMI_JournalMessageLog iterators */ +static GHashTable *cmpi_iters = NULL; +G_LOCK_DEFINE_STATIC(cmpi_iters); +static sig_atomic_t cmpi_iters_count = 0; + +#define JOURNAL_ITER_PREFIX "LMI_JournalMessageLog_CMPI_Iter_" +#define JOURNAL_ITER_SEPARATOR "#" + int create_LMI_JournalLogRecordRef(sd_journal *j, LMI_JournalLogRecordRef *ref, @@ -84,32 +95,60 @@ static int dup_journal_data( return 0; } -int create_LMI_JournalLogRecord(sd_journal *j, - LMI_JournalLogRecord *rec, - const CMPIBroker *_cb) +static int get_record_message(sd_journal *j, gboolean full_format, gchar **out) { int r; - uint64_t usec; - CMPIDateTime *date; gchar *d; gchar *syslog_identifier = NULL; gchar *comm = NULL; gchar *pid = NULL; gchar *fake_pid = NULL; + gchar *realtime = NULL; + gchar *hostname = NULL; GString *str; - /* Construct the message */ + str = g_string_new(NULL); + if (! str) + return -ENOMEM; + /* Message format is inspired by journalctl short output, containing the identifier (process name), * PID and the message. In other words, it's the traditional syslog record format. * Keep in sync with systemd:src/shared/logs-show.c:output_short() */ r = dup_journal_data(j, "MESSAGE", &d); if (r < 0) return r; + dup_journal_data(j, "SYSLOG_IDENTIFIER", &syslog_identifier); dup_journal_data(j, "_COMM", &comm); dup_journal_data(j, "_PID", &pid); dup_journal_data(j, "SYSLOG_PID", &fake_pid); - str = g_string_new(NULL); + + if (full_format) { + /* Include timestamp and hostname */ + char buf[64]; + guint64 x = 0; + time_t t; + struct tm tm; + gchar *endptr = NULL; + + dup_journal_data(j, "_SOURCE_REALTIME_TIMESTAMP", &realtime); + dup_journal_data(j, "_HOSTNAME", &hostname); + if (realtime) { + g_ascii_strtoull(realtime, &endptr, 10); + if (endptr != NULL && *endptr != '\0') + x = 0; + } + if (x == 0) + r = sd_journal_get_realtime_usec(j, &x); + if (r >= 0) { + t = (time_t) (x / 1000000ULL); + r = strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm)); + if (r > 0) + g_string_append_printf(str, "%s ", buf); + } + if (hostname) + g_string_append_printf(str, "%s ", hostname); + } if (syslog_identifier || comm) g_string_append(str, syslog_identifier ? syslog_identifier : comm); if (pid || fake_pid) @@ -117,13 +156,33 @@ int create_LMI_JournalLogRecord(sd_journal *j, if (str->len > 0) g_string_append(str, ": "); g_string_append(str, d); - LMI_JournalLogRecord_Set_DataFormat(rec, str->str); - g_string_free(str, TRUE); + *out = g_string_free(str, FALSE); g_free(d); g_free(syslog_identifier); g_free(comm); g_free(pid); g_free(fake_pid); + g_free(realtime); + g_free(hostname); + + return 0; +} + +int create_LMI_JournalLogRecord(sd_journal *j, + LMI_JournalLogRecord *rec, + const CMPIBroker *_cb) +{ + int r; + uint64_t usec; + CMPIDateTime *date; + gchar *d; + + /* Construct the message */ + r = get_record_message(j, FALSE, &d); + if (r < 0) + return r; + LMI_JournalLogRecord_Set_DataFormat(rec, d); + g_free(d); /* Set timestamp */ r = sd_journal_get_realtime_usec(j, &usec); @@ -319,3 +378,273 @@ bool ind_filter_cb(const CMPISelectExp *filter) if (strcasecmp(rhs_str, LMI_JournalLogRecord_ClassName) == 0) return true; return false; } + +/* --------------------------------------------------------------------------- */ + +/* TODO: count references to the journal struct -- someone may cancel the iteration + * while others are still using the journal struct (racy) */ + +#define set_cmpi_status_fmt(CB, STATUS, CODE, MSG, ...) \ + { gchar *errs; \ + errs = g_strdup_printf(MSG, ##__VA_ARGS__); \ + KSetStatus2(CB, STATUS, CODE, errs); \ + g_free(errs);\ + } + +static gchar * +make_iterator_string(sd_journal *journal, const char *cursor, gchar *iter_id_short) +{ + return g_strdup_printf("%s%s%p%s%s", iter_id_short, JOURNAL_ITER_SEPARATOR, (void *)journal, JOURNAL_ITER_SEPARATOR, cursor); +} + +bool +journal_iter_parse_iterator_string(const char *iter_id, gchar **out_iter_id_short, gpointer *out_iter_ptr, gchar **out_iter_cursor) +{ + gchar **s; + bool res; + gpointer valid_p; + + res = (iter_id && strlen(iter_id) > 0); + if (res) + s = g_strsplit(iter_id, JOURNAL_ITER_SEPARATOR, 3); + res = res && s && g_strv_length(s) == 3 && strlen(s[0]) > 0 && strlen(s[1]) > 0 && strlen(s[2]) > 0; + valid_p = NULL; + res = res && (sscanf(s[1], "%p", &valid_p) == 1); + if (res && out_iter_id_short) + res = res && ((*out_iter_id_short = g_strdup(s[0])) != NULL); + if (res && out_iter_ptr) + *out_iter_ptr = valid_p; + if (res && out_iter_cursor) + res = res && ((*out_iter_cursor = g_strdup(s[2])) != NULL); + g_strfreev(s); + + return res; +} + +gchar * +journal_iter_new(const gchar *req_cursor, sd_journal **journal_out) +{ + gchar *iter_id = NULL; + gchar *iter_id_full = NULL; + char *cursor; + sd_journal *journal; + int r; + + if (journal_out) + *journal_out = NULL; + + r = sd_journal_open(&journal, 0); + if (r < 0) { + error("Error opening journal: %s\n", strerror(-r)); + return NULL; + } + + if (req_cursor) + r = sd_journal_seek_cursor(journal, req_cursor); + else + r = sd_journal_seek_head(journal); + + if (r < 0) { + error("Error seeking to the requested journal position: %s\n", strerror(-r)); + sd_journal_close(journal); + return NULL; + } + + r = sd_journal_next(journal); + if (r < 0) { + error("Error stepping next in the journal: %s\n", strerror(-r)); + sd_journal_close(journal); + return NULL; + } + + r = sd_journal_get_cursor(journal, &cursor); + if (r < 0) { + error("Error getting current cursor: %s\n", strerror(-r)); + sd_journal_close(journal); + return NULL; + } + + G_LOCK(cmpi_iters); + if (cmpi_iters == NULL) + cmpi_iters = g_hash_table_new_full(g_str_hash, g_str_equal, (GDestroyNotify) g_free, (GDestroyNotify) sd_journal_close); + if (cmpi_iters == NULL) { + error("Memory allocation failure\n"); + sd_journal_close(journal); + G_UNLOCK(cmpi_iters); + return NULL; + } + iter_id = g_strdup_printf("%s%d", JOURNAL_ITER_PREFIX, cmpi_iters_count++); + if (iter_id) + iter_id_full = make_iterator_string(journal, cursor, iter_id); + if (iter_id == NULL || iter_id_full == NULL) { + error("Memory allocation failure\n"); + sd_journal_close(journal); + } else { + g_hash_table_insert(cmpi_iters, iter_id, journal); + } + G_UNLOCK(cmpi_iters); + + if (iter_id_full && journal_out) + *journal_out = journal; + + return iter_id_full; +} + +bool +journal_iter_validate_id(gchar **iter_id, sd_journal **journal_out, gchar **prefix_out, const CMPIBroker *_cb, CMPIStatus *status) +{ + gboolean res; + gchar *iter_id_short, *iter_cursor; + gpointer iter_ptr; + + res = TRUE; + if (journal_out) + *journal_out = NULL; + if (prefix_out) + *prefix_out = NULL; + + if (! journal_iter_parse_iterator_string(*iter_id, &iter_id_short, &iter_ptr, &iter_cursor)) { + set_cmpi_status_fmt(_cb, status, ERR_INVALID_PARAMETER, "Malformed IterationIdentifier argument: \'%s\'\n", *iter_id); + return false; + } + KSetStatus(status, OK); + + if (journal_out) { + G_LOCK(cmpi_iters); + if (cmpi_iters) + *journal_out = g_hash_table_lookup(cmpi_iters, iter_id_short); + G_UNLOCK(cmpi_iters); + if (*journal_out == NULL || *journal_out != iter_ptr) { + /* Assume stale iterator ID, reopen journal and try to find the position by the cursor */ + warn("journal_iter_validate_id(): iterator pointer %p doesn't match with hashtable %p, reopening journal...\n", iter_ptr, *journal_out); + g_free(*iter_id); + *iter_id = journal_iter_new(iter_cursor, journal_out); + if (*iter_id == NULL) { + error("The IterationIdentifier is not valid anymore: \'%s\'\n", *iter_id); + res = FALSE; + } + } + } + + if (res && prefix_out) { + /* No need to check prefix validity as it's supposed to be used in journal_iter_cancel() only */ + res = res && ((*prefix_out = g_strdup(iter_id_short)) != NULL); + } + + g_free(iter_id_short); + g_free(iter_cursor); + return res; +} + + +bool +journal_iter_cancel(const char *iter_id) +{ + gboolean b; + + g_return_val_if_fail(iter_id != NULL, false); + + G_LOCK(cmpi_iters); + b = cmpi_iters && g_hash_table_remove(cmpi_iters, iter_id); + G_UNLOCK(cmpi_iters); + if (! b) { + error("IterationIdentifier \'%s\' not registered\n", iter_id); + return false; + } + + return true; +} + +static bool +update_iter(gchar **iter_id, sd_journal *journal) +{ + gchar *iter_id_short; + char *cursor; + int r; + + r = sd_journal_get_cursor(journal, &cursor); + if (r < 0) { + error("Error getting current cursor: %s\n", strerror(-r)); + return false; + } + + if (! journal_iter_parse_iterator_string(*iter_id, &iter_id_short, NULL, NULL)) + return false; + *iter_id = make_iterator_string(journal, cursor, iter_id_short); + return *iter_id != NULL; +} + + +bool +journal_iter_seek(gchar **iter_id, sd_journal *journal, gint64 position) +{ + int r; + + g_return_val_if_fail(journal != NULL, false); + + if (position == 0) { /* NOP */ + warn("journal_iter_seek(): Spurious seek request to relative position 0\n"); + return true; + } + + if (position > 0) + r = sd_journal_next_skip(journal, position); + else + r = sd_journal_previous_skip(journal, -position); + + if (r < 0) { + error("Error seeking to the requested position: %s\n", strerror(-r)); + return false; + } + + if (! update_iter(iter_id, journal)) { + error("Error seeking to the requested position\n"); + return false; + } + + return true; +} + +gchar * +journal_iter_get_data(gchar **iter_id, sd_journal *journal, gboolean step_next) +{ + gchar *d; + int r; + + g_return_val_if_fail(journal != NULL, false); + + /* Construct the message */ + r = get_record_message(journal, TRUE, &d); + if (r < 0) { + error("Error getting record message: %s\n", strerror(-r)); + return NULL; + } + + if (step_next) { + r = sd_journal_next(journal); + if (r < 0) { + error("Error advancing to the next record: %s\n", strerror(-r)); + g_free(d); + return NULL; + } + } + + if (! update_iter(iter_id, journal)) { + error("Error getting record message\n"); + return NULL; + } + + return d; +} + +/* FIXME: unused for the moment as the hash table is global, shared across instances */ +void +journal_iters_destroy() +{ + G_LOCK(cmpi_iters); + if (cmpi_iters != NULL) { + g_hash_table_destroy(cmpi_iters); + cmpi_iters = NULL; + } + G_UNLOCK(cmpi_iters); +} diff --git a/src/journald/instutil.h b/src/journald/instutil.h index ee823c5..0ba7ff1 100644 --- a/src/journald/instutil.h +++ b/src/journald/instutil.h @@ -21,6 +21,8 @@ #ifndef INSTUTIL_H_ #define INSTUTIL_H_ +#include <glib.h> +#include <konkret/konkret.h> #include <systemd/sd-journal.h> #include <ind_manager.h> @@ -35,4 +37,13 @@ bool ind_filter_cb(const CMPISelectExp *filter); bool ind_gather(const IMManager *manager, CMPIInstance **old, CMPIInstance **new, void *data); void ind_destroy(); +gchar * journal_iter_new(const gchar *req_cursor, sd_journal **journal_out); +bool journal_iter_parse_iterator_string(const char *iter_id, gchar **out_iter_id_short, gpointer *out_iter_ptr, gchar **out_iter_cursor); +bool journal_iter_validate_id(gchar **iter_id, sd_journal **journal_out, gchar **prefix_out, const CMPIBroker *_cb, CMPIStatus *status); +bool journal_iter_cancel(const gchar *iter_id); +bool journal_iter_seek(gchar **iter_id, sd_journal *journal, gint64 position); +gchar * journal_iter_get_data(gchar **iter_id, sd_journal *journal, gboolean step_next); +void journal_iters_destroy(); + + #endif /* INSTUTIL_H_ */ |