journal-upload.c revision e1ad6e245dcf63faa8f183063eb97678f4f9ac94
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering This file is part of systemd.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering Copyright 2014 Zbigniew Jędrzejewski-Szmek
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 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 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/>.
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen#define PRIV_KEY_FILE CERTIFICATE_ROOT "/private/journal-upload.pem"
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen#define CERT_FILE CERTIFICATE_ROOT "/certs/journal-upload.pem"
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen#define TRUST_FILE CERTIFICATE_ROOT "/ca/trusted.pem"
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringstatic const char* arg_url;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringstatic bool arg_after_cursor = false;
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poetteringstatic bool arg_merge = false;
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering#define STATE_FILE "/var/lib/systemd/journal-upload/state"
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering#define easy_setopt(curl, opt, value, level, cmd) \
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering code = curl_easy_setopt(curl, opt, value); \
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering "curl_easy_setopt " #opt " failed: %s", \
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering log_debug("The server answers (%zu bytes): %.*s",
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering log_warning("Failed to store server answer (%zu bytes): %s",
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poetteringstatic int update_cursor_state(Uploader *u) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering r = fopen_temporary(u->state_file, &f, &temp_path);
3e684349c2cead2e6fd2f816c34eb17daba23a49Lennart Poettering "# This is private data. Do not parse.\n"
3e684349c2cead2e6fd2f816c34eb17daba23a49Lennart Poettering "LAST_CURSOR=%s\n",
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (ferror(f) || rename(temp_path, u->state_file) < 0) {
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen log_error("Failed to save state %s: %s", u->state_file, strerror(-r));
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering r = parse_env_file(u->state_file, NEWLINE,
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (r < 0 && r != -ENOENT) {
6cb08a8930bdaca950b152b1e8b82466ed59511cLennart Poettering log_error("Failed to read state file %s: %s",
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering h = curl_slist_append(NULL, "Content-Type: application/vnd.fdo.journal");
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering h = curl_slist_append(h, "Transfer-Encoding: chunked");
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering h = curl_slist_append(h, "Accept: text/plain");
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering log_error("Call to curl_easy_init failed.");
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering /* tell it to POST to the URL */
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering easy_setopt(curl, CURLOPT_ERRORBUFFER, &u->error,
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering /* set where to write to */
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering easy_setopt(curl, CURLOPT_WRITEFUNCTION, output_callback,
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering easy_setopt(curl, CURLOPT_WRITEDATA, data,
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering /* set where to read from */
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering easy_setopt(curl, CURLOPT_READFUNCTION, input_callback,
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering easy_setopt(curl, CURLOPT_READDATA, data,
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering /* use our special own mime type and chunked transfer */
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering easy_setopt(curl, CURLOPT_HTTPHEADER, u->header,
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering /* enable verbose for easier tracing */
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering easy_setopt(curl, CURLOPT_VERBOSE, 1L, LOG_WARNING, );
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering "systemd-journal-upload " PACKAGE_STRING,
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering if (arg_key || startswith(u->url, "https://")) {
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering easy_setopt(curl, CURLOPT_SSLKEY, arg_key ?: PRIV_KEY_FILE,
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering easy_setopt(curl, CURLOPT_SSLCERT, arg_cert ?: CERT_FILE,
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering if (arg_trust || startswith(u->url, "https://"))
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering easy_setopt(curl, CURLOPT_CAINFO, arg_trust ?: TRUST_FILE,
de54e62b4bd7856fb897c9a2ee93cc228adb2135Lennart Poettering easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1,
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering /* truncate the potential old error message */
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering /* upload to this place */
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering code = curl_easy_setopt(u->easy, CURLOPT_URL, u->url);
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering log_error("curl_easy_setopt CURLOPT_URL failed: %s",
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poetteringstatic size_t fd_input_callback(void *buf, size_t size, size_t nmemb, void *userp) {
d74fb368b18f0fbd9a4fe6f15691bbea7f3c4a01Tom Gundersen log_debug("%s: allowed %zu, read %zu", __func__, size*nmemb, r);
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen if (r == 0) {
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering log_error("Aborting transfer after read error on input: %m.");
4e0b8b17a7465653f4e7b819dad5f8e30d64c0c4Tom Gundersenstatic int dispatch_fd_input(sd_event_source *event,
571370c1555d2aa697733479a50957aff024bbcbLennart Poettering log_warning("Unexpected poll event %"PRIu32".", revents);
de54e62b4bd7856fb897c9a2ee93cc228adb2135Lennart Poettering log_warning("dispatch_fd_input called when uploading, ignoring.");
de54e62b4bd7856fb897c9a2ee93cc228adb2135Lennart Poettering return start_upload(u, fd_input_callback, u);
de54e62b4bd7856fb897c9a2ee93cc228adb2135Lennart Poetteringstatic int open_file_for_upload(Uploader *u, const char *filename) {
de54e62b4bd7856fb897c9a2ee93cc228adb2135Lennart Poettering fd = open(filename, O_RDONLY|O_CLOEXEC|O_NOCTTY);
de54e62b4bd7856fb897c9a2ee93cc228adb2135Lennart Poettering log_error("Failed to open %s: %m", filename);
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen r = sd_event_add_io(u->events, &u->input_event,
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen log_error("Failed to register input event: %s", strerror(-r));
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen /* Normal files should just be consumed without polling. */
011842775f750711833526d5bba1b818713947f5Lennart Poetteringstatic int dispatch_sigterm(sd_event_source *event,
f4461e5641d53f27d6e76e0607bdaa9c0c58c1f6Lennart Poettering sigset_add_many(&mask, SIGINT, SIGTERM, -1);
f4461e5641d53f27d6e76e0607bdaa9c0c58c1f6Lennart Poettering assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
6bb2c08597c999c429e889cd2403b2fef5f3e1a0Lennart Poettering r = sd_event_add_signal(u->events, &u->sigterm_event, SIGTERM, dispatch_sigterm, u);
6a1a5eec43892dee3ff6e208bceb1931c25c782eLennart Poettering r = sd_event_add_signal(u->events, &u->sigint_event, SIGINT, dispatch_sigterm, u);
de54e62b4bd7856fb897c9a2ee93cc228adb2135Lennart Poetteringstatic int setup_uploader(Uploader *u, const char *url, const char *state_file) {
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen if (!startswith(url, "http://") && !startswith(url, "https://"))
de54e62b4bd7856fb897c9a2ee93cc228adb2135Lennart Poettering log_error("sd_event_default failed: %s", strerror(-r));
de54e62b4bd7856fb897c9a2ee93cc228adb2135Lennart Poettering log_error("Failed to set up signals: %s", strerror(-r));
de54e62b4bd7856fb897c9a2ee93cc228adb2135Lennart Poetteringstatic void destroy_uploader(Uploader *u) {
de54e62b4bd7856fb897c9a2ee93cc228adb2135Lennart Poettering u->input_event = sd_event_source_unref(u->input_event);
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen u->error[0] ? (int) sizeof(u->error) : INT_MAX,
f4461e5641d53f27d6e76e0607bdaa9c0c58c1f6Lennart Poettering u->error[0] ? u->error : curl_easy_strerror(code));
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering code = curl_easy_getinfo(u->easy, CURLINFO_RESPONSE_CODE, &status);
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering log_error("Failed to retrieve response code: %s",
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering log_error("Upload to %s failed with code %lu: %s",
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering log_error("Upload to %s finished with unexpected code %lu: %s",
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering log_debug("Upload finished successfully with code %lu: %s",
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poetteringstatic int parse_config(void) {
6cb08a8930bdaca950b152b1e8b82466ed59511cLennart Poettering { "Upload", "URL", config_parse_string, 0, &arg_url },
6cb08a8930bdaca950b152b1e8b82466ed59511cLennart Poettering { "Upload", "ServerKeyFile", config_parse_path, 0, &arg_key },
6cb08a8930bdaca950b152b1e8b82466ed59511cLennart Poettering { "Upload", "ServerCertificateFile", config_parse_path, 0, &arg_cert },
6cb08a8930bdaca950b152b1e8b82466ed59511cLennart Poettering { "Upload", "TrustedCertificateFile", config_parse_path, 0, &arg_trust },
6cb08a8930bdaca950b152b1e8b82466ed59511cLennart Poettering return config_parse(NULL, PKGSYSCONFDIR "/journal-upload.conf", NULL,
92ec902aad1ade7acbe50efd7b8ef87fbdc63af3Lennart Poettering false, false, true, NULL);
92ec902aad1ade7acbe50efd7b8ef87fbdc63af3Lennart Poetteringstatic void help(void) {
92ec902aad1ade7acbe50efd7b8ef87fbdc63af3Lennart Poettering "Upload journal events to a remote server.\n\n"
92ec902aad1ade7acbe50efd7b8ef87fbdc63af3Lennart Poettering " -h --help Show this help\n"
de54e62b4bd7856fb897c9a2ee93cc228adb2135Lennart Poettering " --version Show package version\n"
de54e62b4bd7856fb897c9a2ee93cc228adb2135Lennart Poettering " -u --url=URL Upload to this address\n"
de54e62b4bd7856fb897c9a2ee93cc228adb2135Lennart Poettering " --key=FILENAME Specify key in PEM format\n"
de54e62b4bd7856fb897c9a2ee93cc228adb2135Lennart Poettering " --cert=FILENAME Specify certificate in PEM format\n"
92ec902aad1ade7acbe50efd7b8ef87fbdc63af3Lennart Poettering " --trust=FILENAME Specify CA certificate in PEM format\n"
92ec902aad1ade7acbe50efd7b8ef87fbdc63af3Lennart Poettering " --system Use the system journal\n"
92ec902aad1ade7acbe50efd7b8ef87fbdc63af3Lennart Poettering " --user Use the user journal for the current user\n"
92ec902aad1ade7acbe50efd7b8ef87fbdc63af3Lennart Poettering " -m --merge Use all available journals\n"
92ec902aad1ade7acbe50efd7b8ef87fbdc63af3Lennart Poettering " -M --machine=CONTAINER Operate on local container\n"
92ec902aad1ade7acbe50efd7b8ef87fbdc63af3Lennart Poettering " -D --directory=PATH Use journal files from directory\n"
92ec902aad1ade7acbe50efd7b8ef87fbdc63af3Lennart Poettering " --file=PATH Use this journal file\n"
92ec902aad1ade7acbe50efd7b8ef87fbdc63af3Lennart Poettering " --cursor=CURSOR Start at the specified cursor\n"
92ec902aad1ade7acbe50efd7b8ef87fbdc63af3Lennart Poettering " --after-cursor=CURSOR Start after the specified cursor\n"
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersen " --follow[=BOOL] Do [not] wait for input\n"
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering " --save-state[=FILE] Save uploaded cursors (default \n"
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersen " -h --help Show this help and exit\n"
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering " --version Print version string and exit\n"
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poetteringstatic int parse_argv(int argc, char *argv[]) {
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering { "version", no_argument, NULL, ARG_VERSION },
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering { "key", required_argument, NULL, ARG_KEY },
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering { "cert", required_argument, NULL, ARG_CERT },
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering { "trust", required_argument, NULL, ARG_TRUST },
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering { "system", no_argument, NULL, ARG_SYSTEM },
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering { "machine", required_argument, NULL, 'M' },
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering { "directory", required_argument, NULL, 'D' },
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering { "file", required_argument, NULL, ARG_FILE },
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering { "cursor", required_argument, NULL, ARG_CURSOR },
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering { "after-cursor", required_argument, NULL, ARG_AFTER_CURSOR },
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering { "follow", optional_argument, NULL, ARG_FOLLOW },
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering { "save-state", optional_argument, NULL, ARG_SAVE_STATE },
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering while ((c = getopt_long(argc, argv, "hu:mM:D:", options, NULL)) >= 0)
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering return 0 /* done */;
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering return 0 /* done */;
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering log_error("cannot use more than one --url");
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering log_error("cannot use more than one --key");
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering log_error("cannot use more than one --cert");
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering log_error("cannot use more than one --trust");
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering arg_journal_type |= SD_JOURNAL_CURRENT_USER;
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering log_error("cannot use more than one --machine/-M");
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering log_error("cannot use more than one --directory/-D");
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering log_error("Failed to add paths: %s", strerror(-r));
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering log_error("cannot use more than one --cursor/--after-cursor");
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering log_error("cannot use more than one --cursor/--after-cursor");
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen log_error("Failed to parse --follow= parameter.");
case ARG_SAVE_STATE:
return -EINVAL;
return -EINVAL;
if (!arg_url) {
return -EINVAL;
return -EINVAL;
return -EINVAL;
if (arg_directory)
else if (arg_file)
else if (arg_machine)
strerror(-r));
Uploader u;
bool use_journal;
log_show_color(true);
r = parse_config();
goto finish;
goto finish;
goto cleanup;
if (use_journal) {
sd_journal *j;
r = open_journal(&j);
goto finish;
r = open_journal_for_upload(&u, j,
!!arg_follow);
goto finish;
sd_notify(false,
if (use_journal) {
if (!u.journal)
r = check_journal_input(&u);
goto cleanup;
if (r == SD_EVENT_FINISHED)
if (u.uploading) {
r = perform_upload(&u);
destroy_uploader(&u);