journal-upload-journal.c revision 8a3db16df40c1bbcb1d1debc80fbf92339503235
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2014 Zbigniew Jędrzejewski-Szmek
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdbool.h>
#include "alloc-util.h"
#include "journal-upload.h"
#include "log.h"
#include "utf8.h"
#include "util.h"
/**
* 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.
*/
int r;
for (;;) {
switch(u->entry_state) {
case ENTRY_CURSOR: {
if (r < 0)
return log_error_errno(r, "Failed to get cursor: %m");
"__CURSOR=%s\n", u->current_cursor);
/* not enough space */
return pos;
u->entry_state ++;
/* exactly one character short, but we don't need it */
return size;
}
pos += r;
} /* fall through */
case ENTRY_REALTIME: {
if (r < 0)
return log_error_errno(r, "Failed to get realtime timestamp: %m");
/* not enough space */
return pos;
u->entry_state ++;
/* exactly one character short, but we don't need it */
return size;
}
pos += r;
} /* fall through */
case ENTRY_MONOTONIC: {
if (r < 0)
return log_error_errno(r, "Failed to get monotonic timestamp: %m");
/* not enough space */
return pos;
u->entry_state ++;
/* exactly one character short, but we don't need it */
return size;
}
pos += r;
} /* fall through */
case ENTRY_BOOT_ID: {
char sid[33];
if (r < 0)
return log_error_errno(r, "Failed to get monotonic timestamp: %m");
/* not enough space */
return pos;
u->entry_state ++;
/* exactly one character short, but we don't need it */
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)) {
continue;
}
u->entry_state ++;
} /* fall through */
case ENTRY_TEXT_FIELD:
case ENTRY_BINARY_FIELD: {
bool done;
if (done)
else
(char*) u->field_data + u->field_pos,
tocopy);
if (done) {
u->entry_state = ENTRY_NEW_FIELD;
continue;
} else {
return size;
}
}
case ENTRY_BINARY_FIELD_START: {
const char *c;
if (!c || c == u->field_data) {
log_error("Invalid field.");
return -EINVAL;
}
len = c - (const char*)u->field_data;
/* need space for label + '\n' */
return pos;
u->entry_state ++;
} /* fall through */
case ENTRY_BINARY_FIELD_SIZE: {
/* need space for uint64_t */
return pos;
pos += 8;
u->entry_state ++;
continue;
}
case ENTRY_OUTRO:
/* need space for '\n' */
return pos;
u->entry_state ++;
u->entries_sent ++;
return pos;
default:
assert_not_reached("WTF?");
}
}
assert_not_reached("WTF?");
}
int r;
sd_journal *j;
ssize_t w;
assert(u);
j = u->journal;
if (u->entry_state == ENTRY_DONE) {
r = sd_journal_next(j);
if (r < 0) {
log_error_errno(r, "Failed to move to next entry in journal: %m");
return CURL_READFUNC_ABORT;
} else if (r == 0) {
if (u->input_event)
log_debug("No more entries, waiting for journal.");
else {
log_info("No more entries, closing journal.");
}
u->uploading = false;
break;
}
u->entry_state = ENTRY_CURSOR;
}
if (w < 0)
return CURL_READFUNC_ABORT;
filled += w;
if (filled == 0) {
log_error("Buffer space is too small to write entry.");
return CURL_READFUNC_ABORT;
} else if (u->entry_state != ENTRY_DONE)
/* This means that all available space was used up */
break;
log_debug("Entry %zu (%s) has been uploaded.",
u->entries_sent, u->current_cursor);
}
return filled;
}
void close_journal_input(Uploader *u) {
assert(u);
if (u->journal) {
log_debug("Closing journal input.");
sd_journal_close(u->journal);
}
u->timeout = 0;
}
int r;
if (u->uploading)
return 0;
if (r < 0)
return log_error_errno(r, "Failed to skip to next entry: %m");
else if (r < skip)
return 0;
/* have data */
u->entry_state = ENTRY_CURSOR;
return start_upload(u, journal_input_callback, u);
}
int check_journal_input(Uploader *u) {
if (u->input_event) {
int r;
r = sd_journal_process(u->journal);
if (r < 0) {
log_error_errno(r, "Failed to process journal: %m");
return r;
}
if (r == SD_JOURNAL_NOP)
return 0;
}
return process_journal_input(u, 1);
}
int fd,
void *userp) {
assert(u);
if (u->uploading)
return 0;
log_debug("Detected journal input, checking for new data.");
return check_journal_input(u);
}
int open_journal_for_upload(Uploader *u,
sd_journal *j,
const char *cursor,
bool after_cursor,
bool follow) {
u->journal = j;
if (follow) {
fd = sd_journal_get_fd(j);
if (fd < 0)
events = sd_journal_get_events(j);
r = sd_journal_reliable_fd(j);
assert(r >= 0);
if (r > 0)
u->timeout = -1;
else
if (r < 0)
return log_error_errno(r, "Failed to register input event: %m");
log_debug("Listening for journal events on fd:%d, timeout %d",
} else
log_debug("Not listening for journal events.");
if (cursor) {
r = sd_journal_seek_cursor(j, cursor);
if (r < 0)
return log_error_errno(r, "Failed to seek to cursor %s: %m",
cursor);
}
}