]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal-remote/journal-upload-journal.c
Merge pull request #2706 from whot/hwdb-updates
[thirdparty/systemd.git] / src / journal-remote / journal-upload-journal.c
CommitLineData
b5efdb8a
LP
1/***
2 This file is part of systemd.
3
4 Copyright 2014 Zbigniew Jędrzejewski-Szmek
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18***/
19
eacbb4d3 20#include <curl/curl.h>
cf0fbc49 21#include <stdbool.h>
eacbb4d3 22
b5efdb8a
LP
23#include "alloc-util.h"
24#include "journal-upload.h"
eacbb4d3
ZJS
25#include "log.h"
26#include "utf8.h"
b5efdb8a 27#include "util.h"
eacbb4d3
ZJS
28
29/**
30 * Write up to size bytes to buf. Return negative on error, and number of
31 * bytes written otherwise. The last case is a kind of an error too.
32 */
33static ssize_t write_entry(char *buf, size_t size, Uploader *u) {
34 int r;
35 size_t pos = 0;
36
37 assert(size <= SSIZE_MAX);
38
57255510 39 for (;;) {
eacbb4d3
ZJS
40
41 switch(u->entry_state) {
42 case ENTRY_CURSOR: {
a1e58e8e 43 u->current_cursor = mfree(u->current_cursor);
eacbb4d3 44
722b6795 45 r = sd_journal_get_cursor(u->journal, &u->current_cursor);
eb56eb9b
MS
46 if (r < 0)
47 return log_error_errno(r, "Failed to get cursor: %m");
eacbb4d3
ZJS
48
49 r = snprintf(buf + pos, size - pos,
722b6795 50 "__CURSOR=%s\n", u->current_cursor);
eacbb4d3
ZJS
51 if (pos + r > size)
52 /* not enough space */
53 return pos;
54
55 u->entry_state ++;
56
57 if (pos + r == size) {
58 /* exactly one character short, but we don't need it */
59 buf[size - 1] = '\n';
60 return size;
61 }
62
63 pos += r;
64 } /* fall through */
65
66 case ENTRY_REALTIME: {
67 usec_t realtime;
68
69 r = sd_journal_get_realtime_usec(u->journal, &realtime);
eb56eb9b
MS
70 if (r < 0)
71 return log_error_errno(r, "Failed to get realtime timestamp: %m");
eacbb4d3
ZJS
72
73 r = snprintf(buf + pos, size - pos,
74 "__REALTIME_TIMESTAMP="USEC_FMT"\n", realtime);
75 if (r + pos > size)
76 /* not enough space */
77 return pos;
78
79 u->entry_state ++;
80
81 if (r + pos == size) {
82 /* exactly one character short, but we don't need it */
83 buf[size - 1] = '\n';
84 return size;
85 }
86
87 pos += r;
88 } /* fall through */
89
90 case ENTRY_MONOTONIC: {
91 usec_t monotonic;
92 sd_id128_t boot_id;
93
94 r = sd_journal_get_monotonic_usec(u->journal, &monotonic, &boot_id);
eb56eb9b
MS
95 if (r < 0)
96 return log_error_errno(r, "Failed to get monotonic timestamp: %m");
eacbb4d3
ZJS
97
98 r = snprintf(buf + pos, size - pos,
99 "__MONOTONIC_TIMESTAMP="USEC_FMT"\n", monotonic);
100 if (r + pos > size)
101 /* not enough space */
102 return pos;
103
104 u->entry_state ++;
105
106 if (r + pos == size) {
107 /* exactly one character short, but we don't need it */
108 buf[size - 1] = '\n';
109 return size;
110 }
111
112 pos += r;
113 } /* fall through */
114
115 case ENTRY_BOOT_ID: {
116 sd_id128_t boot_id;
117 char sid[33];
118
119 r = sd_journal_get_monotonic_usec(u->journal, NULL, &boot_id);
eb56eb9b
MS
120 if (r < 0)
121 return log_error_errno(r, "Failed to get monotonic timestamp: %m");
eacbb4d3
ZJS
122
123 r = snprintf(buf + pos, size - pos,
124 "_BOOT_ID=%s\n", sd_id128_to_string(boot_id, sid));
5ffa8c81 125 if (r + pos > size)
eacbb4d3
ZJS
126 /* not enough space */
127 return pos;
128
129 u->entry_state ++;
130
131 if (r + pos == size) {
132 /* exactly one character short, but we don't need it */
133 buf[size - 1] = '\n';
134 return size;
135 }
136
137 pos += r;
138 } /* fall through */
139
140 case ENTRY_NEW_FIELD: {
141 u->field_pos = 0;
142
143 r = sd_journal_enumerate_data(u->journal,
144 &u->field_data,
145 &u->field_length);
eb56eb9b
MS
146 if (r < 0)
147 return log_error_errno(r, "Failed to move to next field in entry: %m");
148 else if (r == 0) {
eacbb4d3
ZJS
149 u->entry_state = ENTRY_OUTRO;
150 continue;
151 }
152
153 if (!utf8_is_printable_newline(u->field_data,
154 u->field_length, false)) {
155 u->entry_state = ENTRY_BINARY_FIELD_START;
156 continue;
157 }
158
159 u->entry_state ++;
160 } /* fall through */
161
162 case ENTRY_TEXT_FIELD:
163 case ENTRY_BINARY_FIELD: {
164 bool done;
165 size_t tocopy;
166
167 done = size - pos > u->field_length - u->field_pos;
168 if (done)
169 tocopy = u->field_length - u->field_pos;
170 else
171 tocopy = size - pos;
172
173 memcpy(buf + pos,
174 (char*) u->field_data + u->field_pos,
175 tocopy);
176
177 if (done) {
178 buf[pos + tocopy] = '\n';
179 pos += tocopy + 1;
180 u->entry_state = ENTRY_NEW_FIELD;
181 continue;
182 } else {
183 u->field_pos += tocopy;
184 return size;
185 }
186 }
187
188 case ENTRY_BINARY_FIELD_START: {
189 const char *c;
190 size_t len;
191
192 c = memchr(u->field_data, '=', u->field_length);
193 if (!c || c == u->field_data) {
194 log_error("Invalid field.");
195 return -EINVAL;
196 }
197
198 len = c - (const char*)u->field_data;
199
200 /* need space for label + '\n' */
201 if (size - pos < len + 1)
202 return pos;
203
204 memcpy(buf + pos, u->field_data, len);
205 buf[pos + len] = '\n';
206 pos += len + 1;
207
208 u->field_pos = len + 1;
209 u->entry_state ++;
210 } /* fall through */
211
212 case ENTRY_BINARY_FIELD_SIZE: {
213 uint64_t le64;
214
215 /* need space for uint64_t */
216 if (size - pos < 8)
217 return pos;
218
219 le64 = htole64(u->field_length - u->field_pos);
220 memcpy(buf + pos, &le64, 8);
221 pos += 8;
222
223 u->entry_state ++;
224 continue;
225 }
226
227 case ENTRY_OUTRO:
228 /* need space for '\n' */
229 if (size - pos < 1)
230 return pos;
231
232 buf[pos++] = '\n';
233 u->entry_state ++;
234 u->entries_sent ++;
235
236 return pos;
237
238 default:
239 assert_not_reached("WTF?");
240 }
241 }
242 assert_not_reached("WTF?");
243}
244
245static size_t journal_input_callback(void *buf, size_t size, size_t nmemb, void *userp) {
246 Uploader *u = userp;
247 int r;
248 sd_journal *j;
249 size_t filled = 0;
250 ssize_t w;
251
252 assert(u);
253 assert(nmemb <= SSIZE_MAX / size);
254
255 j = u->journal;
256
257 while (j && filled < size * nmemb) {
258 if (u->entry_state == ENTRY_DONE) {
259 r = sd_journal_next(j);
260 if (r < 0) {
c33b3297 261 log_error_errno(r, "Failed to move to next entry in journal: %m");
eacbb4d3
ZJS
262 return CURL_READFUNC_ABORT;
263 } else if (r == 0) {
264 if (u->input_event)
265 log_debug("No more entries, waiting for journal.");
266 else {
267 log_info("No more entries, closing journal.");
268 close_journal_input(u);
269 }
270
271 u->uploading = false;
272
273 break;
274 }
275
276 u->entry_state = ENTRY_CURSOR;
277 }
278
279 w = write_entry((char*)buf + filled, size * nmemb - filled, u);
280 if (w < 0)
281 return CURL_READFUNC_ABORT;
282 filled += w;
283
284 if (filled == 0) {
285 log_error("Buffer space is too small to write entry.");
286 return CURL_READFUNC_ABORT;
287 } else if (u->entry_state != ENTRY_DONE)
288 /* This means that all available space was used up */
289 break;
290
291 log_debug("Entry %zu (%s) has been uploaded.",
722b6795 292 u->entries_sent, u->current_cursor);
eacbb4d3
ZJS
293 }
294
295 return filled;
296}
297
298void close_journal_input(Uploader *u) {
299 assert(u);
300
301 if (u->journal) {
302 log_debug("Closing journal input.");
303
304 sd_journal_close(u->journal);
305 u->journal = NULL;
306 }
307 u->timeout = 0;
308}
309
310static int process_journal_input(Uploader *u, int skip) {
311 int r;
312
8a3db16d
KC
313 if (u->uploading)
314 return 0;
315
eacbb4d3 316 r = sd_journal_next_skip(u->journal, skip);
eb56eb9b
MS
317 if (r < 0)
318 return log_error_errno(r, "Failed to skip to next entry: %m");
319 else if (r < skip)
eacbb4d3
ZJS
320 return 0;
321
322 /* have data */
323 u->entry_state = ENTRY_CURSOR;
324 return start_upload(u, journal_input_callback, u);
325}
326
327int check_journal_input(Uploader *u) {
328 if (u->input_event) {
329 int r;
330
331 r = sd_journal_process(u->journal);
332 if (r < 0) {
da927ba9 333 log_error_errno(r, "Failed to process journal: %m");
eacbb4d3
ZJS
334 close_journal_input(u);
335 return r;
336 }
337
338 if (r == SD_JOURNAL_NOP)
339 return 0;
340 }
341
342 return process_journal_input(u, 1);
343}
344
345static int dispatch_journal_input(sd_event_source *event,
346 int fd,
347 uint32_t revents,
348 void *userp) {
349 Uploader *u = userp;
350
351 assert(u);
352
8a3db16d 353 if (u->uploading)
eacbb4d3 354 return 0;
eacbb4d3
ZJS
355
356 log_debug("Detected journal input, checking for new data.");
357 return check_journal_input(u);
358}
359
360int open_journal_for_upload(Uploader *u,
361 sd_journal *j,
362 const char *cursor,
363 bool after_cursor,
364 bool follow) {
365 int fd, r, events;
366
367 u->journal = j;
368
369 sd_journal_set_data_threshold(j, 0);
370
371 if (follow) {
372 fd = sd_journal_get_fd(j);
eb56eb9b
MS
373 if (fd < 0)
374 return log_error_errno(fd, "sd_journal_get_fd failed: %m");
eacbb4d3
ZJS
375
376 events = sd_journal_get_events(j);
377
378 r = sd_journal_reliable_fd(j);
379 assert(r >= 0);
380 if (r > 0)
381 u->timeout = -1;
382 else
383 u->timeout = JOURNAL_UPLOAD_POLL_TIMEOUT;
384
385 r = sd_event_add_io(u->events, &u->input_event,
386 fd, events, dispatch_journal_input, u);
eb56eb9b
MS
387 if (r < 0)
388 return log_error_errno(r, "Failed to register input event: %m");
eacbb4d3
ZJS
389
390 log_debug("Listening for journal events on fd:%d, timeout %d",
391 fd, u->timeout == (uint64_t) -1 ? -1 : (int) u->timeout);
392 } else
393 log_debug("Not listening for journal events.");
394
395 if (cursor) {
396 r = sd_journal_seek_cursor(j, cursor);
ece174c5 397 if (r < 0)
eb56eb9b
MS
398 return log_error_errno(r, "Failed to seek to cursor %s: %m",
399 cursor);
eacbb4d3
ZJS
400 }
401
402 return process_journal_input(u, 1 + !!after_cursor);
403}