static int journal_get_realtime_usec (lua_State *L) { sd_journal *j = check_journal(L, 1); uint64_t usec; int err = sd_journal_get_realtime_usec(j, &usec); if (err != 0) return handle_error(L, -err); lua_pushuint64(L, usec); return 1; }
static int output_timestamp_realtime(FILE *f, sd_journal *j, OutputMode mode, OutputFlags flags, const char *realtime) { char buf[MAX(FORMAT_TIMESTAMP_MAX, 64)]; struct tm *(*gettime_r)(const time_t *, struct tm *); struct tm tm; uint64_t x; time_t t; int r; assert(f); assert(j); if (realtime) r = safe_atou64(realtime, &x); if (!realtime || r < 0 || !VALID_REALTIME(x)) r = sd_journal_get_realtime_usec(j, &x); if (r < 0) return log_error_errno(r, "Failed to get realtime timestamp: %m"); if (IN_SET(mode, OUTPUT_SHORT_FULL, OUTPUT_WITH_UNIT)) { const char *k; if (flags & OUTPUT_UTC) k = format_timestamp_utc(buf, sizeof(buf), x); else k = format_timestamp(buf, sizeof(buf), x); if (!k) { log_error("Failed to format timestamp: %"PRIu64, x); return -EINVAL; } } else { char usec[7]; gettime_r = (flags & OUTPUT_UTC) ? gmtime_r : localtime_r; t = (time_t) (x / USEC_PER_SEC); switch (mode) { case OUTPUT_SHORT_UNIX: xsprintf(buf, "%10"PRI_TIME".%06"PRIu64, t, x % USEC_PER_SEC); break; case OUTPUT_SHORT_ISO: if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", gettime_r(&t, &tm)) <= 0) { log_error("Failed to format ISO time"); return -EINVAL; } break; case OUTPUT_SHORT_ISO_PRECISE: /* No usec in strftime, so we leave space and copy over */ if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S.xxxxxx%z", gettime_r(&t, &tm)) <= 0) { log_error("Failed to format ISO-precise time"); return -EINVAL; } xsprintf(usec, "%06"PRI_USEC, x % USEC_PER_SEC); memcpy(buf + 20, usec, 6); break; case OUTPUT_SHORT: case OUTPUT_SHORT_PRECISE: if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm)) <= 0) { log_error("Failed to format syslog time"); return -EINVAL; } if (mode == OUTPUT_SHORT_PRECISE) { size_t k; assert(sizeof(buf) > strlen(buf)); k = sizeof(buf) - strlen(buf); r = snprintf(buf + strlen(buf), k, ".%06"PRIu64, x % USEC_PER_SEC); if (r <= 0 || (size_t) r >= k) { /* too long? */ log_error("Failed to format precise time"); return -EINVAL; } } break; default: assert_not_reached("Unknown time format"); } } fputs(buf, f); return (int) strlen(buf); }
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); }
tlog_grc tlog_journal_json_reader_read(struct tlog_json_reader *reader, struct json_object **pobject) { struct tlog_journal_json_reader *journal_json_reader = (struct tlog_journal_json_reader*)reader; tlog_grc grc; int sd_rc; struct json_object *object = NULL; /* If we ran out of time limit */ if (journal_json_reader->last > journal_json_reader->until) { goto exit; } /* Advance to the next entry */ sd_rc = sd_journal_next(journal_json_reader->journal); /* If failed */ if (sd_rc < 0) { grc = TLOG_GRC_FROM(systemd, sd_rc); goto cleanup; /* If got an entry */ } else if (sd_rc > 0) { const char *field_ptr; size_t field_len; const char *message_ptr; size_t message_len; /* Advance entry counter */ journal_json_reader->entry++; /* Get the entry realtime timestamp */ sd_rc = sd_journal_get_realtime_usec(journal_json_reader->journal, &journal_json_reader->last); if (sd_rc < 0) { grc = TLOG_GRC_FROM(systemd, sd_rc); goto cleanup; } if (journal_json_reader->last > journal_json_reader->until) { goto exit; } /* Get the entry message field data */ sd_rc = sd_journal_get_data(journal_json_reader->journal, "MESSAGE", (const void **)&field_ptr, &field_len); if (sd_rc < 0) { grc = TLOG_GRC_FROM(systemd, sd_rc); goto cleanup; } /* Extract the message */ message_ptr = (const char *)memchr(field_ptr, '=', field_len); if (message_ptr == NULL) { grc = TLOG_RC_FAILURE; goto cleanup; } message_ptr++; message_len = field_len - (message_ptr - field_ptr); /* Parse the message */ object = json_tokener_parse_ex(journal_json_reader->tok, message_ptr, message_len); if (object == NULL) { grc = TLOG_GRC_FROM( json, json_tokener_get_error(journal_json_reader->tok)); goto cleanup; } } exit: *pobject = object; object = NULL; grc = TLOG_RC_OK; cleanup: json_object_put(object); return grc; }
/** * 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?"); }
QStringList UnitModel::getLastJrnlEntries(QString unit) const { QString match1, match2; int r, jflags; QStringList reply; const void *data; size_t length; uint64_t time; sd_journal *journal; if (!userBus.isEmpty()) { match1 = QString("USER_UNIT=" + unit); jflags = (SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_CURRENT_USER); } else { match1 = QString("_SYSTEMD_UNIT=" + unit); match2 = QString("UNIT=" + unit); jflags = (SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_SYSTEM); } r = sd_journal_open(&journal, jflags); if (r != 0) { qDebug() << "Failed to open journal"; return reply; } sd_journal_flush_matches(journal); r = sd_journal_add_match(journal, match1.toUtf8(), 0); if (r != 0) return reply; if (!match2.isEmpty()) { sd_journal_add_disjunction(journal); r = sd_journal_add_match(journal, match2.toUtf8(), 0); if (r != 0) return reply; } r = sd_journal_seek_tail(journal); if (r != 0) return reply; // Fetch the last 5 entries for (int i = 0; i < 5; ++i) { r = sd_journal_previous(journal); if (r == 1) { QString line; // Get the date and time r = sd_journal_get_realtime_usec(journal, &time); if (r == 0) { QDateTime date; date.setMSecsSinceEpoch(time/1000); line.append(date.toString("yyyy.MM.dd hh:mm")); } // Color messages according to priority r = sd_journal_get_data(journal, "PRIORITY", &data, &length); if (r == 0) { int prio = QString::fromUtf8((const char *)data, length).section('=',1).toInt(); if (prio <= 3) line.append("<span style='color:tomato;'>"); else if (prio == 4) line.append("<span style='color:khaki;'>"); else line.append("<span style='color:palegreen;'>"); } // Get the message itself r = sd_journal_get_data(journal, "MESSAGE", &data, &length); if (r == 0) { line.append(": " + QString::fromUtf8((const char *)data, length).section('=',1) + "</span>"); if (line.length() > 195) line = QString(line.left(195) + "..." + "</span>"); reply << line; } } else // previous failed, no more entries return reply; } sd_journal_close(journal); return reply; }