journal-upload.c revision 07630cea1f3a845c09309f197ac7c4f11edd3b62
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering/***
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering This file is part of systemd.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering Copyright 2014 Zbigniew Jędrzejewski-Szmek
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering systemd is free software; you can redistribute it and/or modify it
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering under the terms of the GNU Lesser General Public License as published by
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering (at your option) any later version.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering systemd is distributed in the hope that it will be useful, but
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering Lesser General Public License for more details.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering You should have received a copy of the GNU Lesser General Public License
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering***/
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
a0166609f782da91710dea9183d1bf138538db37Tom Gundersen#include <curl/curl.h>
71d35b6b5563817dfbe757ab9e3b9f018b2db491Thomas Hindoe Paaboel Andersen#include <fcntl.h>
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include <getopt.h>
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include <stdio.h>
7e8e0422aeb16f2a09a40546c61df753d10029b6Lennart Poettering#include <sys/stat.h>
71d35b6b5563817dfbe757ab9e3b9f018b2db491Thomas Hindoe Paaboel Andersen
71d35b6b5563817dfbe757ab9e3b9f018b2db491Thomas Hindoe Paaboel Andersen#include "sd-daemon.h"
7e8e0422aeb16f2a09a40546c61df753d10029b6Lennart Poettering
7e8e0422aeb16f2a09a40546c61df753d10029b6Lennart Poettering#include "conf-parser.h"
7e8e0422aeb16f2a09a40546c61df753d10029b6Lennart Poettering#include "fileio.h"
7e8e0422aeb16f2a09a40546c61df753d10029b6Lennart Poettering#include "formats-util.h"
51323288fc628a5cac50914df915545d685b793eLennart Poettering#include "log.h"
71d35b6b5563817dfbe757ab9e3b9f018b2db491Thomas Hindoe Paaboel Andersen#include "mkdir.h"
71d35b6b5563817dfbe757ab9e3b9f018b2db491Thomas Hindoe Paaboel Andersen#include "sigbus.h"
71d35b6b5563817dfbe757ab9e3b9f018b2db491Thomas Hindoe Paaboel Andersen#include "signal-util.h"
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include "string-util.h"
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering#include "util.h"
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering#include "journal-upload.h"
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering#define PRIV_KEY_FILE CERTIFICATE_ROOT "/private/journal-upload.pem"
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering#define CERT_FILE CERTIFICATE_ROOT "/certs/journal-upload.pem"
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering#define TRUST_FILE CERTIFICATE_ROOT "/ca/trusted.pem"
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering#define DEFAULT_PORT 19532
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringstatic const char* arg_url = NULL;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringstatic const char *arg_key = NULL;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringstatic const char *arg_cert = NULL;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringstatic const char *arg_trust = NULL;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringstatic const char *arg_directory = NULL;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringstatic char **arg_file = NULL;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringstatic const char *arg_cursor = NULL;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringstatic bool arg_after_cursor = false;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringstatic int arg_journal_type = 0;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringstatic const char *arg_machine = NULL;
a0166609f782da91710dea9183d1bf138538db37Tom Gundersenstatic bool arg_merge = false;
c73ce96b569e2f10dff64b7dc0bd271972674c2aLennart Poetteringstatic int arg_follow = -1;
c73ce96b569e2f10dff64b7dc0bd271972674c2aLennart Poetteringstatic const char *arg_save_state = NULL;
c73ce96b569e2f10dff64b7dc0bd271972674c2aLennart Poettering
e1c959948c0e31d6997bcdfbabfbd077784b2baeLennart Poetteringstatic void close_fd_input(Uploader *u);
c73ce96b569e2f10dff64b7dc0bd271972674c2aLennart Poettering
e1c959948c0e31d6997bcdfbabfbd077784b2baeLennart Poettering#define SERVER_ANSWER_KEEP 2048
e1c959948c0e31d6997bcdfbabfbd077784b2baeLennart Poettering
e1c959948c0e31d6997bcdfbabfbd077784b2baeLennart Poettering#define STATE_FILE "/var/lib/systemd/journal-upload/state"
e1c959948c0e31d6997bcdfbabfbd077784b2baeLennart Poettering
d74fb368b18f0fbd9a4fe6f15691bbea7f3c4a01Tom Gundersen#define easy_setopt(curl, opt, value, level, cmd) \
d74fb368b18f0fbd9a4fe6f15691bbea7f3c4a01Tom Gundersen do { \
d74fb368b18f0fbd9a4fe6f15691bbea7f3c4a01Tom Gundersen code = curl_easy_setopt(curl, opt, value); \
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (code) { \
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering log_full(level, \
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering "curl_easy_setopt " #opt " failed: %s", \
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering curl_easy_strerror(code)); \
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering cmd; \
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering } \
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering } while(0)
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poetteringstatic size_t output_callback(char *buf,
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering size_t size,
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering size_t nmemb,
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering void *userp) {
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering Uploader *u = userp;
d75acfb059ece4512278b8820a9103664996f1e5Lennart Poettering
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering assert(u);
dc61b7e45d89a69f0469ab7b3289cdde7fcc55abTorstein Husebø
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering log_debug("The server answers (%zu bytes): %.*s",
623a4c97b9175f95c4b1c6fc34e36c56f1e4ddbfLennart Poettering size*nmemb, (int)(size*nmemb), buf);
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering
623a4c97b9175f95c4b1c6fc34e36c56f1e4ddbfLennart Poettering if (nmemb && !u->answer) {
0dd25fb9f005d8ab7ac4bc10a609d00569f8c56aLennart Poettering u->answer = strndup(buf, size*nmemb);
a407657425a3e47fd2b559cd3bc800f791303f63Lennart Poettering if (!u->answer)
9c491563837983385bf9fa244590e76e142f4fa3Daniel Mack log_warning_errno(ENOMEM, "Failed to store server answer (%zu bytes): %m",
9c491563837983385bf9fa244590e76e142f4fa3Daniel Mack size*nmemb);
9c491563837983385bf9fa244590e76e142f4fa3Daniel Mack }
a8812dd7f161a3e459c1730ac92ff2bbc9986ff1Lennart Poettering
a8812dd7f161a3e459c1730ac92ff2bbc9986ff1Lennart Poettering return size * nmemb;
a8812dd7f161a3e459c1730ac92ff2bbc9986ff1Lennart Poettering}
a8812dd7f161a3e459c1730ac92ff2bbc9986ff1Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringstatic int check_cursor_updating(Uploader *u) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering _cleanup_free_ char *temp_path = NULL;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering _cleanup_fclose_ FILE *f = NULL;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering int r;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (!u->state_file)
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering return 0;
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering r = mkdir_parents(u->state_file, 0755);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (r < 0)
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering return log_error_errno(r, "Cannot create parent directory of state file %s: %m",
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering u->state_file);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
3cb10d3a0b1b6a7c44f307f2abb5215104e16941Lennart Poettering r = fopen_temporary(u->state_file, &f, &temp_path);
3cb10d3a0b1b6a7c44f307f2abb5215104e16941Lennart Poettering if (r < 0)
3cb10d3a0b1b6a7c44f307f2abb5215104e16941Lennart Poettering return log_error_errno(r, "Cannot save state to %s: %m",
8b757a38611006a751c90933d1810cccaa47e1afDaniel Mack u->state_file);
ad867662936a4c7ab2c7116d804c272338801231Lennart Poettering unlink(temp_path);
8b757a38611006a751c90933d1810cccaa47e1afDaniel Mack
8b757a38611006a751c90933d1810cccaa47e1afDaniel Mack return 0;
8b757a38611006a751c90933d1810cccaa47e1afDaniel Mack}
8b757a38611006a751c90933d1810cccaa47e1afDaniel Mack
f3abbe25403444688e1a1a23b9dbcc9aeefc0507Lennart Poetteringstatic int update_cursor_state(Uploader *u) {
f3abbe25403444688e1a1a23b9dbcc9aeefc0507Lennart Poettering _cleanup_free_ char *temp_path = NULL;
f3abbe25403444688e1a1a23b9dbcc9aeefc0507Lennart Poettering _cleanup_fclose_ FILE *f = NULL;
f3abbe25403444688e1a1a23b9dbcc9aeefc0507Lennart Poettering int r;
f3abbe25403444688e1a1a23b9dbcc9aeefc0507Lennart Poettering
f3abbe25403444688e1a1a23b9dbcc9aeefc0507Lennart Poettering if (!u->state_file || !u->last_cursor)
f3abbe25403444688e1a1a23b9dbcc9aeefc0507Lennart Poettering return 0;
f3abbe25403444688e1a1a23b9dbcc9aeefc0507Lennart Poettering
f3abbe25403444688e1a1a23b9dbcc9aeefc0507Lennart Poettering r = fopen_temporary(u->state_file, &f, &temp_path);
f3abbe25403444688e1a1a23b9dbcc9aeefc0507Lennart Poettering if (r < 0)
f3abbe25403444688e1a1a23b9dbcc9aeefc0507Lennart Poettering goto fail;
8b757a38611006a751c90933d1810cccaa47e1afDaniel Mack
8b757a38611006a751c90933d1810cccaa47e1afDaniel Mack fprintf(f,
8b757a38611006a751c90933d1810cccaa47e1afDaniel Mack "# This is private data. Do not parse.\n"
8b757a38611006a751c90933d1810cccaa47e1afDaniel Mack "LAST_CURSOR=%s\n",
8b757a38611006a751c90933d1810cccaa47e1afDaniel Mack u->last_cursor);
3cb10d3a0b1b6a7c44f307f2abb5215104e16941Lennart Poettering
3cb10d3a0b1b6a7c44f307f2abb5215104e16941Lennart Poettering r = fflush_and_check(f);
3cb10d3a0b1b6a7c44f307f2abb5215104e16941Lennart Poettering if (r < 0)
3cb10d3a0b1b6a7c44f307f2abb5215104e16941Lennart Poettering goto fail;
3cb10d3a0b1b6a7c44f307f2abb5215104e16941Lennart Poettering
3cb10d3a0b1b6a7c44f307f2abb5215104e16941Lennart Poettering if (rename(temp_path, u->state_file) < 0) {
f0258e473667f44f4656dde49597b2badb9f598aLennart Poettering r = -errno;
f0258e473667f44f4656dde49597b2badb9f598aLennart Poettering goto fail;
f0258e473667f44f4656dde49597b2badb9f598aLennart Poettering }
f0258e473667f44f4656dde49597b2badb9f598aLennart Poettering
f0258e473667f44f4656dde49597b2badb9f598aLennart Poettering return 0;
f0258e473667f44f4656dde49597b2badb9f598aLennart Poettering
f0258e473667f44f4656dde49597b2badb9f598aLennart Poetteringfail:
f0258e473667f44f4656dde49597b2badb9f598aLennart Poettering if (temp_path)
f0258e473667f44f4656dde49597b2badb9f598aLennart Poettering (void) unlink(temp_path);
3cb10d3a0b1b6a7c44f307f2abb5215104e16941Lennart Poettering
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poettering (void) unlink(u->state_file);
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poettering
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poettering return log_error_errno(r, "Failed to save state %s: %m", u->state_file);
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poettering}
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poettering
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poetteringstatic int load_cursor_state(Uploader *u) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering int r;
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering
24710c48ed16be5fa461fbb303a744a907541dafLennart Poettering if (!u->state_file)
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering return 0;
dbfbb6e776d613cb9be76d13de076d08450c9d29Daniel Mack
dbfbb6e776d613cb9be76d13de076d08450c9d29Daniel Mack r = parse_env_file(u->state_file, NEWLINE,
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering "LAST_CURSOR", &u->last_cursor,
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering NULL);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (r == -ENOENT)
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering log_debug("State file %s is not present.", u->state_file);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering else if (r < 0)
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering return log_error_errno(r, "Failed to read state file %s: %m",
623a4c97b9175f95c4b1c6fc34e36c56f1e4ddbfLennart Poettering u->state_file);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering else
8af5b883227ac8dfa796742b9edcc1647a5d4d6cLennart Poettering log_debug("Last cursor was %s", u->last_cursor);
8af5b883227ac8dfa796742b9edcc1647a5d4d6cLennart Poettering
623a4c97b9175f95c4b1c6fc34e36c56f1e4ddbfLennart Poettering return 0;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering}
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
623a4c97b9175f95c4b1c6fc34e36c56f1e4ddbfLennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
2001c80560e3dae69e14fd994d3978c187af48b8Lennart Poetteringint start_upload(Uploader *u,
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering size_t (*input_callback)(void *ptr,
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering size_t size,
623a4c97b9175f95c4b1c6fc34e36c56f1e4ddbfLennart Poettering size_t nmemb,
a8812dd7f161a3e459c1730ac92ff2bbc9986ff1Lennart Poettering void *userdata),
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering void *data) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering CURLcode code;
9c5e12a4314e7192e834e1b855e5e80111e636a6Tom Gundersen
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering assert(u);
9c5e12a4314e7192e834e1b855e5e80111e636a6Tom Gundersen assert(input_callback);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
623a4c97b9175f95c4b1c6fc34e36c56f1e4ddbfLennart Poettering if (!u->header) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering struct curl_slist *h;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering h = curl_slist_append(NULL, "Content-Type: application/vnd.fdo.journal");
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (!h)
2001c80560e3dae69e14fd994d3978c187af48b8Lennart Poettering return log_oom();
2001c80560e3dae69e14fd994d3978c187af48b8Lennart Poettering
d2579eec5e1b845b2cf29caddc951dc22f2abb91Lennart Poettering h = curl_slist_append(h, "Transfer-Encoding: chunked");
d2579eec5e1b845b2cf29caddc951dc22f2abb91Lennart Poettering if (!h) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering curl_slist_free_all(h);
8ba9fd9cee0eef572f7b3ed7a8c3ed31160e93d3Lennart Poettering return log_oom();
8ba9fd9cee0eef572f7b3ed7a8c3ed31160e93d3Lennart Poettering }
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering h = curl_slist_append(h, "Accept: text/plain");
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (!h) {
d830ebbdf67d8cb32d33d8fdd47cf467fd6d3815Lennart Poettering curl_slist_free_all(h);
d830ebbdf67d8cb32d33d8fdd47cf467fd6d3815Lennart Poettering return log_oom();
d830ebbdf67d8cb32d33d8fdd47cf467fd6d3815Lennart Poettering }
d830ebbdf67d8cb32d33d8fdd47cf467fd6d3815Lennart Poettering
d830ebbdf67d8cb32d33d8fdd47cf467fd6d3815Lennart Poettering u->header = h;
d830ebbdf67d8cb32d33d8fdd47cf467fd6d3815Lennart Poettering }
d830ebbdf67d8cb32d33d8fdd47cf467fd6d3815Lennart Poettering
d830ebbdf67d8cb32d33d8fdd47cf467fd6d3815Lennart Poettering if (!u->easy) {
f3abbe25403444688e1a1a23b9dbcc9aeefc0507Lennart Poettering CURL *curl;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering curl = curl_easy_init();
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (!curl) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering log_error("Call to curl_easy_init failed.");
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering return -ENOSR;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering }
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering /* tell it to POST to the URL */
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering easy_setopt(curl, CURLOPT_POST, 1L,
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering LOG_ERR, return -EXFULL);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering easy_setopt(curl, CURLOPT_ERRORBUFFER, u->error,
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering LOG_ERR, return -EXFULL);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering /* set where to write to */
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering easy_setopt(curl, CURLOPT_WRITEFUNCTION, output_callback,
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering LOG_ERR, return -EXFULL);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering easy_setopt(curl, CURLOPT_WRITEDATA, data,
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering LOG_ERR, return -EXFULL);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering /* set where to read from */
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering easy_setopt(curl, CURLOPT_READFUNCTION, input_callback,
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering LOG_ERR, return -EXFULL);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering easy_setopt(curl, CURLOPT_READDATA, data,
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering LOG_ERR, return -EXFULL);
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering /* use our special own mime type and chunked transfer */
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering easy_setopt(curl, CURLOPT_HTTPHEADER, u->header,
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering LOG_ERR, return -EXFULL);
8db0d2f5c37e7e8f5bfce016cfdad7947a3ea939Zbigniew Jędrzejewski-Szmek
0db4c90afd7d9c7c8884bf8b3ec459edc74a03daDaniel Mack if (_unlikely_(log_get_max_level() >= LOG_DEBUG))
b6c5d46b23a28b5b03601ee1e8162b1bc7c7be25Daniel Mack /* enable verbose for easier tracing */
0db4c90afd7d9c7c8884bf8b3ec459edc74a03daDaniel Mack easy_setopt(curl, CURLOPT_VERBOSE, 1L, LOG_WARNING, );
931851e8e492a4d2715e22dcde50a5e7ccef4b49Lennart Poettering
931851e8e492a4d2715e22dcde50a5e7ccef4b49Lennart Poettering easy_setopt(curl, CURLOPT_USERAGENT,
51323288fc628a5cac50914df915545d685b793eLennart Poettering "systemd-journal-upload " PACKAGE_STRING,
931851e8e492a4d2715e22dcde50a5e7ccef4b49Lennart Poettering LOG_WARNING, );
931851e8e492a4d2715e22dcde50a5e7ccef4b49Lennart Poettering
931851e8e492a4d2715e22dcde50a5e7ccef4b49Lennart Poettering if (arg_key || startswith(u->url, "https://")) {
51323288fc628a5cac50914df915545d685b793eLennart Poettering easy_setopt(curl, CURLOPT_SSLKEY, arg_key ?: PRIV_KEY_FILE,
106784ebb7b303ae471851100a773ad2aebf5b80Daniel Mack LOG_ERR, return -EXFULL);
106784ebb7b303ae471851100a773ad2aebf5b80Daniel Mack easy_setopt(curl, CURLOPT_SSLCERT, arg_cert ?: CERT_FILE,
931851e8e492a4d2715e22dcde50a5e7ccef4b49Lennart Poettering LOG_ERR, return -EXFULL);
51323288fc628a5cac50914df915545d685b793eLennart Poettering }
106784ebb7b303ae471851100a773ad2aebf5b80Daniel Mack
931851e8e492a4d2715e22dcde50a5e7ccef4b49Lennart Poettering if (streq_ptr(arg_trust, "all"))
51323288fc628a5cac50914df915545d685b793eLennart Poettering easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0,
4e5bf5e15899de3f9d11c2ddfe9721d9f8b07a37Daniel Mack LOG_ERR, return -EUCLEAN);
4e5bf5e15899de3f9d11c2ddfe9721d9f8b07a37Daniel Mack else if (arg_trust || startswith(u->url, "https://"))
4e5bf5e15899de3f9d11c2ddfe9721d9f8b07a37Daniel Mack easy_setopt(curl, CURLOPT_CAINFO, arg_trust ?: TRUST_FILE,
106784ebb7b303ae471851100a773ad2aebf5b80Daniel Mack LOG_ERR, return -EXFULL);
106784ebb7b303ae471851100a773ad2aebf5b80Daniel Mack
106784ebb7b303ae471851100a773ad2aebf5b80Daniel Mack if (arg_key || arg_trust)
106784ebb7b303ae471851100a773ad2aebf5b80Daniel Mack easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1,
51323288fc628a5cac50914df915545d685b793eLennart Poettering LOG_WARNING, );
51323288fc628a5cac50914df915545d685b793eLennart Poettering
u->easy = curl;
} else {
/* truncate the potential old error message */
u->error[0] = '\0';
free(u->answer);
u->answer = 0;
}
/* upload to this place */
code = curl_easy_setopt(u->easy, CURLOPT_URL, u->url);
if (code) {
log_error("curl_easy_setopt CURLOPT_URL failed: %s",
curl_easy_strerror(code));
return -EXFULL;
}
u->uploading = true;
return 0;
}
static size_t fd_input_callback(void *buf, size_t size, size_t nmemb, void *userp) {
Uploader *u = userp;
ssize_t r;
assert(u);
assert(nmemb <= SSIZE_MAX / size);
if (u->input < 0)
return 0;
r = read(u->input, buf, size * nmemb);
log_debug("%s: allowed %zu, read %zd", __func__, size*nmemb, r);
if (r > 0)
return r;
u->uploading = false;
if (r == 0) {
log_debug("Reached EOF");
close_fd_input(u);
return 0;
} else {
log_error_errno(errno, "Aborting transfer after read error on input: %m.");
return CURL_READFUNC_ABORT;
}
}
static void close_fd_input(Uploader *u) {
assert(u);
if (u->input >= 0)
close_nointr(u->input);
u->input = -1;
u->timeout = 0;
}
static int dispatch_fd_input(sd_event_source *event,
int fd,
uint32_t revents,
void *userp) {
Uploader *u = userp;
assert(u);
assert(fd >= 0);
if (revents & EPOLLHUP) {
log_debug("Received HUP");
close_fd_input(u);
return 0;
}
if (!(revents & EPOLLIN)) {
log_warning("Unexpected poll event %"PRIu32".", revents);
return -EINVAL;
}
if (u->uploading) {
log_warning("dispatch_fd_input called when uploading, ignoring.");
return 0;
}
return start_upload(u, fd_input_callback, u);
}
static int open_file_for_upload(Uploader *u, const char *filename) {
int fd, r = 0;
if (streq(filename, "-"))
fd = STDIN_FILENO;
else {
fd = open(filename, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
return log_error_errno(errno, "Failed to open %s: %m", filename);
}
u->input = fd;
if (arg_follow) {
r = sd_event_add_io(u->events, &u->input_event,
fd, EPOLLIN, dispatch_fd_input, u);
if (r < 0) {
if (r != -EPERM || arg_follow > 0)
return log_error_errno(r, "Failed to register input event: %m");
/* Normal files should just be consumed without polling. */
r = start_upload(u, fd_input_callback, u);
}
}
return r;
}
static int dispatch_sigterm(sd_event_source *event,
const struct signalfd_siginfo *si,
void *userdata) {
Uploader *u = userdata;
assert(u);
log_received_signal(LOG_INFO, si);
close_fd_input(u);
close_journal_input(u);
sd_event_exit(u->events, 0);
return 0;
}
static int setup_signals(Uploader *u) {
int r;
assert(u);
assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, SIGTERM, -1) >= 0);
r = sd_event_add_signal(u->events, &u->sigterm_event, SIGTERM, dispatch_sigterm, u);
if (r < 0)
return r;
r = sd_event_add_signal(u->events, &u->sigint_event, SIGINT, dispatch_sigterm, u);
if (r < 0)
return r;
return 0;
}
static int setup_uploader(Uploader *u, const char *url, const char *state_file) {
int r;
const char *host, *proto = "";
assert(u);
assert(url);
memzero(u, sizeof(Uploader));
u->input = -1;
if (!(host = startswith(url, "http://")) && !(host = startswith(url, "https://"))) {
host = url;
proto = "https://";
}
if (strchr(host, ':'))
u->url = strjoin(proto, url, "/upload", NULL);
else {
char *t;
size_t x;
t = strdupa(url);
x = strlen(t);
while (x > 0 && t[x - 1] == '/')
t[x - 1] = '\0';
u->url = strjoin(proto, t, ":" STRINGIFY(DEFAULT_PORT), "/upload", NULL);
}
if (!u->url)
return log_oom();
u->state_file = state_file;
r = sd_event_default(&u->events);
if (r < 0)
return log_error_errno(r, "sd_event_default failed: %m");
r = setup_signals(u);
if (r < 0)
return log_error_errno(r, "Failed to set up signals: %m");
return load_cursor_state(u);
}
static void destroy_uploader(Uploader *u) {
assert(u);
curl_easy_cleanup(u->easy);
curl_slist_free_all(u->header);
free(u->answer);
free(u->last_cursor);
free(u->current_cursor);
free(u->url);
u->input_event = sd_event_source_unref(u->input_event);
close_fd_input(u);
close_journal_input(u);
sd_event_source_unref(u->sigterm_event);
sd_event_source_unref(u->sigint_event);
sd_event_unref(u->events);
}
static int perform_upload(Uploader *u) {
CURLcode code;
long status;
assert(u);
code = curl_easy_perform(u->easy);
if (code) {
if (u->error[0])
log_error("Upload to %s failed: %.*s",
u->url, (int) sizeof(u->error), u->error);
else
log_error("Upload to %s failed: %s",
u->url, curl_easy_strerror(code));
return -EIO;
}
code = curl_easy_getinfo(u->easy, CURLINFO_RESPONSE_CODE, &status);
if (code) {
log_error("Failed to retrieve response code: %s",
curl_easy_strerror(code));
return -EUCLEAN;
}
if (status >= 300) {
log_error("Upload to %s failed with code %ld: %s",
u->url, status, strna(u->answer));
return -EIO;
} else if (status < 200) {
log_error("Upload to %s finished with unexpected code %ld: %s",
u->url, status, strna(u->answer));
return -EIO;
} else
log_debug("Upload finished successfully with code %ld: %s",
status, strna(u->answer));
free(u->last_cursor);
u->last_cursor = u->current_cursor;
u->current_cursor = NULL;
return update_cursor_state(u);
}
static int parse_config(void) {
const ConfigTableItem items[] = {
{ "Upload", "URL", config_parse_string, 0, &arg_url },
{ "Upload", "ServerKeyFile", config_parse_path, 0, &arg_key },
{ "Upload", "ServerCertificateFile", config_parse_path, 0, &arg_cert },
{ "Upload", "TrustedCertificateFile", config_parse_path, 0, &arg_trust },
{}};
return config_parse_many(PKGSYSCONFDIR "/journal-upload.conf",
CONF_DIRS_NULSTR("systemd/journal-upload.conf"),
"Upload\0", config_item_table_lookup, items,
false, NULL);
}
static void help(void) {
printf("%s -u URL {FILE|-}...\n\n"
"Upload journal events to a remote server.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" -u --url=URL Upload to this address (default port "
STRINGIFY(DEFAULT_PORT) ")\n"
" --key=FILENAME Specify key in PEM format (default:\n"
" \"" PRIV_KEY_FILE "\")\n"
" --cert=FILENAME Specify certificate in PEM format (default:\n"
" \"" CERT_FILE "\")\n"
" --trust=FILENAME|all Specify CA certificate or disable checking (default:\n"
" \"" TRUST_FILE "\")\n"
" --system Use the system journal\n"
" --user Use the user journal for the current user\n"
" -m --merge Use all available journals\n"
" -M --machine=CONTAINER Operate on local container\n"
" -D --directory=PATH Use journal files from directory\n"
" --file=PATH Use this journal file\n"
" --cursor=CURSOR Start at the specified cursor\n"
" --after-cursor=CURSOR Start after the specified cursor\n"
" --follow[=BOOL] Do [not] wait for input\n"
" --save-state[=FILE] Save uploaded cursors (default \n"
" " STATE_FILE ")\n"
" -h --help Show this help and exit\n"
" --version Print version string and exit\n"
, program_invocation_short_name);
}
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_KEY,
ARG_CERT,
ARG_TRUST,
ARG_USER,
ARG_SYSTEM,
ARG_FILE,
ARG_CURSOR,
ARG_AFTER_CURSOR,
ARG_FOLLOW,
ARG_SAVE_STATE,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "url", required_argument, NULL, 'u' },
{ "key", required_argument, NULL, ARG_KEY },
{ "cert", required_argument, NULL, ARG_CERT },
{ "trust", required_argument, NULL, ARG_TRUST },
{ "system", no_argument, NULL, ARG_SYSTEM },
{ "user", no_argument, NULL, ARG_USER },
{ "merge", no_argument, NULL, 'm' },
{ "machine", required_argument, NULL, 'M' },
{ "directory", required_argument, NULL, 'D' },
{ "file", required_argument, NULL, ARG_FILE },
{ "cursor", required_argument, NULL, ARG_CURSOR },
{ "after-cursor", required_argument, NULL, ARG_AFTER_CURSOR },
{ "follow", optional_argument, NULL, ARG_FOLLOW },
{ "save-state", optional_argument, NULL, ARG_SAVE_STATE },
{}
};
int c, r;
assert(argc >= 0);
assert(argv);
opterr = 0;
while ((c = getopt_long(argc, argv, "hu:mM:D:", options, NULL)) >= 0)
switch(c) {
case 'h':
help();
return 0 /* done */;
case ARG_VERSION:
return version();
case 'u':
if (arg_url) {
log_error("cannot use more than one --url");
return -EINVAL;
}
arg_url = optarg;
break;
case ARG_KEY:
if (arg_key) {
log_error("cannot use more than one --key");
return -EINVAL;
}
arg_key = optarg;
break;
case ARG_CERT:
if (arg_cert) {
log_error("cannot use more than one --cert");
return -EINVAL;
}
arg_cert = optarg;
break;
case ARG_TRUST:
if (arg_trust) {
log_error("cannot use more than one --trust");
return -EINVAL;
}
arg_trust = optarg;
break;
case ARG_SYSTEM:
arg_journal_type |= SD_JOURNAL_SYSTEM;
break;
case ARG_USER:
arg_journal_type |= SD_JOURNAL_CURRENT_USER;
break;
case 'm':
arg_merge = true;
break;
case 'M':
if (arg_machine) {
log_error("cannot use more than one --machine/-M");
return -EINVAL;
}
arg_machine = optarg;
break;
case 'D':
if (arg_directory) {
log_error("cannot use more than one --directory/-D");
return -EINVAL;
}
arg_directory = optarg;
break;
case ARG_FILE:
r = glob_extend(&arg_file, optarg);
if (r < 0)
return log_error_errno(r, "Failed to add paths: %m");
break;
case ARG_CURSOR:
if (arg_cursor) {
log_error("cannot use more than one --cursor/--after-cursor");
return -EINVAL;
}
arg_cursor = optarg;
break;
case ARG_AFTER_CURSOR:
if (arg_cursor) {
log_error("cannot use more than one --cursor/--after-cursor");
return -EINVAL;
}
arg_cursor = optarg;
arg_after_cursor = true;
break;
case ARG_FOLLOW:
if (optarg) {
r = parse_boolean(optarg);
if (r < 0) {
log_error("Failed to parse --follow= parameter.");
return -EINVAL;
}
arg_follow = !!r;
} else
arg_follow = true;
break;
case ARG_SAVE_STATE:
arg_save_state = optarg ?: STATE_FILE;
break;
case '?':
log_error("Unknown option %s.", argv[optind-1]);
return -EINVAL;
case ':':
log_error("Missing argument to %s.", argv[optind-1]);
return -EINVAL;
default:
assert_not_reached("Unhandled option code.");
}
if (!arg_url) {
log_error("Required --url/-u option missing.");
return -EINVAL;
}
if (!!arg_key != !!arg_cert) {
log_error("Options --key and --cert must be used together.");
return -EINVAL;
}
if (optind < argc && (arg_directory || arg_file || arg_machine || arg_journal_type)) {
log_error("Input arguments make no sense with journal input.");
return -EINVAL;
}
return 1;
}
static int open_journal(sd_journal **j) {
int r;
if (arg_directory)
r = sd_journal_open_directory(j, arg_directory, arg_journal_type);
else if (arg_file)
r = sd_journal_open_files(j, (const char**) arg_file, 0);
else if (arg_machine)
r = sd_journal_open_container(j, arg_machine, 0);
else
r = sd_journal_open(j, !arg_merge*SD_JOURNAL_LOCAL_ONLY + arg_journal_type);
if (r < 0)
log_error_errno(r, "Failed to open %s: %m",
arg_directory ? arg_directory : arg_file ? "files" : "journal");
return r;
}
int main(int argc, char **argv) {
Uploader u;
int r;
bool use_journal;
log_show_color(true);
log_parse_environment();
r = parse_config();
if (r < 0)
goto finish;
r = parse_argv(argc, argv);
if (r <= 0)
goto finish;
sigbus_install();
r = setup_uploader(&u, arg_url, arg_save_state);
if (r < 0)
goto cleanup;
sd_event_set_watchdog(u.events, true);
r = check_cursor_updating(&u);
if (r < 0)
goto cleanup;
log_debug("%s running as pid "PID_FMT,
program_invocation_short_name, getpid());
use_journal = optind >= argc;
if (use_journal) {
sd_journal *j;
r = open_journal(&j);
if (r < 0)
goto finish;
r = open_journal_for_upload(&u, j,
arg_cursor ?: u.last_cursor,
arg_cursor ? arg_after_cursor : true,
!!arg_follow);
if (r < 0)
goto finish;
}
sd_notify(false,
"READY=1\n"
"STATUS=Processing input...");
for (;;) {
r = sd_event_get_state(u.events);
if (r < 0)
break;
if (r == SD_EVENT_FINISHED)
break;
if (use_journal) {
if (!u.journal)
break;
r = check_journal_input(&u);
} else if (u.input < 0 && !use_journal) {
if (optind >= argc)
break;
log_debug("Using %s as input.", argv[optind]);
r = open_file_for_upload(&u, argv[optind++]);
}
if (r < 0)
goto cleanup;
if (u.uploading) {
r = perform_upload(&u);
if (r < 0)
break;
}
r = sd_event_run(u.events, u.timeout);
if (r < 0) {
log_error_errno(r, "Failed to run event loop: %m");
break;
}
}
cleanup:
sd_notify(false,
"STOPPING=1\n"
"STATUS=Shutting down...");
destroy_uploader(&u);
finish:
return r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}