1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Zbigniew Jędrzejewski-Szmek
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
23 #include <curl/curl.h>
28 #include "sd-daemon.h"
33 #include "journal-upload.h"
35 static const char* arg_url;
37 static void close_fd_input(Uploader *u);
39 static const char *arg_key = NULL;
40 static const char *arg_cert = NULL;
41 static const char *arg_trust = NULL;
43 #define easy_setopt(curl, opt, value, level, cmd) \
45 code = curl_easy_setopt(curl, opt, value); \
48 "curl_easy_setopt " #opt " failed: %s", \
49 curl_easy_strerror(code)); \
54 int start_upload(Uploader *u,
55 size_t (*input_callback)(void *ptr,
63 assert(input_callback);
68 h = curl_slist_append(NULL, "Content-Type: application/vnd.fdo.journal");
72 h = curl_slist_append(h, "Transfer-Encoding: chunked");
74 curl_slist_free_all(h);
78 h = curl_slist_append(h, "Accept: text/plain");
80 curl_slist_free_all(h);
90 curl = curl_easy_init();
92 log_error("Call to curl_easy_init failed.");
96 /* tell it to POST to the URL */
97 easy_setopt(curl, CURLOPT_POST, 1L,
98 LOG_ERR, return -EXFULL);
100 /* set where to read from */
101 easy_setopt(curl, CURLOPT_READFUNCTION, input_callback,
102 LOG_ERR, return -EXFULL);
104 easy_setopt(curl, CURLOPT_READDATA, data,
105 LOG_ERR, return -EXFULL);
107 /* use our special own mime type and chunked transfer */
108 easy_setopt(curl, CURLOPT_HTTPHEADER, u->header,
109 LOG_ERR, return -EXFULL);
111 /* enable verbose for easier tracing */
112 easy_setopt(curl, CURLOPT_VERBOSE, 1L, LOG_WARNING, );
114 easy_setopt(curl, CURLOPT_USERAGENT,
115 "systemd-journal-upload " PACKAGE_STRING,
121 easy_setopt(curl, CURLOPT_SSLKEY, arg_key,
122 LOG_ERR, return -EXFULL);
123 easy_setopt(curl, CURLOPT_SSLCERT, arg_cert,
124 LOG_ERR, return -EXFULL);
128 easy_setopt(curl, CURLOPT_CAINFO, arg_trust,
129 LOG_ERR, return -EXFULL);
131 if (arg_key || arg_trust)
132 easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1,
138 /* upload to this place */
139 code = curl_easy_setopt(u->easy, CURLOPT_URL, u->url);
141 log_error("curl_easy_setopt CURLOPT_URL failed: %s",
142 curl_easy_strerror(code));
151 static size_t fd_input_callback(void *buf, size_t size, size_t nmemb, void *userp) {
157 assert(nmemb <= SSIZE_MAX / size);
162 r = read(u->input, buf, size * nmemb);
163 log_debug("%s: allowed %zu, read %zu", __func__, size*nmemb, r);
168 u->uploading = false;
170 log_debug("Reached EOF");
174 log_error("Aborting transfer after read error on input: %m.");
175 return CURL_READFUNC_ABORT;
179 static void close_fd_input(Uploader *u) {
183 close_nointr(u->input);
187 static int dispatch_fd_input(sd_event_source *event,
194 assert(revents & EPOLLIN);
198 log_warning("dispatch_fd_input called when uploading, ignoring.");
202 return start_upload(u, fd_input_callback, u);
205 static int open_file_for_upload(Uploader *u, const char *filename) {
208 if (streq(filename, "-"))
211 fd = open(filename, O_RDONLY|O_CLOEXEC|O_NOCTTY);
213 log_error("Failed to open %s: %m", filename);
220 r = sd_event_add_io(u->events, &u->input_event,
221 fd, EPOLLIN, dispatch_fd_input, u);
224 log_error("Failed to register input event: %s", strerror(-r));
228 /* Normal files should just be consumed without polling. */
229 r = start_upload(u, fd_input_callback, u);
234 static int setup_uploader(Uploader *u, const char *url) {
240 memzero(u, sizeof(Uploader));
245 r = sd_event_default(&u->events);
247 log_error("sd_event_default failed: %s", strerror(-r));
254 static void destroy_uploader(Uploader *u) {
257 curl_easy_cleanup(u->easy);
258 curl_slist_free_all(u->header);
260 u->input_event = sd_event_source_unref(u->input_event);
264 sd_event_unref(u->events);
267 static void help(void) {
268 printf("%s -u URL {FILE|-}...\n\n"
269 "Upload journal events to a remote server.\n\n"
271 " --url=URL Upload to this address\n"
272 " --key=FILENAME Specify key in PEM format\n"
273 " --cert=FILENAME Specify certificate in PEM format\n"
274 " --trust=FILENAME Specify CA certificate in PEM format\n"
275 " -h --help Show this help and exit\n"
276 " --version Print version string and exit\n"
277 , program_invocation_short_name);
280 static int parse_argv(int argc, char *argv[]) {
288 static const struct option options[] = {
289 { "help", no_argument, NULL, 'h' },
290 { "version", no_argument, NULL, ARG_VERSION },
291 { "url", required_argument, NULL, 'u' },
292 { "key", required_argument, NULL, ARG_KEY },
293 { "cert", required_argument, NULL, ARG_CERT },
294 { "trust", required_argument, NULL, ARG_TRUST },
305 while ((c = getopt_long(argc, argv, "hu:", options, NULL)) >= 0)
312 puts(PACKAGE_STRING);
313 puts(SYSTEMD_FEATURES);
318 log_error("cannot use more than one --url");
327 log_error("cannot use more than one --key");
336 log_error("cannot use more than one --cert");
345 log_error("cannot use more than one --trust");
353 log_error("Unknown option %s.", argv[optind-1]);
357 log_error("Missing argument to %s.", argv[optind-1]);
361 assert_not_reached("Unhandled option code.");
365 log_error("Required --url/-u option missing.");
369 if (!!arg_key != !!arg_cert) {
370 log_error("Options --key and --cert must be used together.");
374 if (optind >= argc) {
375 log_error("Input argument missing.");
383 int main(int argc, char **argv) {
387 log_show_color(true);
388 log_parse_environment();
390 r = parse_argv(argc, argv);
394 r = setup_uploader(&u, arg_url);
398 log_debug("%s running as pid "PID_FMT,
399 program_invocation_short_name, getpid());
402 "STATUS=Processing input...");
409 log_debug("Using %s as input.", argv[optind]);
411 r = open_file_for_upload(&u, argv[optind++]);
417 r = sd_event_get_state(u.events);
420 if (r == SD_EVENT_FINISHED)
428 code = curl_easy_perform(u.easy);
430 log_error("Upload to %s failed: %s",
431 u.url, curl_easy_strerror(code));
435 log_debug("Upload finished successfully.");
438 r = sd_event_run(u.events, u.input >= 0 ? -1 : 0);
440 log_error("Failed to run event loop: %s", strerror(-r));
446 sd_notify(false, "STATUS=Shutting down...");
447 destroy_uploader(&u);
450 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;