summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTomas Bzatek <tbzatek@redhat.com>2013-09-18 17:03:03 +0200
committerTomas Bzatek <tbzatek@redhat.com>2013-10-15 15:23:49 +0200
commit4bc2a81c75ec3625d72bbe5374a8b102dd3f97f1 (patch)
tree1effb045374bc5cd67eb83d375ecb78800ce72e2
parent650acac5a9bee23f4d820335811ba42cff3e1074 (diff)
downloadopenlmi-providers-4bc2a81c75ec3625d72bbe5374a8b102dd3f97f1.tar.gz
openlmi-providers-4bc2a81c75ec3625d72bbe5374a8b102dd3f97f1.tar.xz
openlmi-providers-4bc2a81c75ec3625d72bbe5374a8b102dd3f97f1.zip
journald: Support writing new records
This change adds ability to report new messages in the journal log. This is done by the LMI_JournalLogRecord.CreateInstance() method call taking only CreationClassName, LogCreationClassName, LogName and DataFormat key properties as mandatory, with no need to specify the MessageTimestamp and RecordID key properties. The returned instance will contain all the information with actual RecordID key property of the new record. As long as sd_journal_send() actually only queues the request, we need to watch for journal changes in order to find the new record. This is quite unfortunate as the daemon could be busy and we can't wait forever. Messages are available immediately usually, a timeout is set to 5 seconds for the moment. Record matching is done by comparing the message itself, the origin function name and PID. This brings sufficient amount of confidence though more sophisticated approach may be needed in future to ensure full uniqueness.
-rw-r--r--src/journald/LMI_JournalLogRecordProvider.c112
-rw-r--r--src/journald/instutil.c29
-rw-r--r--src/journald/instutil.h2
3 files changed, 142 insertions, 1 deletions
diff --git a/src/journald/LMI_JournalLogRecordProvider.c b/src/journald/LMI_JournalLogRecordProvider.c
index 34a8f93..03493b3 100644
--- a/src/journald/LMI_JournalLogRecordProvider.c
+++ b/src/journald/LMI_JournalLogRecordProvider.c
@@ -21,6 +21,7 @@
#include <konkret/konkret.h>
#include <cmpi/cmpimacs.h>
#include "LMI_JournalLogRecord.h"
+#include "LMI_JournalMessageLog.h"
#include <errno.h>
#include <glib.h>
@@ -31,6 +32,9 @@
#include "instutil.h"
+#define WRITE_RECORD_TIMEOUT 5000 /* ms */
+
+
static const CMPIBroker* _cb = NULL;
static sd_journal *journal_iter = NULL;
@@ -161,6 +165,25 @@ static CMPIStatus LMI_JournalLogRecordGetInstance(
KReturn(OK);
}
+static const char *
+get_string_property(const char *property_name, const CMPIInstance* ci)
+{
+ CMPIData d;
+ CMPIStatus rc = { CMPI_RC_OK, NULL };
+
+ d = CMGetProperty(ci, property_name, &rc);
+ if (rc.rc != CMPI_RC_OK || (d.state & CMPI_nullValue) == CMPI_nullValue ||
+ (d.state & CMPI_notFound) == CMPI_notFound || d.value.string == NULL)
+ return NULL;
+ return CMGetCharPtr(d.value.string);
+}
+
+#define J_RESULT_CHECK(r,j,msg) \
+ if (r < 0) { \
+ sd_journal_close(j); \
+ KReturn2(_cb, ERR_FAILED, msg ": %s\n", strerror(-r)); \
+ }
+
static CMPIStatus LMI_JournalLogRecordCreateInstance(
CMPIInstanceMI* mi,
const CMPIContext* cc,
@@ -168,7 +191,94 @@ static CMPIStatus LMI_JournalLogRecordCreateInstance(
const CMPIObjectPath* cop,
const CMPIInstance* ci)
{
- CMReturn(CMPI_RC_ERR_NOT_SUPPORTED);
+ const char *cr_cl_n;
+ const char *log_cr_cl_n;
+ const char *log_name;
+ const char *data_format;
+ sd_journal *journal;
+ int r;
+ CMPIrc rc;
+ bool found;
+
+ cr_cl_n = get_string_property("CreationClassName", ci);
+ log_cr_cl_n = get_string_property("LogCreationClassName", ci);
+ log_name = get_string_property("LogName", ci);
+ data_format = get_string_property("DataFormat", ci);
+
+ if (cr_cl_n == NULL || log_cr_cl_n == NULL || log_name == NULL || data_format == NULL)
+ KReturn(ERR_INVALID_PARAMETER);
+ if (strcmp(cr_cl_n, LMI_JournalLogRecord_ClassName) != 0 ||
+ strcmp(log_cr_cl_n, LMI_JournalMessageLog_ClassName) != 0 ||
+ strcmp(log_name, JOURNAL_MESSAGE_LOG_NAME) != 0) {
+ KReturn(ERR_INVALID_PARAMETER);
+ }
+
+ r = sd_journal_open(&journal, 0);
+ if (r < 0) {
+ KReturn2(_cb, ERR_FAILED, "Error opening journal: %s\n", strerror(-r));
+ }
+
+ r = sd_journal_seek_tail(journal);
+ J_RESULT_CHECK(r, journal, "Error seeking to the end of the journal");
+
+ /* Need to call at least one of the _previous()/_next() functions to make the iterator set */
+ r = sd_journal_previous(journal);
+ if (r > 0)
+ while ((r = sd_journal_next(journal)) > 0)
+ ;
+ J_RESULT_CHECK(r, journal, "Error seeking to the end of the journal");
+
+ /* Queue send new record */
+ r = sd_journal_send("MESSAGE=%s", data_format,
+ NULL);
+ J_RESULT_CHECK(r, journal, "Error writing new record to the journal");
+
+ r = sd_journal_wait(journal, WRITE_RECORD_TIMEOUT * 1000 /* usec */);
+ /* First wait call will likely return INVALIDATE, try again */
+ if (r == SD_JOURNAL_INVALIDATE)
+ r = sd_journal_wait(journal, WRITE_RECORD_TIMEOUT * 1000 /* usec */);
+ J_RESULT_CHECK(r, journal, "Error checking the journal for new records");
+
+ found = false;
+ while (sd_journal_next(journal) > 0) {
+ /* TODO: alternatively, we can use custom property and an unique identifier
+ * but let's not bloat the journal with unneccessary data for now */
+ r = match_journal_record(journal, data_format, __func__);
+ if (r == 1) {
+ const char *ns = KNameSpace(cop);
+ LMI_JournalLogRecord log_record;
+ char *cursor;
+
+ LMI_JournalLogRecord_InitFromObjectPath(&log_record, _cb, cop);
+ LMI_JournalLogRecord_Set_DataFormat(&log_record, data_format);
+ LMI_JournalLogRecord_Set_CreationClassName(&log_record, LMI_JournalLogRecord_ClassName);
+ LMI_JournalLogRecord_Set_LogCreationClassName(&log_record, LMI_JournalMessageLog_ClassName);
+ LMI_JournalLogRecord_Set_LogName(&log_record, JOURNAL_MESSAGE_LOG_NAME);
+
+ /* Get stable cursor string */
+ r = sd_journal_get_cursor(journal, &cursor);
+ J_RESULT_CHECK(r, journal, "Failed to get cursor position");
+ LMI_JournalLogRecord_Set_RecordID(&log_record, cursor);
+ free(cursor);
+
+ r = create_LMI_JournalLogRecord(journal, &log_record, _cb);
+ if (r <= 0) {
+ sd_journal_close(journal);
+ KReturn2(_cb, ERR_FAILED, "Failed to create instance: %s\n", strerror(-r));
+ }
+
+ CMReturnObjectPath(cr, LMI_JournalLogRecord_ToObjectPath(&log_record, NULL));
+ CMReturnDone(cr);
+ found = true;
+ break;
+ }
+ J_RESULT_CHECK(r, journal, "Failed to find newly written record");
+ }
+ sd_journal_close(journal);
+
+ if (! found)
+ KReturn2(_cb, ERR_FAILED, "Failed to find newly written record\n");
+ CMReturn(CMPI_RC_OK);
}
static CMPIStatus LMI_JournalLogRecordModifyInstance(
diff --git a/src/journald/instutil.c b/src/journald/instutil.c
index a38202a..e664621 100644
--- a/src/journald/instutil.c
+++ b/src/journald/instutil.c
@@ -20,6 +20,7 @@
#include <errno.h>
#include <syslog.h>
+#include <unistd.h>
#include <glib.h>
#include <konkret/konkret.h>
@@ -239,6 +240,34 @@ int create_LMI_JournalLogRecord(sd_journal *j,
return 1;
}
+int match_journal_record(sd_journal *j, const char *message, const char *code_func)
+{
+ gchar *msg = NULL;
+ gchar *pid = NULL;
+ gchar *cfunc = NULL;
+ char *conv_err = NULL;
+ long int pid_n;
+ int r;
+
+ r = dup_journal_data(j, "MESSAGE", &msg);
+ if (r < 0)
+ return r;
+ dup_journal_data(j, "_PID", &pid);
+ dup_journal_data(j, "CODE_FUNC", &cfunc);
+
+ if (pid)
+ pid_n = strtol(pid, &conv_err, 10);
+
+ r = msg && pid && cfunc &&
+ (strcmp(message, msg) == 0) && (strcmp(code_func, cfunc) == 0) &&
+ (conv_err == NULL || *conv_err == '\0') && (pid_n == getpid());
+
+ g_free(msg);
+ g_free(pid);
+ g_free(cfunc);
+
+ return r;
+}
void ind_init()
{
diff --git a/src/journald/instutil.h b/src/journald/instutil.h
index 0ba7ff1..1ea96a2 100644
--- a/src/journald/instutil.h
+++ b/src/journald/instutil.h
@@ -31,6 +31,8 @@
int create_LMI_JournalLogRecordRef(sd_journal *j, LMI_JournalLogRecordRef *ref, const CMPIBroker *_cb);
int create_LMI_JournalLogRecord(sd_journal *j, LMI_JournalLogRecord *rec, const CMPIBroker *_cb);
+int match_journal_record(sd_journal *j, const char *message, const char *code_func);
+
void ind_init();
bool ind_watcher(void **data);
bool ind_filter_cb(const CMPISelectExp *filter);