static int journal_get_cursor (lua_State *L) { sd_journal *j = check_journal(L, 1); char *cursor; int err = sd_journal_get_cursor(j, &cursor); if (err != 0) return handle_error(L, -err); lua_pushstring(L, cursor); free(cursor); return 1; }
static void verify_contents(sd_journal *j, unsigned skip) { unsigned i; assert(j); i = 0; SD_JOURNAL_FOREACH(j) { const void *d; char *k, *c; size_t l; unsigned u; assert_se(sd_journal_get_cursor(j, &k) >= 0); printf("cursor: %s\n", k); free(k); assert_se(sd_journal_get_data(j, "MAGIC", &d, &l) >= 0); printf("\t%.*s\n", (int) l, (const char*) d); assert_se(sd_journal_get_data(j, "NUMBER", &d, &l) >= 0); assert_se(k = strndup(d, l)); printf("\t%s\n", k); if (skip > 0) { assert_se(safe_atou(k + 7, &u) >= 0); assert_se(i == u); i += skip; } free(k); assert_se(sd_journal_get_cursor(j, &c) >= 0); assert_se(sd_journal_test_cursor(j, c) > 0); free(c); } if (skip > 0) assert_se(i == N_ENTRIES); }
int main(int argc, char *argv[]) { JournalFile *one, *two, *three; char t[] = "/tmp/journal-stream-XXXXXX"; unsigned i; _cleanup_journal_close_ sd_journal *j = NULL; char *z; const void *data; size_t l; log_set_max_level(LOG_DEBUG); assert_se(mkdtemp(t)); assert_se(chdir(t) >= 0); assert_se(journal_file_open("one.journal", O_RDWR|O_CREAT, 0666, true, NULL, NULL, NULL, &one) == 0); assert_se(journal_file_open("two.journal", O_RDWR|O_CREAT, 0666, true, NULL, NULL, NULL, &two) == 0); assert_se(journal_file_open("three.journal", O_RDWR|O_CREAT, 0666, true, NULL, NULL, NULL, &three) == 0); for (i = 0; i < N_ENTRIES; i++) { char *p, *q; dual_timestamp ts; struct iovec iovec[2]; dual_timestamp_get(&ts); assert_se(asprintf(&p, "NUMBER=%u", i) >= 0); iovec[0].iov_base = p; iovec[0].iov_len = strlen(p); assert_se(asprintf(&q, "MAGIC=%s", i % 5 == 0 ? "quux" : "waldo") >= 0); iovec[1].iov_base = q; iovec[1].iov_len = strlen(q); if (i % 10 == 0) assert_se(journal_file_append_entry(three, &ts, iovec, 2, NULL, NULL, NULL) == 0); else { if (i % 3 == 0) assert_se(journal_file_append_entry(two, &ts, iovec, 2, NULL, NULL, NULL) == 0); assert_se(journal_file_append_entry(one, &ts, iovec, 2, NULL, NULL, NULL) == 0); } free(p); free(q); } journal_file_close(one); journal_file_close(two); journal_file_close(three); assert_se(sd_journal_open_directory(&j, t, 0) >= 0); assert_se(sd_journal_add_match(j, "MAGIC=quux", 0) >= 0); SD_JOURNAL_FOREACH_BACKWARDS(j) { _cleanup_free_ char *c = NULL; assert_se(sd_journal_get_data(j, "NUMBER", &data, &l) >= 0); printf("\t%.*s\n", (int) l, (const char*) data); assert_se(sd_journal_get_cursor(j, &c) >= 0); assert_se(sd_journal_test_cursor(j, c) > 0); } SD_JOURNAL_FOREACH(j) { _cleanup_free_ char *c = NULL; assert_se(sd_journal_get_data(j, "NUMBER", &data, &l) >= 0); printf("\t%.*s\n", (int) l, (const char*) data); assert_se(sd_journal_get_cursor(j, &c) >= 0); assert_se(sd_journal_test_cursor(j, c) > 0); } sd_journal_flush_matches(j); verify_contents(j, 1); printf("NEXT TEST\n"); assert_se(sd_journal_add_match(j, "MAGIC=quux", 0) >= 0); assert_se(z = journal_make_match_string(j)); printf("resulting match expression is: %s\n", z); free(z); verify_contents(j, 5); printf("NEXT TEST\n"); sd_journal_flush_matches(j); assert_se(sd_journal_add_match(j, "MAGIC=waldo", 0) >= 0); assert_se(sd_journal_add_match(j, "NUMBER=10", 0) >= 0); assert_se(sd_journal_add_match(j, "NUMBER=11", 0) >= 0); assert_se(sd_journal_add_match(j, "NUMBER=12", 0) >= 0); assert_se(z = journal_make_match_string(j)); printf("resulting match expression is: %s\n", z); free(z); verify_contents(j, 0); assert_se(sd_journal_query_unique(j, "NUMBER") >= 0); SD_JOURNAL_FOREACH_UNIQUE(j, data, l) printf("%.*s\n", (int) l, (const char*) data); assert_se(rm_rf_dangerous(t, false, true, false) >= 0); return 0; }
char *get_entry_string(){ const void *data; json_t *message = json_object(); size_t length; // journal meta information, prefixed by '__' char *cursor; uint64_t realtime_usec; uint64_t monotonic_usec; sd_id128_t boot_id; /* mapping from systemd- to GELF- field names */ const char *fn_sd_host="_HOSTNAME", *fn_gelf_host="host"; const char *fn_sd_msg="MESSAGE", *fn_gelf_msg="short_message"; const char /**fn_sd_time="__REALTIME_TIMESTAMP",*/ *fn_gelf_time="timestamp"; const char *fn_sd_prio="PRIORITY", *fn_gelf_prio="level"; int rc = 0; //add version field necessary for GELF json_object_set_new(message, "version", json_string("1.1")); /* get data necessary for GELF*/ rc = sd_journal_get_data(j, fn_sd_host, &data, &length); if (!rc){ char *v = get_value(data, length); assert(v); json_object_set_new(message, fn_gelf_host, json_string(v)); free(v); } else{ json_object_set_new(message, fn_gelf_host, json_string("not_available")); } rc = sd_journal_get_data(j, fn_sd_msg, &data, &length); if (!rc){ char *v = get_value(data, length); assert(v); json_object_set_new(message, fn_gelf_msg, json_string(v)); free(v); } else{ json_object_set_new(message, fn_gelf_msg, json_string("not_available")); } rc = sd_journal_get_data(j, fn_sd_prio, &data, &length); if (!rc){ char *v = get_value(data, length); assert(v); int prio = strtol(v, NULL, 10); if (prio<0) prio = 0; if (prio>7) prio = 7; //TODO: log meldung absetzen json_object_set_new(message, fn_gelf_prio, json_integer(prio)); free(v); } else{ json_object_set_new(message, fn_gelf_prio, json_string("not_available")); } // get systemd journal meta fields cursor, realtime- and monotonic timestamp // __REALTIME_TIMESTAMP corresponds to GELF necessary timestamp const char *meta_prefixes[] = {"___CURSOR", fn_gelf_time , "___MONOTONIC_TIMESTAMP" }; sd_journal_get_cursor( j, &cursor ); // needs to be free'd afterwards json_object_set_new(message, meta_prefixes[0], json_string(cursor)); free(cursor); sd_journal_get_realtime_usec( j, &realtime_usec ); json_object_set_new(message, meta_prefixes[1], json_integer(realtime_usec)); sd_journal_get_monotonic_usec( j, &monotonic_usec, &boot_id); json_object_set_new(message, meta_prefixes[2], json_integer(monotonic_usec)); /* get all remaining fields */ // (PRIORITY, _HOSTNAME, and MESSAGE are read again) // format of prefixes: additional '_' for additional fields in GELF // format of retrieved arguments: data="FIELD_NAME=field_value" length= SD_JOURNAL_FOREACH_DATA(j, data, length){ char *v = get_value(data, length); assert(v); char *k = get_key(data); assert(k); json_object_set_new(message, k, json_string(v)); free(v); free(k); }
/** * Write up to size bytes to buf. Return negative on error, and number of * bytes written otherwise. The last case is a kind of an error too. */ static ssize_t write_entry(char *buf, size_t size, Uploader *u) { int r; size_t pos = 0; assert(size <= SSIZE_MAX); while (true) { switch(u->entry_state) { case ENTRY_CURSOR: { free(u->current_cursor); u->current_cursor = NULL; r = sd_journal_get_cursor(u->journal, &u->current_cursor); if (r < 0) return log_error_errno(r, "Failed to get cursor: %m"); r = snprintf(buf + pos, size - pos, "__CURSOR=%s\n", u->current_cursor); if (pos + r > size) /* not enough space */ return pos; u->entry_state ++; if (pos + r == size) { /* exactly one character short, but we don't need it */ buf[size - 1] = '\n'; return size; } pos += r; } /* fall through */ case ENTRY_REALTIME: { usec_t realtime; r = sd_journal_get_realtime_usec(u->journal, &realtime); if (r < 0) return log_error_errno(r, "Failed to get realtime timestamp: %m"); r = snprintf(buf + pos, size - pos, "__REALTIME_TIMESTAMP="USEC_FMT"\n", realtime); if (r + pos > size) /* not enough space */ return pos; u->entry_state ++; if (r + pos == size) { /* exactly one character short, but we don't need it */ buf[size - 1] = '\n'; return size; } pos += r; } /* fall through */ case ENTRY_MONOTONIC: { usec_t monotonic; sd_id128_t boot_id; r = sd_journal_get_monotonic_usec(u->journal, &monotonic, &boot_id); if (r < 0) return log_error_errno(r, "Failed to get monotonic timestamp: %m"); r = snprintf(buf + pos, size - pos, "__MONOTONIC_TIMESTAMP="USEC_FMT"\n", monotonic); if (r + pos > size) /* not enough space */ return pos; u->entry_state ++; if (r + pos == size) { /* exactly one character short, but we don't need it */ buf[size - 1] = '\n'; return size; } pos += r; } /* fall through */ case ENTRY_BOOT_ID: { sd_id128_t boot_id; char sid[33]; r = sd_journal_get_monotonic_usec(u->journal, NULL, &boot_id); if (r < 0) return log_error_errno(r, "Failed to get monotonic timestamp: %m"); r = snprintf(buf + pos, size - pos, "_BOOT_ID=%s\n", sd_id128_to_string(boot_id, sid)); if (r + pos > size) /* not enough space */ return pos; u->entry_state ++; if (r + pos == size) { /* exactly one character short, but we don't need it */ buf[size - 1] = '\n'; return size; } pos += r; } /* fall through */ case ENTRY_NEW_FIELD: { u->field_pos = 0; r = sd_journal_enumerate_data(u->journal, &u->field_data, &u->field_length); if (r < 0) return log_error_errno(r, "Failed to move to next field in entry: %m"); else if (r == 0) { u->entry_state = ENTRY_OUTRO; continue; } if (!utf8_is_printable_newline(u->field_data, u->field_length, false)) { u->entry_state = ENTRY_BINARY_FIELD_START; continue; } u->entry_state ++; } /* fall through */ case ENTRY_TEXT_FIELD: case ENTRY_BINARY_FIELD: { bool done; size_t tocopy; done = size - pos > u->field_length - u->field_pos; if (done) tocopy = u->field_length - u->field_pos; else tocopy = size - pos; memcpy(buf + pos, (char*) u->field_data + u->field_pos, tocopy); if (done) { buf[pos + tocopy] = '\n'; pos += tocopy + 1; u->entry_state = ENTRY_NEW_FIELD; continue; } else { u->field_pos += tocopy; return size; } } case ENTRY_BINARY_FIELD_START: { const char *c; size_t len; c = memchr(u->field_data, '=', u->field_length); if (!c || c == u->field_data) { log_error("Invalid field."); return -EINVAL; } len = c - (const char*)u->field_data; /* need space for label + '\n' */ if (size - pos < len + 1) return pos; memcpy(buf + pos, u->field_data, len); buf[pos + len] = '\n'; pos += len + 1; u->field_pos = len + 1; u->entry_state ++; } /* fall through */ case ENTRY_BINARY_FIELD_SIZE: { uint64_t le64; /* need space for uint64_t */ if (size - pos < 8) return pos; le64 = htole64(u->field_length - u->field_pos); memcpy(buf + pos, &le64, 8); pos += 8; u->entry_state ++; continue; } case ENTRY_OUTRO: /* need space for '\n' */ if (size - pos < 1) return pos; buf[pos++] = '\n'; u->entry_state ++; u->entries_sent ++; return pos; default: assert_not_reached("WTF?"); } } assert_not_reached("WTF?"); }
void systemd_refresh(void) { /* Absorb any changes such as inotify() messages. */ (void) sd_journal_process(journald_context); (void) sd_journal_process(journald_context_seeky); while (1) { char *cursor = NULL; char *timestamp_str = NULL; struct timeval timestamp; int rc = sd_journal_next(journald_context); if (rc == 0) /* No recent entries. */ break; if (rc < 0) { __pmNotifyErr(LOG_ERR, "sd_journal_next failure: %s", strerror(-rc)); break; } /* NB: we enqueue the journal cursor string, rather than the actual journal records. */ rc = sd_journal_get_cursor(journald_context, &cursor); if (rc < 0) { __pmNotifyErr(LOG_ERR, "sd_journal_get_cursor failure: %s", strerror(-rc)); break; } /* Extract a timestamp from the journald event fields. */ timestamp_str = my_sd_journal_get_data(journald_context, "_SOURCE_REALTIME_TIMESTAMP"); if (timestamp_str == NULL) timestamp_str = my_sd_journal_get_data(journald_context, "__REALTIME_TIMESTAMP"); if (timestamp_str == NULL) rc = -ENOMEM; else { const char* curse; unsigned long long epoch_us; /* defined in systemd.journal-fields(7) as FIELD_NAME=NNNN, where NNNN is decimal us since epoch. */ curse = strchr (timestamp_str, '='); if (curse == NULL) rc = -EINVAL; else { curse ++; epoch_us = strtoull (curse, NULL, 10); timestamp.tv_sec = epoch_us / 1000000; timestamp.tv_usec = epoch_us % 1000000; } free (timestamp_str); } /* Improvise. */ if (rc < 0) gettimeofday (& timestamp, NULL); /* Enqueue it to fresh visitors. */ rc = pmdaEventQueueAppend(queue_entries, cursor, strlen(cursor)+1 /* \0 */, ×tamp); free(cursor); /* Already copied. */ if (rc < 0) { __pmNotifyErr(LOG_ERR, "pmdaEventQueueAppend failure: %s", pmErrStr(rc)); break; } } }