journal-upload.c revision 1af719edc5958c01c19204fb68d6fc45c9eea85c
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering This file is part of systemd.
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering Copyright 2014 Zbigniew Jędrzejewski-Szmek
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering systemd is free software; you can redistribute it and/or modify it
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering under the terms of the GNU Lesser General Public License as published by
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering (at your option) any later version.
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering systemd is distributed in the hope that it will be useful, but
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering Lesser General Public License for more details.
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering You should have received a copy of the GNU Lesser General Public License
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
3f6fd1ba65f962702753c4ad284b588e59689a23Lennart Poettering#define PRIV_KEY_FILE CERTIFICATE_ROOT "/private/journal-upload.pem"
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering#define CERT_FILE CERTIFICATE_ROOT "/certs/journal-upload.pem"
9d12709626bccc0cae677a7035f62efe6aabb4abLennart Poettering#define TRUST_FILE CERTIFICATE_ROOT "/ca/trusted.pem"
3f6fd1ba65f962702753c4ad284b588e59689a23Lennart Poetteringstatic const char* arg_url;
0b452006de98294d1690f045f6ea2f7f6630ec3bRonny Chevalierstatic bool arg_after_cursor = false;
3f6fd1ba65f962702753c4ad284b588e59689a23Lennart Poetteringstatic bool arg_merge = false;
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering#define STATE_FILE "/var/lib/systemd/journal-upload/state"
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering#define easy_setopt(curl, opt, value, level, cmd) \
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering code = curl_easy_setopt(curl, opt, value); \
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering "curl_easy_setopt " #opt " failed: %s", \
c454426c54c9beb274f415a80c64a4f1580700e7Lennart Poettering log_debug("The server answers (%zu bytes): %.*s",
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering log_warning("Failed to store server answer (%zu bytes): %s",
acf97e213e69a97e63ab8f7fad7ecd53608c757aLennart Poetteringstatic int update_cursor_state(Uploader *u) {
acf97e213e69a97e63ab8f7fad7ecd53608c757aLennart Poettering r = fopen_temporary(u->state_file, &f, &temp_path);
8b0cc9a36c8f92f010f2e8465942d2cd7c580d78Lennart Poettering "# This is private data. Do not parse.\n"
8b0cc9a36c8f92f010f2e8465942d2cd7c580d78Lennart Poettering "LAST_CURSOR=%s\n",
0b63e2789f984e84f40bf6e49f5da15c87298cedLennart Poettering if (ferror(f) || rename(temp_path, u->state_file) < 0) {
0b63e2789f984e84f40bf6e49f5da15c87298cedLennart Poettering log_error("Failed to save state %s: %s", u->state_file, strerror(-r));
56159e0d918e9a9be07988133bb2847779325de0Lennart Poetteringstatic int load_cursor_state(Uploader *u) {
0b63e2789f984e84f40bf6e49f5da15c87298cedLennart Poettering r = parse_env_file(u->state_file, NEWLINE,
56159e0d918e9a9be07988133bb2847779325de0Lennart Poettering if (r < 0 && r != -ENOENT) {
56159e0d918e9a9be07988133bb2847779325de0Lennart Poettering log_error("Failed to read state file %s: %s",
fee6d013d859bc66f5c993530898fece53fab06dLennart Poettering h = curl_slist_append(NULL, "Content-Type: application/vnd.fdo.journal");
0b63e2789f984e84f40bf6e49f5da15c87298cedLennart Poettering h = curl_slist_append(h, "Transfer-Encoding: chunked");
0b63e2789f984e84f40bf6e49f5da15c87298cedLennart Poettering h = curl_slist_append(h, "Accept: text/plain");
a1da85830bfaa77b9eb9c54693e5573559c97e50Tom Gundersen /* tell it to POST to the URL */
0b63e2789f984e84f40bf6e49f5da15c87298cedLennart Poettering easy_setopt(curl, CURLOPT_ERRORBUFFER, u->error,
0b63e2789f984e84f40bf6e49f5da15c87298cedLennart Poettering /* set where to write to */
0b63e2789f984e84f40bf6e49f5da15c87298cedLennart Poettering easy_setopt(curl, CURLOPT_WRITEFUNCTION, output_callback,
0b63e2789f984e84f40bf6e49f5da15c87298cedLennart Poettering easy_setopt(curl, CURLOPT_WRITEDATA, data,
0b63e2789f984e84f40bf6e49f5da15c87298cedLennart Poettering /* set where to read from */
0b63e2789f984e84f40bf6e49f5da15c87298cedLennart Poettering easy_setopt(curl, CURLOPT_READFUNCTION, input_callback,
0b63e2789f984e84f40bf6e49f5da15c87298cedLennart Poettering easy_setopt(curl, CURLOPT_READDATA, data,
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering /* use our special own mime type and chunked transfer */
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering easy_setopt(curl, CURLOPT_HTTPHEADER, u->header,
cd61c3bfd718fb398cc53ced906266a9297782c9Lennart Poettering /* enable verbose for easier tracing */
cd61c3bfd718fb398cc53ced906266a9297782c9Lennart Poettering easy_setopt(curl, CURLOPT_VERBOSE, 1L, LOG_WARNING, );
b6b1849830f5e4a6065c3b0c993668e500c954d3Lennart Poettering "systemd-journal-upload " PACKAGE_STRING,
cd61c3bfd718fb398cc53ced906266a9297782c9Lennart Poettering if (arg_key || startswith(u->url, "https://")) {
cd61c3bfd718fb398cc53ced906266a9297782c9Lennart Poettering easy_setopt(curl, CURLOPT_SSLKEY, arg_key ?: PRIV_KEY_FILE,
cd61c3bfd718fb398cc53ced906266a9297782c9Lennart Poettering easy_setopt(curl, CURLOPT_SSLCERT, arg_cert ?: CERT_FILE,
cd61c3bfd718fb398cc53ced906266a9297782c9Lennart Poettering if (arg_trust || startswith(u->url, "https://"))
cd61c3bfd718fb398cc53ced906266a9297782c9Lennart Poettering easy_setopt(curl, CURLOPT_CAINFO, arg_trust ?: TRUST_FILE,
cd61c3bfd718fb398cc53ced906266a9297782c9Lennart Poettering easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1,
cd61c3bfd718fb398cc53ced906266a9297782c9Lennart Poettering /* truncate the potential old error message */
cd61c3bfd718fb398cc53ced906266a9297782c9Lennart Poettering /* upload to this place */
cd61c3bfd718fb398cc53ced906266a9297782c9Lennart Poettering code = curl_easy_setopt(u->easy, CURLOPT_URL, u->url);
cd61c3bfd718fb398cc53ced906266a9297782c9Lennart Poettering log_error("curl_easy_setopt CURLOPT_URL failed: %s",
cd61c3bfd718fb398cc53ced906266a9297782c9Lennart Poetteringstatic size_t fd_input_callback(void *buf, size_t size, size_t nmemb, void *userp) {
cd61c3bfd718fb398cc53ced906266a9297782c9Lennart Poettering log_debug("%s: allowed %zu, read %zu", __func__, size*nmemb, r);
cd61c3bfd718fb398cc53ced906266a9297782c9Lennart Poettering log_error("Aborting transfer after read error on input: %m.");
10f9c75519671e7c7ab8993b54fe22da7c2d0c38Lennart Poetteringstatic int dispatch_fd_input(sd_event_source *event,
cd61c3bfd718fb398cc53ced906266a9297782c9Lennart Poettering log_warning("Unexpected poll event %"PRIu32".", revents);
b6b1849830f5e4a6065c3b0c993668e500c954d3Lennart Poettering log_warning("dispatch_fd_input called when uploading, ignoring.");
c19de71113f956809995fc68817e055e9f61f607Lennart Poettering return start_upload(u, fd_input_callback, u);
cd61c3bfd718fb398cc53ced906266a9297782c9Lennart Poetteringstatic int open_file_for_upload(Uploader *u, const char *filename) {
cd61c3bfd718fb398cc53ced906266a9297782c9Lennart Poettering fd = open(filename, O_RDONLY|O_CLOEXEC|O_NOCTTY);
b6b1849830f5e4a6065c3b0c993668e500c954d3Lennart Poettering log_error("Failed to open %s: %m", filename);
cd61c3bfd718fb398cc53ced906266a9297782c9Lennart Poettering r = sd_event_add_io(u->events, &u->input_event,
a1da85830bfaa77b9eb9c54693e5573559c97e50Tom Gundersen log_error("Failed to register input event: %s", strerror(-r));
8b0cc9a36c8f92f010f2e8465942d2cd7c580d78Lennart Poettering /* Normal files should just be consumed without polling. */
aa1936ea1a89c2bb968ba33e3274898a4eeae771Lennart Poettering r = start_upload(u, fd_input_callback, u);
aa1936ea1a89c2bb968ba33e3274898a4eeae771Lennart Poetteringstatic int dispatch_sigterm(sd_event_source *event,
6f883237f1b8a96ec0ea354866e033b6fcea9506Lennart Poettering sigset_add_many(&mask, SIGINT, SIGTERM, -1);
9d12709626bccc0cae677a7035f62efe6aabb4abLennart Poettering assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
aa1936ea1a89c2bb968ba33e3274898a4eeae771Lennart Poettering r = sd_event_add_signal(u->events, &u->sigterm_event, SIGTERM, dispatch_sigterm, u);
aa1936ea1a89c2bb968ba33e3274898a4eeae771Lennart Poettering r = sd_event_add_signal(u->events, &u->sigint_event, SIGINT, dispatch_sigterm, u);
878cd7e95ca303f9851d227a22d2022bd49944b0Lennart Poetteringstatic int setup_uploader(Uploader *u, const char *url, const char *state_file) {
878cd7e95ca303f9851d227a22d2022bd49944b0Lennart Poettering if (!startswith(url, "http://") && !startswith(url, "https://"))
878cd7e95ca303f9851d227a22d2022bd49944b0Lennart Poettering log_error("sd_event_default failed: %s", strerror(-r));
878cd7e95ca303f9851d227a22d2022bd49944b0Lennart Poettering log_error("Failed to set up signals: %s", strerror(-r));
878cd7e95ca303f9851d227a22d2022bd49944b0Lennart Poetteringstatic void destroy_uploader(Uploader *u) {
878cd7e95ca303f9851d227a22d2022bd49944b0Lennart Poettering u->input_event = sd_event_source_unref(u->input_event);
717603e391b52983ca1fd218e7333a1b9dfc5c05Lennart Poettering u->error[0] ? (int) sizeof(u->error) : INT_MAX,
717603e391b52983ca1fd218e7333a1b9dfc5c05Lennart Poettering u->error[0] ? u->error : curl_easy_strerror(code));
717603e391b52983ca1fd218e7333a1b9dfc5c05Lennart Poettering code = curl_easy_getinfo(u->easy, CURLINFO_RESPONSE_CODE, &status);
717603e391b52983ca1fd218e7333a1b9dfc5c05Lennart Poettering log_error("Failed to retrieve response code: %s",
717603e391b52983ca1fd218e7333a1b9dfc5c05Lennart Poettering log_error("Upload to %s failed with code %lu: %s",
717603e391b52983ca1fd218e7333a1b9dfc5c05Lennart Poettering log_error("Upload to %s finished with unexpected code %lu: %s",
717603e391b52983ca1fd218e7333a1b9dfc5c05Lennart Poettering log_debug("Upload finished successfully with code %lu: %s",
717603e391b52983ca1fd218e7333a1b9dfc5c05Lennart Poetteringstatic int parse_config(void) {
717603e391b52983ca1fd218e7333a1b9dfc5c05Lennart Poettering { "Upload", "URL", config_parse_string, 0, &arg_url },
717603e391b52983ca1fd218e7333a1b9dfc5c05Lennart Poettering { "Upload", "ServerKeyFile", config_parse_path, 0, &arg_key },
717603e391b52983ca1fd218e7333a1b9dfc5c05Lennart Poettering { "Upload", "ServerCertificateFile", config_parse_path, 0, &arg_cert },
717603e391b52983ca1fd218e7333a1b9dfc5c05Lennart Poettering { "Upload", "TrustedCertificateFile", config_parse_path, 0, &arg_trust },
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering return config_parse(NULL, PKGSYSCONFDIR "/journal-upload.conf", NULL,
89f7c8465cd1ab37347dd0c15920bce31e8225dfLennart Poettering false, false, true, NULL);
8b0cc9a36c8f92f010f2e8465942d2cd7c580d78Lennart Poetteringstatic void help(void) {
f48e75cb9a8112d35855c44a156934f2ee0edb2eLennart Poettering "Upload journal events to a remote server.\n\n"
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering " -h --help Show this help\n"
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering " --version Show package version\n"
e7e55dbdc38f929805ab2407fbd50886043a9e7cDavid Herrmann " -u --url=URL Upload to this address\n"
e7e55dbdc38f929805ab2407fbd50886043a9e7cDavid Herrmann " --key=FILENAME Specify key in PEM format (default:\n"
e7e55dbdc38f929805ab2407fbd50886043a9e7cDavid Herrmann " --cert=FILENAME Specify certificate in PEM format (default:\n"
e7e55dbdc38f929805ab2407fbd50886043a9e7cDavid Herrmann " --trust=FILENAME|all Specify CA certificate or disable checking (default:\n"
e7e55dbdc38f929805ab2407fbd50886043a9e7cDavid Herrmann " --system Use the system journal\n"
e7e55dbdc38f929805ab2407fbd50886043a9e7cDavid Herrmann " --user Use the user journal for the current user\n"
e7e55dbdc38f929805ab2407fbd50886043a9e7cDavid Herrmann " -m --merge Use all available journals\n"
e7e55dbdc38f929805ab2407fbd50886043a9e7cDavid Herrmann " -M --machine=CONTAINER Operate on local container\n"
e7e55dbdc38f929805ab2407fbd50886043a9e7cDavid Herrmann " -D --directory=PATH Use journal files from directory\n"
a1da85830bfaa77b9eb9c54693e5573559c97e50Tom Gundersen " --file=PATH Use this journal file\n"
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering " --cursor=CURSOR Start at the specified cursor\n"
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering " --after-cursor=CURSOR Start after the specified cursor\n"
f48e75cb9a8112d35855c44a156934f2ee0edb2eLennart Poettering " --follow[=BOOL] Do [not] wait for input\n"
f48e75cb9a8112d35855c44a156934f2ee0edb2eLennart Poettering " --save-state[=FILE] Save uploaded cursors (default \n"
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering " -h --help Show this help and exit\n"
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering " --version Print version string and exit\n"
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poetteringstatic int parse_argv(int argc, char *argv[]) {
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering { "version", no_argument, NULL, ARG_VERSION },
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering { "key", required_argument, NULL, ARG_KEY },
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering { "cert", required_argument, NULL, ARG_CERT },
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering { "trust", required_argument, NULL, ARG_TRUST },
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering { "system", no_argument, NULL, ARG_SYSTEM },
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering { "machine", required_argument, NULL, 'M' },
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering { "directory", required_argument, NULL, 'D' },
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering { "file", required_argument, NULL, ARG_FILE },
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering { "cursor", required_argument, NULL, ARG_CURSOR },
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering { "after-cursor", required_argument, NULL, ARG_AFTER_CURSOR },
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering { "follow", optional_argument, NULL, ARG_FOLLOW },
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering { "save-state", optional_argument, NULL, ARG_SAVE_STATE },
f48e75cb9a8112d35855c44a156934f2ee0edb2eLennart Poettering while ((c = getopt_long(argc, argv, "hu:mM:D:", options, NULL)) >= 0)
f48e75cb9a8112d35855c44a156934f2ee0edb2eLennart Poettering return 0 /* done */;
f48e75cb9a8112d35855c44a156934f2ee0edb2eLennart Poettering return 0 /* done */;
f48e75cb9a8112d35855c44a156934f2ee0edb2eLennart Poettering log_error("cannot use more than one --url");
717603e391b52983ca1fd218e7333a1b9dfc5c05Lennart Poettering log_error("cannot use more than one --key");
8b0cc9a36c8f92f010f2e8465942d2cd7c580d78Lennart Poettering log_error("cannot use more than one --cert");
8b0cc9a36c8f92f010f2e8465942d2cd7c580d78Lennart Poettering log_error("cannot use more than one --trust");
f48e75cb9a8112d35855c44a156934f2ee0edb2eLennart Poettering arg_journal_type |= SD_JOURNAL_CURRENT_USER;
f48e75cb9a8112d35855c44a156934f2ee0edb2eLennart Poettering log_error("cannot use more than one --machine/-M");
8b0cc9a36c8f92f010f2e8465942d2cd7c580d78Lennart Poettering log_error("cannot use more than one --directory/-D");
9f6eb1cd58f2ddf2eb6ba0e4de056e13d938af75Kay Sievers if (r < 0) {
9f6eb1cd58f2ddf2eb6ba0e4de056e13d938af75Kay Sievers log_error("Failed to add paths: %s", strerror(-r));
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering log_error("cannot use more than one --cursor/--after-cursor");
f647962d64e844689f3e2acfce6102fc47e76df2Michal Schmidt log_error("cannot use more than one --cursor/--after-cursor");
9f6eb1cd58f2ddf2eb6ba0e4de056e13d938af75Kay Sievers log_error("Failed to parse --follow= parameter.");
a7893c6b28772edbc7e1fea3c209caa54d465648Lennart Poettering log_error("Unknown option %s.", argv[optind-1]);
56159e0d918e9a9be07988133bb2847779325de0Lennart Poettering log_error("Missing argument to %s.", argv[optind-1]);
56159e0d918e9a9be07988133bb2847779325de0Lennart Poettering assert_not_reached("Unhandled option code.");
1ee306e1248866617c96ed9f4263f375588ad838Lennart Poettering log_error("Required --url/-u option missing.");
56159e0d918e9a9be07988133bb2847779325de0Lennart Poettering log_error("Options --key and --cert must be used together.");
fefdc04b38725457a91651218feb7000f6ccc1f4Lennart Poettering if (optind < argc && (arg_directory || arg_file || arg_machine || arg_journal_type)) {
8c841f21f5042b11acc91cc1b039cb162cbbe8f4Djalal Harouni log_error("Input arguments make no sense with journal input.");
a1da85830bfaa77b9eb9c54693e5573559c97e50Tom Gundersen r = sd_journal_open_directory(j, arg_directory, arg_journal_type);
a1da85830bfaa77b9eb9c54693e5573559c97e50Tom Gundersen r = sd_journal_open_files(j, (const char**) arg_file, 0);
56159e0d918e9a9be07988133bb2847779325de0Lennart Poettering r = sd_journal_open_container(j, arg_machine, 0);
a1da85830bfaa77b9eb9c54693e5573559c97e50Tom Gundersen r = sd_journal_open(j, !arg_merge*SD_JOURNAL_LOCAL_ONLY + arg_journal_type);
a1da85830bfaa77b9eb9c54693e5573559c97e50Tom Gundersen arg_directory ? arg_directory : arg_file ? "files" : "journal",
c19de71113f956809995fc68817e055e9f61f607Lennart Poettering r = setup_uploader(&u, arg_url, arg_save_state);
fefdc04b38725457a91651218feb7000f6ccc1f4Lennart Poettering "STATUS=Processing input...");
fefdc04b38725457a91651218feb7000f6ccc1f4Lennart Poettering while (true) {
fefdc04b38725457a91651218feb7000f6ccc1f4Lennart Poettering } else if (u.input < 0 && !use_journal) {
fefdc04b38725457a91651218feb7000f6ccc1f4Lennart Poettering log_debug("Using %s as input.", argv[optind]);
1fc464f6fbecfc5d8ba9f7b98d19e21fb324bfb9Lennart Poettering r = open_file_for_upload(&u, argv[optind++]);
c19de71113f956809995fc68817e055e9f61f607Lennart Poettering log_error("Failed to run event loop: %s", strerror(-r));
b6b1849830f5e4a6065c3b0c993668e500c954d3Lennart Poettering "STOPPING=1\n"
b6b1849830f5e4a6065c3b0c993668e500c954d3Lennart Poettering "STATUS=Shutting down...");
fefdc04b38725457a91651218feb7000f6ccc1f4Lennart Poettering return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;