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 #define easy_setopt(curl, opt, value, level, cmd) \
41 code = curl_easy_setopt(curl, opt, value); \
44 "curl_easy_setopt " #opt " failed: %s", \
45 curl_easy_strerror(code)); \
50 int start_upload(Uploader *u,
51 size_t (*input_callback)(void *ptr,
59 assert(input_callback);
64 h = curl_slist_append(NULL, "Content-Type: application/vnd.fdo.journal");
68 h = curl_slist_append(h, "Transfer-Encoding: chunked");
70 curl_slist_free_all(h);
74 h = curl_slist_append(h, "Accept: text/plain");
76 curl_slist_free_all(h);
86 curl = curl_easy_init();
88 log_error("Call to curl_easy_init failed.");
92 /* tell it to POST to the URL */
93 easy_setopt(curl, CURLOPT_POST, 1L,
94 LOG_ERR, return -EXFULL);
96 /* set where to read from */
97 easy_setopt(curl, CURLOPT_READFUNCTION, input_callback,
98 LOG_ERR, return -EXFULL);
100 easy_setopt(curl, CURLOPT_READDATA, data,
101 LOG_ERR, return -EXFULL);
103 /* use our special own mime type and chunked transfer */
104 easy_setopt(curl, CURLOPT_HTTPHEADER, u->header,
105 LOG_ERR, return -EXFULL);
107 /* enable verbose for easier tracing */
108 easy_setopt(curl, CURLOPT_VERBOSE, 1L, LOG_WARNING, );
110 easy_setopt(curl, CURLOPT_USERAGENT,
111 "systemd-journal-upload " PACKAGE_STRING,
117 /* upload to this place */
118 code = curl_easy_setopt(u->easy, CURLOPT_URL, u->url);
120 log_error("curl_easy_setopt CURLOPT_URL failed: %s",
121 curl_easy_strerror(code));
130 static size_t fd_input_callback(void *buf, size_t size, size_t nmemb, void *userp) {
136 assert(nmemb <= SSIZE_MAX / size);
141 r = read(u->input, buf, size * nmemb);
142 log_debug("%s: allowed %zu, read %zu", __func__, size*nmemb, r);
147 u->uploading = false;
149 log_debug("Reached EOF");
153 log_error("Aborting transfer after read error on input: %m.");
154 return CURL_READFUNC_ABORT;
158 static void close_fd_input(Uploader *u) {
162 close_nointr(u->input);
166 static int dispatch_fd_input(sd_event_source *event,
173 assert(revents & EPOLLIN);
177 log_warning("dispatch_fd_input called when uploading, ignoring.");
181 return start_upload(u, fd_input_callback, u);
184 static int open_file_for_upload(Uploader *u, const char *filename) {
187 if (streq(filename, "-"))
190 fd = open(filename, O_RDONLY|O_CLOEXEC|O_NOCTTY);
192 log_error("Failed to open %s: %m", filename);
199 r = sd_event_add_io(u->events, &u->input_event,
200 fd, EPOLLIN, dispatch_fd_input, u);
203 log_error("Failed to register input event: %s", strerror(-r));
207 /* Normal files should just be consumed without polling. */
208 r = start_upload(u, fd_input_callback, u);
213 static int setup_uploader(Uploader *u, const char *url) {
219 memzero(u, sizeof(Uploader));
224 r = sd_event_default(&u->events);
226 log_error("sd_event_default failed: %s", strerror(-r));
233 static void destroy_uploader(Uploader *u) {
236 curl_easy_cleanup(u->easy);
237 curl_slist_free_all(u->header);
239 u->input_event = sd_event_source_unref(u->input_event);
243 sd_event_unref(u->events);
246 static void help(void) {
247 printf("%s -u URL {FILE|-}...\n\n"
248 "Upload journal events to a remote server.\n\n"
250 " --url=URL Upload to this address\n"
251 " -h --help Show this help and exit\n"
252 " --version Print version string and exit\n"
253 , program_invocation_short_name);
256 static int parse_argv(int argc, char *argv[]) {
261 static const struct option options[] = {
262 { "help", no_argument, NULL, 'h' },
263 { "version", no_argument, NULL, ARG_VERSION },
264 { "url", required_argument, NULL, 'u' },
275 while ((c = getopt_long(argc, argv, "hu:", options, NULL)) >= 0)
282 puts(PACKAGE_STRING);
283 puts(SYSTEMD_FEATURES);
288 log_error("cannot use more than one --url");
296 log_error("Unknown option %s.", argv[optind-1]);
300 log_error("Missing argument to %s.", argv[optind-1]);
304 assert_not_reached("Unhandled option code.");
308 log_error("Required --url/-u option missing.");
312 if (optind >= argc) {
313 log_error("Input argument missing.");
321 int main(int argc, char **argv) {
325 log_show_color(true);
326 log_parse_environment();
328 r = parse_argv(argc, argv);
332 r = setup_uploader(&u, arg_url);
336 log_debug("%s running as pid "PID_FMT,
337 program_invocation_short_name, getpid());
340 "STATUS=Processing input...");
347 log_debug("Using %s as input.", argv[optind]);
349 r = open_file_for_upload(&u, argv[optind++]);
355 r = sd_event_get_state(u.events);
358 if (r == SD_EVENT_FINISHED)
366 code = curl_easy_perform(u.easy);
368 log_error("Upload to %s failed: %s",
369 u.url, curl_easy_strerror(code));
373 log_debug("Upload finished successfully.");
376 r = sd_event_run(u.events, u.input >= 0 ? -1 : 0);
378 log_error("Failed to run event loop: %s", strerror(-r));
384 sd_notify(false, "STATUS=Shutting down...");
385 destroy_uploader(&u);
388 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;