2018-05-12 16:03:00 +00:00
|
|
|
/*
|
2016-05-06 11:25:30 +00:00
|
|
|
*
|
|
|
|
* Conky, a system monitor, based on torsmo
|
|
|
|
*
|
|
|
|
* Any original torsmo code is licensed under the BSD license
|
|
|
|
*
|
|
|
|
* All code written since the fork of torsmo is licensed under the GPL
|
|
|
|
*
|
|
|
|
* Please see COPYING for details
|
|
|
|
*
|
|
|
|
* Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
|
2021-02-27 15:14:19 +00:00
|
|
|
* Copyright (c) 2005-2021 Brenden Matthews, Philip Kovacs, et. al.
|
2016-05-06 11:25:30 +00:00
|
|
|
* (see AUTHORS)
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <string.h>
|
2018-05-12 16:03:00 +00:00
|
|
|
#include <systemd/sd-journal.h>
|
2018-05-13 17:33:18 +00:00
|
|
|
#include <time.h>
|
2016-05-06 11:25:30 +00:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <memory>
|
2018-05-12 16:03:00 +00:00
|
|
|
#include "common.h"
|
|
|
|
#include "config.h"
|
|
|
|
#include "conky.h"
|
|
|
|
#include "logging.h"
|
|
|
|
#include "text_object.h"
|
2016-05-06 11:25:30 +00:00
|
|
|
|
|
|
|
#define MAX_JOURNAL_LINES 200
|
|
|
|
|
2018-05-13 17:33:18 +00:00
|
|
|
class journal {
|
|
|
|
public:
|
2018-05-12 16:03:00 +00:00
|
|
|
int wantedlines;
|
|
|
|
int flags;
|
2016-05-06 11:25:30 +00:00
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
journal() : wantedlines(0), flags(SD_JOURNAL_LOCAL_ONLY) {}
|
2016-05-06 11:25:30 +00:00
|
|
|
};
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
void free_journal(struct text_object *obj) {
|
2018-05-13 17:33:18 +00:00
|
|
|
journal *j = (journal *)obj->data.opaque;
|
2018-05-12 23:26:31 +00:00
|
|
|
obj->data.opaque = nullptr;
|
2018-05-12 16:03:00 +00:00
|
|
|
delete j;
|
2016-05-06 11:25:30 +00:00
|
|
|
}
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
void init_journal(const char *type, const char *arg, struct text_object *obj,
|
|
|
|
void *free_at_crash) {
|
|
|
|
unsigned int args;
|
2018-05-13 17:33:18 +00:00
|
|
|
journal *j = new journal;
|
2018-05-12 16:03:00 +00:00
|
|
|
|
|
|
|
std::unique_ptr<char[]> tmp(new char[DEFAULT_TEXT_BUFFER_SIZE]);
|
|
|
|
memset(tmp.get(), 0, DEFAULT_TEXT_BUFFER_SIZE);
|
|
|
|
|
|
|
|
args = sscanf(arg, "%d %6s", &j->wantedlines, tmp.get());
|
|
|
|
if (args < 1 || args > 2) {
|
|
|
|
free_journal(obj);
|
|
|
|
CRIT_ERR(obj, free_at_crash,
|
|
|
|
"%s a number of lines as 1st argument and optionally a journal "
|
|
|
|
"type as 2nd argument",
|
|
|
|
type);
|
|
|
|
}
|
|
|
|
if (j->wantedlines > 0 && j->wantedlines <= MAX_JOURNAL_LINES) {
|
|
|
|
if (args > 1) {
|
|
|
|
if (strcmp(tmp.get(), "system") == 0) {
|
|
|
|
j->flags |= SD_JOURNAL_SYSTEM;
|
2018-05-13 17:33:18 +00:00
|
|
|
#ifdef SD_JOURNAL_CURRENT_USER // not present in older version of systemd
|
2018-05-12 16:03:00 +00:00
|
|
|
} else if (strcmp(tmp.get(), "user") == 0) {
|
|
|
|
j->flags |= SD_JOURNAL_CURRENT_USER;
|
2018-05-13 17:33:18 +00:00
|
|
|
#endif /* SD_JOURNAL_CURRENT_USER */
|
2018-05-12 16:03:00 +00:00
|
|
|
} else {
|
|
|
|
free_journal(obj);
|
|
|
|
CRIT_ERR(obj, free_at_crash,
|
|
|
|
"invalid arg for %s, type must be 'system' or 'user'", type);
|
|
|
|
}
|
2018-08-06 15:32:04 +00:00
|
|
|
} else {
|
|
|
|
NORM_ERR("You should type a 'user' or 'system' as an argument");
|
2018-05-12 16:03:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
free_journal(obj);
|
|
|
|
CRIT_ERR(obj, free_at_crash,
|
|
|
|
"invalid arg for %s, number of lines must be between 1 and %d",
|
|
|
|
type, MAX_JOURNAL_LINES);
|
|
|
|
}
|
|
|
|
obj->data.opaque = j;
|
2016-05-06 11:25:30 +00:00
|
|
|
}
|
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
static int print_field(sd_journal *jh, const char *field, char spacer,
|
2018-08-07 23:22:23 +00:00
|
|
|
size_t *read, char *p, unsigned int p_max_size) {
|
2018-05-12 16:03:00 +00:00
|
|
|
const void *get;
|
|
|
|
size_t length;
|
|
|
|
size_t fieldlen = strlen(field) + 1;
|
2016-05-06 11:25:30 +00:00
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
int ret = sd_journal_get_data(jh, field, &get, &length);
|
|
|
|
if (ret == -ENOENT) goto out;
|
2016-05-06 11:25:30 +00:00
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
if (ret < 0 || length + *read > p_max_size) return -1;
|
2016-05-06 11:25:30 +00:00
|
|
|
|
2018-05-12 16:03:00 +00:00
|
|
|
memcpy(p + *read, (const char *)get + fieldlen, length - fieldlen);
|
|
|
|
*read += length - fieldlen;
|
2016-05-06 11:25:30 +00:00
|
|
|
|
|
|
|
out:
|
2018-08-06 15:32:04 +00:00
|
|
|
if (spacer) {
|
|
|
|
if (p_max_size < *read) {
|
|
|
|
*read = p_max_size - 1;
|
|
|
|
} else {
|
|
|
|
p[(*read)++] = spacer;
|
|
|
|
}
|
|
|
|
}
|
2018-05-12 16:03:00 +00:00
|
|
|
return length ? length - fieldlen : 0;
|
2016-05-06 11:25:30 +00:00
|
|
|
}
|
|
|
|
|
2018-05-13 17:33:18 +00:00
|
|
|
bool read_log(size_t *read, size_t *length, time_t *time, uint64_t *timestamp,
|
2018-08-08 13:33:13 +00:00
|
|
|
sd_journal *jh, char *p, unsigned int p_max_size) {
|
2018-05-13 17:33:18 +00:00
|
|
|
struct tm tm;
|
|
|
|
if (sd_journal_get_realtime_usec(jh, timestamp) < 0) return false;
|
|
|
|
*time = *timestamp / 1000000;
|
|
|
|
localtime_r(time, &tm);
|
|
|
|
|
|
|
|
if ((*length =
|
|
|
|
strftime(p + *read, p_max_size - *read, "%b %d %H:%M:%S", &tm)) <= 0)
|
|
|
|
return false;
|
|
|
|
*read += *length;
|
2018-08-04 20:26:40 +00:00
|
|
|
|
|
|
|
if (p_max_size < *read) {
|
|
|
|
*read = p_max_size - 1;
|
|
|
|
return false;
|
|
|
|
}
|
2018-05-13 17:33:18 +00:00
|
|
|
p[*read++] = ' ';
|
|
|
|
|
|
|
|
if (print_field(jh, "_HOSTNAME", ' ', read, p, p_max_size) < 0) return false;
|
|
|
|
|
|
|
|
if (print_field(jh, "SYSLOG_IDENTIFIER", '[', read, p, p_max_size) < 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (print_field(jh, "_PID", ']', read, p, p_max_size) < 0) return false;
|
|
|
|
|
2018-08-04 20:26:40 +00:00
|
|
|
if (p_max_size < *read) {
|
|
|
|
*read = p_max_size - 1;
|
|
|
|
return false;
|
|
|
|
}
|
2018-05-13 17:33:18 +00:00
|
|
|
p[*read++] = ':';
|
2018-08-04 20:26:40 +00:00
|
|
|
|
|
|
|
if (p_max_size < *read) {
|
|
|
|
*read = p_max_size - 1;
|
|
|
|
return false;
|
|
|
|
}
|
2018-05-13 17:33:18 +00:00
|
|
|
p[*read++] = ' ';
|
|
|
|
|
|
|
|
if (print_field(jh, "MESSAGE", '\n', read, p, p_max_size) < 0) return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-08-08 13:33:13 +00:00
|
|
|
void print_journal(struct text_object *obj, char *p, unsigned int p_max_size) {
|
2018-12-22 20:43:40 +00:00
|
|
|
journal *j = (journal *)obj->data.opaque;
|
2018-05-12 23:26:31 +00:00
|
|
|
sd_journal *jh = nullptr;
|
2018-05-13 17:33:18 +00:00
|
|
|
size_t read = 0;
|
|
|
|
size_t length;
|
2018-05-12 16:03:00 +00:00
|
|
|
time_t time;
|
|
|
|
uint64_t timestamp;
|
|
|
|
|
|
|
|
if (sd_journal_open(&jh, j->flags) != 0) {
|
|
|
|
NORM_ERR("unable to open journal");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sd_journal_seek_tail(jh) < 0) {
|
|
|
|
NORM_ERR("unable to seek to end of journal");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (sd_journal_previous_skip(jh, j->wantedlines) < 0) {
|
|
|
|
NORM_ERR("unable to seek back %d lines", j->wantedlines);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2018-05-13 17:33:18 +00:00
|
|
|
while (read_log(&read, &length, &time, ×tamp, jh, p, p_max_size) &&
|
|
|
|
sd_journal_next(jh))
|
|
|
|
;
|
2016-05-06 11:25:30 +00:00
|
|
|
|
|
|
|
out:
|
2018-05-12 16:03:00 +00:00
|
|
|
if (jh) sd_journal_close(jh);
|
|
|
|
p[read] = '\0';
|
2016-05-06 11:25:30 +00:00
|
|
|
}
|