Merge lp:~sergei.glushchenko/percona-xtrabackup/xbcloud into lp:percona-xtrabackup/2.2

Proposed by Sergei Glushchenko
Status: Rejected
Rejected by: Alexey Kopytov
Proposed branch: lp:~sergei.glushchenko/percona-xtrabackup/xbcloud
Merge into: lp:percona-xtrabackup/2.2
Diff against target: 1471 lines (+1373/-1)
9 files modified
cmake/FindLibEv.cmake (+49/-0)
cmake/curl.cmake (+26/-0)
cmake/libev.cmake (+26/-0)
storage/innobase/xtrabackup/src/CMakeLists.txt (+24/-0)
storage/innobase/xtrabackup/src/xbcloud.cc (+1173/-0)
storage/innobase/xtrabackup/test/t/xbcloud.sh (+69/-0)
storage/innobase/xtrabackup/utils/debian/control (+2/-0)
storage/innobase/xtrabackup/utils/debian/rules (+1/-0)
storage/innobase/xtrabackup/utils/percona-xtrabackup.spec (+3/-1)
To merge this branch: bzr merge lp:~sergei.glushchenko/percona-xtrabackup/xbcloud
Reviewer Review Type Date Requested Status
Alexey Kopytov (community) Needs Resubmitting
Review via email: mp+239220@code.launchpad.net

Description of the change

To post a comment you must log in.
Revision history for this message
Sergei Glushchenko (sergei.glushchenko) wrote :
Revision history for this message
Alexey Kopytov (akopytov) wrote :

Will resubmit for PXB 2.3.

review: Needs Resubmitting

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'cmake/FindLibEv.cmake'
2--- cmake/FindLibEv.cmake 1970-01-01 00:00:00 +0000
3+++ cmake/FindLibEv.cmake 2014-10-23 18:15:30 +0000
4@@ -0,0 +1,49 @@
5+# Copyright (c) 2014 Percona LLC and/or its affiliates
6+#
7+# This program is free software; you can redistribute it and/or modify
8+# it under the terms of the GNU General Public License as published by
9+# the Free Software Foundation; version 2 of the License.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program; if not, write to the Free Software
18+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19+
20+# This module defines
21+# LIBEV_INCLUDE_DIRS, where to find LibEv headers
22+# LIBEV_LIBRARIES, LibEv libraries
23+# LIBEV_FOUND, If false, do not try to use ant
24+
25+find_path(LIBEV_INCLUDE_DIRS ev.h PATHS
26+ /usr/include
27+ /usr/local/include
28+ /opt/local/include
29+ /usr/include/libev
30+ )
31+
32+set(LIBEV_LIB_PATHS /usr/lib /usr/local/lib /opt/local/lib)
33+find_library(LIBEV_LIB NAMES ev PATHS ${LIBEV_LIB_PATHS})
34+
35+if (LIBEV_LIB AND LIBEV_INCLUDE_DIRS)
36+ set(LIBEV_FOUND TRUE)
37+ set(LIBEV_LIBRARIES ${LIBEV_LIB})
38+else ()
39+ set(LIBEV_FOUND FALSE)
40+endif ()
41+
42+if (LIBEV_FOUND)
43+ if (NOT LIBEV_FIND_QUIETLY)
44+ message(STATUS "Found libev: ${LIBEV_LIBRARIES}")
45+ endif ()
46+else ()
47+ message(STATUS "libev NOT found.")
48+endif ()
49+
50+mark_as_advanced(
51+ LIBEV_LIB
52+ LIBEV_INCLUDE_DIRS
53+ )
54
55=== added file 'cmake/curl.cmake'
56--- cmake/curl.cmake 1970-01-01 00:00:00 +0000
57+++ cmake/curl.cmake 2014-10-23 18:15:30 +0000
58@@ -0,0 +1,26 @@
59+# Copyright (c) 2014 Percona LLC and/or its affiliates
60+#
61+# This program is free software; you can redistribute it and/or modify
62+# it under the terms of the GNU General Public License as published by
63+# the Free Software Foundation; version 2 of the License.
64+#
65+# This program is distributed in the hope that it will be useful,
66+# but WITHOUT ANY WARRANTY; without even the implied warranty of
67+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
68+# GNU General Public License for more details.
69+#
70+# You should have received a copy of the GNU General Public License
71+# along with this program; if not, write to the Free Software
72+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
73+
74+MACRO(FIND_CURL)
75+
76+FIND_PACKAGE(CURL)
77+IF(CURL_FOUND)
78+ MESSAGE(STATUS "CURL libraries found at: ${CURL_LIBRARIES}")
79+ MESSAGE(STATUS "CURL includes found at: ${CURL_INCLUDE_DIRS}")
80+ELSE()
81+ MESSAGE(SEND_ERROR "Could not find cURL on your system")
82+ENDIF(CURL_FOUND)
83+
84+ENDMACRO()
85
86=== added file 'cmake/libev.cmake'
87--- cmake/libev.cmake 1970-01-01 00:00:00 +0000
88+++ cmake/libev.cmake 2014-10-23 18:15:30 +0000
89@@ -0,0 +1,26 @@
90+# Copyright (c) 2014 Percona LLC and/or its affiliates
91+#
92+# This program is free software; you can redistribute it and/or modify
93+# it under the terms of the GNU General Public License as published by
94+# the Free Software Foundation; version 2 of the License.
95+#
96+# This program is distributed in the hope that it will be useful,
97+# but WITHOUT ANY WARRANTY; without even the implied warranty of
98+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
99+# GNU General Public License for more details.
100+#
101+# You should have received a copy of the GNU General Public License
102+# along with this program; if not, write to the Free Software
103+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
104+
105+MACRO(FIND_EV)
106+
107+FIND_PACKAGE(LibEv)
108+IF(LIBEV_FOUND)
109+ MESSAGE(STATUS "libev libraries found at: ${LIBEV_LIBRARIES}")
110+ MESSAGE(STATUS "libev includes found at: ${LIBEV_INCLUDE_DIRS}")
111+ELSE()
112+ MESSAGE(SEND_ERROR "Could not find libev on your system")
113+ENDIF(LIBEV_FOUND)
114+
115+ENDMACRO()
116
117=== modified file 'storage/innobase/xtrabackup/src/CMakeLists.txt'
118--- storage/innobase/xtrabackup/src/CMakeLists.txt 2014-09-20 17:05:56 +0000
119+++ storage/innobase/xtrabackup/src/CMakeLists.txt 2014-10-23 18:15:30 +0000
120@@ -14,10 +14,14 @@
121 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
122
123 INCLUDE(gcrypt)
124+INCLUDE(curl)
125+INCLUDE(libev)
126
127 ADD_SUBDIRECTORY(libarchive)
128
129 FIND_GCRYPT()
130+FIND_CURL()
131+FIND_EV()
132
133 INCLUDE_DIRECTORIES(
134 ${CMAKE_SOURCE_DIR}/include
135@@ -26,6 +30,8 @@
136 ${CMAKE_SOURCE_DIR}/storage/innobase/xtrabackup/src/libarchive/libarchive
137 ${CMAKE_SOURCE_DIR}/storage/innobase/xtrabackup/src/quicklz
138 ${GCRYPT_INCLUDE_DIR}
139+ ${CURL_INCLUDE_DIRS}
140+ ${LIBEV_INCLUDE_DIRS}
141 )
142
143 ########################################################################
144@@ -107,3 +113,21 @@
145 mysys
146 mysys_ssl
147 )
148+
149+########################################################################
150+# xbcloud binary
151+########################################################################
152+MYSQL_ADD_EXECUTABLE(xbcloud
153+ xbcloud.cc
154+ )
155+
156+SET_TARGET_PROPERTIES(xbcloud
157+ PROPERTIES LINKER_LANGUAGE CXX
158+ )
159+
160+TARGET_LINK_LIBRARIES(xbcloud
161+ ${GCRYPT_LIBS}
162+ ${LIBEV_LIBRARIES}
163+ ${CURL_LIBRARIES}
164+ mysys
165+ )
166
167=== added file 'storage/innobase/xtrabackup/src/xbcloud.cc'
168--- storage/innobase/xtrabackup/src/xbcloud.cc 1970-01-01 00:00:00 +0000
169+++ storage/innobase/xtrabackup/src/xbcloud.cc 2014-10-23 18:15:30 +0000
170@@ -0,0 +1,1173 @@
171+/******************************************************
172+Copyright (c) 2014 Percona LLC and/or its affiliates.
173+
174+The xbstream utility: serialize/deserialize files in the XBSTREAM format.
175+
176+This program is free software; you can redistribute it and/or modify
177+it under the terms of the GNU General Public License as published by
178+the Free Software Foundation; version 2 of the License.
179+
180+This program is distributed in the hope that it will be useful,
181+but WITHOUT ANY WARRANTY; without even the implied warranty of
182+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
183+GNU General Public License for more details.
184+
185+You should have received a copy of the GNU General Public License
186+along with this program; if not, write to the Free Software
187+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
188+
189+*******************************************************/
190+
191+#include <stdio.h>
192+#include <stdlib.h>
193+#include <string.h>
194+#include <curl/curl.h>
195+#include <ev.h>
196+#include <unistd.h>
197+#include <errno.h>
198+#include <getopt.h>
199+#include <gcrypt.h>
200+#include <assert.h>
201+#include <algorithm>
202+#include <my_global.h>
203+#include <my_sys.h>
204+
205+using std::min;
206+using std::max;
207+
208+#define SWIFT_MAX_URL_SIZE 2048
209+#define SWIFT_MAX_HDR_SIZE 2048
210+
211+#define SWIFT_CHUNK_SIZE 10485760
212+
213+/*****************************************************************************/
214+
215+typedef struct swift_auth_info_struct swift_auth_info;
216+typedef struct connection_info_struct connection_info;
217+typedef struct socket_info_struct socket_info;
218+typedef struct global_io_info_struct global_io_info;
219+typedef struct slo_chunk_struct slo_chunk;
220+typedef struct slo_manifest_struct slo_manifest;
221+
222+struct swift_auth_info_struct {
223+ char url[SWIFT_MAX_URL_SIZE];
224+ char token[SWIFT_MAX_HDR_SIZE];
225+};
226+
227+struct global_io_info_struct {
228+ struct ev_loop *loop;
229+ struct ev_io input_event;
230+ struct ev_timer timer_event;
231+ CURLM *multi;
232+ int still_running;
233+ int eof;
234+ curl_socket_t input_fd;
235+ connection_info **connections;
236+ long chunk_no;
237+ connection_info *current_connection;
238+ slo_manifest *manifest;
239+};
240+
241+struct socket_info_struct {
242+ curl_socket_t sockfd;
243+ CURL *easy;
244+ int action;
245+ long timeout;
246+ struct ev_io ev;
247+ int evset;
248+ global_io_info *global;
249+};
250+
251+struct connection_info_struct {
252+ CURL *easy;
253+ global_io_info *global;
254+ char *buffer;
255+ size_t buffer_size;
256+ size_t filled_size;
257+ size_t upload_size;
258+ size_t chunk_uploaded;
259+ char error[CURL_ERROR_SIZE];
260+ struct curl_slist *slist;
261+ char *url;
262+ char *container;
263+ char *token;
264+ char *name;
265+ gcry_md_hd_t md5;
266+ char hash[33];
267+ size_t chunk_no;
268+};
269+
270+struct slo_chunk_struct {
271+ char name[SWIFT_MAX_URL_SIZE];
272+ char md5[33];
273+ int idx;
274+ size_t size;
275+};
276+
277+struct slo_manifest_struct {
278+ DYNAMIC_ARRAY chunks;
279+ size_t uploaded_idx;
280+};
281+
282+static int opt_verbose;
283+static enum {S3, SWIFT} opt_storage = SWIFT;
284+static const char *opt_user = NULL;
285+static const char *opt_container = NULL;
286+static const char *opt_url = NULL;
287+static const char *opt_key = NULL;
288+static const char *opt_name = NULL;
289+static const char *opt_cacert = NULL;
290+static int opt_parallel = 1;
291+static int opt_insecure = 0;
292+static enum {MODE_GET, MODE_PUT} opt_mode;
293+
294+static
295+slo_manifest *slo_manifest_init()
296+{
297+ slo_manifest *manifest = (slo_manifest *)(calloc(1, sizeof(slo_manifest)));
298+ if (manifest != NULL) {
299+ my_init_dynamic_array(&manifest->chunks, sizeof(slo_chunk), 50, 50);
300+ }
301+ return manifest;
302+}
303+
304+static
305+int slo_add_chunk(slo_manifest *manifest, const slo_chunk *chunk)
306+{
307+ insert_dynamic(&manifest->chunks, chunk);
308+ return 0;
309+}
310+
311+static
312+void slo_manifest_free(slo_manifest *manifest)
313+{
314+ delete_dynamic(&manifest->chunks);
315+ free(manifest);
316+}
317+
318+static
319+void usage()
320+{
321+ fprintf(stderr, "usage:\n"
322+ " xbcloud [options] put/get <name>\n");
323+}
324+
325+static
326+int parse_args(int argc, char **argv)
327+{
328+ const char *command;
329+ int c;
330+
331+ if (argc < 2) {
332+ fprintf(stderr, "Command isn't specified. "
333+ "Supported commands are put and get\n");
334+ return 1;
335+ }
336+
337+ command = argv[1];
338+ argc--; argv++;
339+
340+ if (strcmp(command, "put") == 0) {
341+ opt_mode = MODE_PUT;
342+ } else if (strcmp(command, "get") == 0) {
343+ opt_mode = MODE_GET;
344+ } else {
345+ fprintf(stderr, "Unknown command %s. "
346+ "Supported commands are put and get\n", command);
347+ return 1;
348+ }
349+
350+ while (1) {
351+ enum {
352+ OPT_HELP = 10,
353+ OPT_STORAGE,
354+ OPT_SWIFT_CONTAINER,
355+ OPT_SWIFT_URL,
356+ OPT_SWIFT_KEY,
357+ OPT_SWIFT_USER,
358+ OPT_PARALLEL,
359+ OPT_CACERT,
360+ OPT_INSECURE,
361+ OPT_VERBOSE
362+ };
363+ static struct option long_options[] =
364+ {
365+ {"verbose", no_argument, &opt_verbose, OPT_VERBOSE},
366+ {"help", no_argument, 0, OPT_HELP},
367+ {"storage", required_argument, 0, OPT_STORAGE},
368+ {"swift-container", required_argument, 0, OPT_SWIFT_CONTAINER},
369+ {"swift-user", required_argument, 0, OPT_SWIFT_USER},
370+ {"swift-url", required_argument, 0, OPT_SWIFT_URL},
371+ {"swift-key", required_argument, 0, OPT_SWIFT_KEY},
372+ {"parallel", required_argument, 0, OPT_PARALLEL},
373+ {"cacert", required_argument, 0, OPT_CACERT},
374+ {"insecure", no_argument, &opt_insecure, OPT_INSECURE},
375+ {NULL, 0, 0, 0}
376+ };
377+
378+ int option_index = 0;
379+
380+ c = getopt_long(argc, argv, "s", long_options, &option_index);
381+
382+ if (c == -1)
383+ break;
384+
385+ switch (c) {
386+ case 0:
387+ break;
388+ case OPT_STORAGE:
389+ if (strcmp(optarg, "Swift") == 0) {
390+ opt_storage = SWIFT;
391+ } else if (strcmp(optarg, "S3") == 0) {
392+ opt_storage = S3;
393+ } else {
394+ fprintf(stderr, "unknown storage %s "
395+ "(allowed are Swift and S3)\n", optarg);
396+ return 1;
397+ }
398+ break;
399+ case OPT_SWIFT_CONTAINER:
400+ opt_container = optarg;
401+ break;
402+ case OPT_SWIFT_USER:
403+ opt_user = optarg;
404+ break;
405+ case OPT_SWIFT_URL:
406+ opt_url = optarg;
407+ break;
408+ case OPT_SWIFT_KEY:
409+ opt_key = optarg;
410+ break;
411+ case OPT_PARALLEL:
412+ opt_parallel = atoi(optarg);
413+ if (opt_parallel < 1) {
414+ fprintf(stderr, "wrong value for parallel option %s\n", optarg);
415+ return 1;
416+ }
417+ break;
418+ case OPT_HELP:
419+ usage();
420+ return 1;
421+ case OPT_CACERT:
422+ opt_cacert = optarg;
423+ break;
424+ default:
425+ return 1;
426+ }
427+ }
428+
429+ /* make sure name is specified */
430+ while (optind < argc)
431+ opt_name = argv[optind++];
432+ if (opt_name == NULL)
433+ {
434+ fprintf(stderr, "object name is required argument\n");
435+ return 1;
436+ }
437+
438+ /* validate arguments */
439+ if (opt_storage == SWIFT) {
440+ if (opt_user == NULL) {
441+ fprintf(stderr, "user for Swift is not specified\n");
442+ return 1;
443+ }
444+ if (opt_container == NULL) {
445+ fprintf(stderr, "container for Swift is not specified\n");
446+ return 1;
447+ }
448+ if (opt_url == NULL) {
449+ fprintf(stderr, "URL for Swift is not specified\n");
450+ return 1;
451+ }
452+ if (opt_key == NULL) {
453+ fprintf(stderr, "key for Swift is not specified\n");
454+ return 1;
455+ }
456+ }
457+
458+ return 0;
459+}
460+
461+static char *hex_md5(const unsigned char *hash, char *out)
462+{
463+ enum { hash_len = 16 };
464+ char *p;
465+ int i;
466+
467+ for (i = 0, p = out; i < hash_len; i++, p+=2) {
468+ sprintf(p, "%02x", hash[i]);
469+ }
470+
471+ return out;
472+}
473+
474+/* If header starts with prefix it's value will be copied into output buffer */
475+static
476+int get_http_header(const char *prefix, const char *buffer,
477+ char *out, size_t out_size)
478+{
479+ const char *beg, *end;
480+ size_t len, prefix_len;
481+
482+ prefix_len = strlen(prefix);
483+
484+ if (strncmp(buffer, prefix, prefix_len) == 0) {
485+ beg = buffer + prefix_len;
486+ end = strchr(beg, '\r');
487+
488+ len = min<size_t>(end - beg, out_size - 1);
489+
490+ strncpy(out, beg, len);
491+
492+ out[len] = 0;
493+
494+ return 1;
495+ }
496+
497+ return 0;
498+}
499+
500+static
501+size_t swift_auth_header_read_cb(void *ptr, size_t size, size_t nmemb,
502+ void *data)
503+{
504+ swift_auth_info *info = (swift_auth_info*)(data);
505+ const char *buffer = (const char *)(ptr);
506+
507+ get_http_header("X-Storage-Url: ", buffer,
508+ info->url, array_elements(info->url));
509+ get_http_header("X-Auth-Token: ", buffer,
510+ info->token, array_elements(info->token));
511+
512+ return nmemb * size;
513+}
514+
515+static
516+int
517+swift_auth(const char *auth_url, const char *username, const char *key,
518+ swift_auth_info *info)
519+{
520+ CURL *curl;
521+ CURLcode res;
522+ long http_code;
523+ char *hdr_buf = NULL;
524+ struct curl_slist *slist = NULL;
525+
526+ curl = curl_easy_init();
527+
528+ if (curl != NULL) {
529+
530+ hdr_buf = (char *)(calloc(14 + max(strlen(username), strlen(key)), 1));
531+
532+ if (!hdr_buf) {
533+ res = CURLE_FAILED_INIT;
534+ goto cleanup;
535+ }
536+
537+ sprintf(hdr_buf, "X-Auth-User: %s", username);
538+ slist = curl_slist_append(slist, hdr_buf);
539+
540+ sprintf(hdr_buf, "X-Auth-Key: %s", key);
541+ slist = curl_slist_append(slist, hdr_buf);
542+
543+ curl_easy_setopt(curl, CURLOPT_VERBOSE, opt_verbose);
544+ curl_easy_setopt(curl, CURLOPT_URL, auth_url);
545+ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
546+ curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION,
547+ swift_auth_header_read_cb);
548+ curl_easy_setopt(curl, CURLOPT_HEADERDATA, info);
549+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
550+ if (opt_cacert != NULL)
551+ curl_easy_setopt(curl, CURLOPT_CAINFO, opt_cacert);
552+ if (opt_insecure)
553+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE);
554+
555+ res = curl_easy_perform(curl);
556+
557+ if (res != CURLE_OK) {
558+ fprintf(stderr, "error: authentication failed: "
559+ "curl_easy_perform(): %s\n",
560+ curl_easy_strerror(res));
561+ goto cleanup;
562+ }
563+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
564+ if (http_code != 200) {
565+ fprintf(stderr, "error: authentication failed "
566+ "with response code: %ld\n", http_code);
567+ res = CURLE_LOGIN_DENIED;
568+ goto cleanup;
569+ }
570+ } else {
571+ res = CURLE_FAILED_INIT;
572+ fprintf(stderr, "error: curl_easy_init() failed\n");
573+ goto cleanup;
574+ }
575+
576+cleanup:
577+ if (hdr_buf)
578+ free(hdr_buf);
579+ if (slist)
580+ curl_slist_free_all(slist);
581+ if (curl)
582+ curl_easy_cleanup(curl);
583+
584+ return res;
585+}
586+
587+static
588+size_t
589+write_null_cb(void *buffer, size_t size, size_t nmemb, void *stream) {
590+ return nmemb * size;
591+}
592+
593+static
594+int
595+swift_create_container(swift_auth_info *info, const char *name)
596+{
597+ char url[SWIFT_MAX_URL_SIZE];
598+ char auth_token[SWIFT_MAX_HDR_SIZE];
599+ CURLcode res;
600+ long http_code;
601+ CURL *curl;
602+ struct curl_slist *slist = NULL;
603+
604+ snprintf(url, array_elements(url), "%s/%s", info->url, name);
605+ snprintf(auth_token, array_elements(auth_token), "X-Auth-Token: %s",
606+ info->token);
607+
608+ curl = curl_easy_init();
609+
610+ if (curl != NULL) {
611+ slist = curl_slist_append(slist, auth_token);
612+
613+ curl_easy_setopt(curl, CURLOPT_VERBOSE, opt_verbose);
614+ curl_easy_setopt(curl, CURLOPT_URL, url);
615+ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
616+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
617+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_null_cb);
618+ curl_easy_setopt(curl, CURLOPT_PUT, 1L);
619+ if (opt_cacert != NULL)
620+ curl_easy_setopt(curl, CURLOPT_CAINFO, opt_cacert);
621+ if (opt_insecure)
622+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE);
623+
624+ res = curl_easy_perform(curl);
625+
626+ if (res != CURLE_OK) {
627+ fprintf(stderr, "error: curl_easy_perform() failed: %s\n",
628+ curl_easy_strerror(res));
629+ goto cleanup;
630+ }
631+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
632+ if (http_code != 201 && /* created */
633+ http_code != 202 /* accepted (already exists) */) {
634+ fprintf(stderr, "error: request failed "
635+ "with response code: %ld\n", http_code);
636+ res = CURLE_LOGIN_DENIED;
637+ goto cleanup;
638+ }
639+ } else {
640+ res = CURLE_FAILED_INIT;
641+ fprintf(stderr, "error: curl_easy_init() failed\n");
642+ goto cleanup;
643+ }
644+
645+cleanup:
646+ if (slist)
647+ curl_slist_free_all(slist);
648+ if (curl)
649+ curl_easy_cleanup(curl);
650+
651+ return res;
652+}
653+
654+/* Check for completed transfers, and remove their easy handles */
655+static void check_multi_info(global_io_info *g)
656+{
657+ char *eff_url;
658+ CURLMsg *msg;
659+ int msgs_left;
660+ connection_info *conn;
661+ CURL *easy;
662+
663+ while ((msg = curl_multi_info_read(g->multi, &msgs_left))) {
664+ if (msg->msg == CURLMSG_DONE) {
665+ easy = msg->easy_handle;
666+ curl_easy_getinfo(easy, CURLINFO_PRIVATE, &conn);
667+ curl_easy_getinfo(easy, CURLINFO_EFFECTIVE_URL, &eff_url);
668+ curl_multi_remove_handle(g->multi, easy);
669+ curl_easy_cleanup(easy);
670+ }
671+ }
672+}
673+
674+/* Die if we get a bad CURLMcode somewhere */
675+static void mcode_or_die(const char *where, CURLMcode code)
676+{
677+ if (code != CURLM_OK)
678+ {
679+ const char *s;
680+ switch (code)
681+ {
682+ case CURLM_BAD_HANDLE: s = "CURLM_BAD_HANDLE"; break;
683+ case CURLM_BAD_EASY_HANDLE: s = "CURLM_BAD_EASY_HANDLE"; break;
684+ case CURLM_OUT_OF_MEMORY: s = "CURLM_OUT_OF_MEMORY"; break;
685+ case CURLM_INTERNAL_ERROR: s = "CURLM_INTERNAL_ERROR"; break;
686+ case CURLM_UNKNOWN_OPTION: s = "CURLM_UNKNOWN_OPTION"; break;
687+ case CURLM_LAST: s = "CURLM_LAST"; break;
688+ default: s = "CURLM_unknown"; break;
689+ case CURLM_BAD_SOCKET: s = "CURLM_BAD_SOCKET";
690+ fprintf(stderr, "error: %s returns %s\n", where, s);
691+ /* ignore this error */
692+ return;
693+ }
694+ fprintf(stderr, "error: %s returns %s\n", where, s);
695+ assert(0);
696+ }
697+}
698+
699+/* Called by libev when we get action on a multi socket */
700+static void event_cb(EV_P_ struct ev_io *w, int revents)
701+{
702+ global_io_info *global = (global_io_info*)(w->data);
703+ CURLMcode rc;
704+
705+#if ((LIBCURL_VERSION_MAJOR >= 7) && (LIBCURL_VERSION_MINOR >= 16))
706+ int action = (revents & EV_READ ? CURL_POLL_IN : 0) |
707+ (revents & EV_WRITE ? CURL_POLL_OUT : 0);
708+
709+ rc = curl_multi_socket_action(global->multi, w->fd, action,
710+ &global->still_running);
711+#else
712+ do {
713+ rc = curl_multi_socket(global->multi, w->fd, &global->still_running);
714+ } while (rc == CURLM_CALL_MULTI_PERFORM);
715+#endif
716+ mcode_or_die("event_cb: curl_multi_socket_action", rc);
717+ check_multi_info(global);
718+ if (global->still_running <= 0) {
719+ ev_timer_stop(global->loop, &global->timer_event);
720+ }
721+}
722+
723+static void remsock(socket_info *fdp, global_io_info *global)
724+{
725+ if (fdp) {
726+ if (fdp->evset)
727+ ev_io_stop(global->loop, &fdp->ev);
728+ free(fdp);
729+ }
730+}
731+
732+static void setsock(socket_info *fdp, curl_socket_t s, CURL *easy, int action,
733+ global_io_info *global)
734+{
735+ int kind = (action & CURL_POLL_IN ? (int)(EV_READ) : 0) |
736+ (action & CURL_POLL_OUT ? (int)(EV_WRITE) : 0);
737+
738+ fdp->sockfd = s;
739+ fdp->action = action;
740+ fdp->easy = easy;
741+ if (fdp->evset)
742+ ev_io_stop(global->loop, &fdp->ev);
743+ ev_io_init(&fdp->ev, event_cb, fdp->sockfd, kind);
744+ fdp->ev.data = global;
745+ fdp->evset = 1;
746+ ev_io_start(global->loop, &fdp->ev);
747+}
748+
749+static void addsock(curl_socket_t s, CURL *easy, int action,
750+ global_io_info *global)
751+{
752+ socket_info *fdp = (socket_info *)(calloc(sizeof(socket_info), 1));
753+
754+ fdp->global = global;
755+ setsock(fdp, s, easy, action, global);
756+ curl_multi_assign(global->multi, s, fdp);
757+}
758+
759+static int sock_cb(CURL *easy, curl_socket_t s, int what, void *cbp,
760+ void *sockp)
761+{
762+ global_io_info *global = (global_io_info*)(cbp);
763+ socket_info *fdp = (socket_info*)(sockp);
764+
765+ if (what == CURL_POLL_REMOVE) {
766+ remsock(fdp, global);
767+ } else {
768+ if (!fdp) {
769+ addsock(s, easy, what, global);
770+ } else {
771+ setsock(fdp, s, easy, what, global);
772+ }
773+ }
774+ return 0;
775+}
776+
777+/* Called by libev when our timeout expires */
778+static void timer_cb(EV_P_ struct ev_timer *w, int revents)
779+{
780+ global_io_info *io_global = (global_io_info*)(w->data);
781+ CURLMcode rc;
782+
783+#if ((LIBCURL_VERSION_MAJOR >= 7) && (LIBCURL_VERSION_MINOR >= 16))
784+ rc = curl_multi_socket_action(io_global->multi, CURL_SOCKET_TIMEOUT, 0,
785+ &io_global->still_running);
786+#else
787+ do {
788+ rc = curl_multi_socket_all(io_global->multi, &io_global->still_running);
789+ } while (rc == CURLM_CALL_MULTI_PERFORM);
790+#endif
791+ mcode_or_die("timer_cb: curl_multi_socket_action", rc);
792+ check_multi_info(io_global);
793+}
794+
795+static int conn_upload_init(connection_info *conn);
796+
797+static connection_info *get_current_connection(global_io_info *global)
798+{
799+ connection_info *conn = global->current_connection;
800+ int i;
801+
802+ if (conn && conn->filled_size < conn->buffer_size)
803+ return conn;
804+
805+ for (i = 0; i < opt_parallel; i++) {
806+ conn = global->connections[i];
807+ if (conn->filled_size == 0 || conn->chunk_uploaded) {
808+ global->current_connection = conn;
809+ conn_upload_init(conn);
810+ return conn;
811+ }
812+ }
813+
814+ return NULL;
815+}
816+
817+/* This gets called whenever data is received from the input */
818+static void input_cb(EV_P_ struct ev_io *w, int revents)
819+{
820+ global_io_info *io_global = (global_io_info *)(w->data);
821+ connection_info *conn = get_current_connection(io_global);
822+
823+ if (conn == NULL)
824+ return;
825+
826+ if (conn->filled_size < conn->buffer_size) {
827+ if (revents & EV_READ) {
828+ ssize_t nbytes = read(io_global->input_fd,
829+ conn->buffer + conn->filled_size,
830+ conn->buffer_size - conn->filled_size);
831+ if (nbytes > 0) {
832+ gcry_md_write(conn->md5,
833+ conn->buffer + conn->filled_size, nbytes);
834+ conn->filled_size += nbytes;
835+ } else if (nbytes < 0) {
836+ if (errno != EAGAIN && errno != EINTR) {
837+ /* failed to read input */
838+ exit(1);
839+ }
840+ } else {
841+ io_global->eof = 1;
842+ ev_io_stop(io_global->loop, w);
843+ }
844+ }
845+ }
846+
847+ assert(conn->filled_size <= conn->buffer_size);
848+}
849+
850+static int swift_upload_read_cb(void *ptr, size_t size, size_t nmemb, void *data)
851+{
852+ size_t realsize;
853+
854+ connection_info *conn = (connection_info*)(data);
855+
856+ if (conn->filled_size == conn->upload_size &&
857+ conn->upload_size < conn->buffer_size && !conn->global->eof) {
858+ ssize_t nbytes;
859+ assert(conn->global->current_connection == conn);
860+ do {
861+ nbytes = read(conn->global->input_fd,
862+ conn->buffer + conn->filled_size,
863+ conn->buffer_size - conn->filled_size);
864+ } while (nbytes == -1 && errno == EAGAIN);
865+ if (nbytes > 0) {
866+ gcry_md_write(conn->md5, conn->buffer + conn->filled_size, nbytes);
867+ conn->filled_size += nbytes;
868+ } else {
869+ conn->global->eof = 1;
870+ }
871+ }
872+
873+ realsize = min(size * nmemb, conn->filled_size - conn->upload_size);
874+
875+ memcpy(ptr, conn->buffer + conn->upload_size, realsize);
876+ conn->upload_size += realsize;
877+
878+ if (conn->upload_size == conn->buffer_size ||
879+ (conn->global->eof && conn->upload_size == conn->filled_size)) {
880+ if (!conn->chunk_uploaded && realsize == 0) {
881+ slo_chunk chunk;
882+
883+ hex_md5(gcry_md_read(conn->md5, GCRY_MD_MD5), conn->hash);
884+ gcry_md_reset(conn->md5);
885+ chunk.idx = conn->chunk_no;
886+ strcpy(chunk.md5, conn->hash);
887+ sprintf(chunk.name, "%s/%s-%020zu", conn->container, conn->name,
888+ conn->chunk_no);
889+ chunk.size = conn->upload_size;
890+ slo_add_chunk(conn->global->manifest, &chunk);
891+
892+ conn->chunk_uploaded = 1;
893+ }
894+ }
895+
896+ assert(conn->filled_size <= conn->buffer_size);
897+ assert(conn->upload_size <= conn->filled_size);
898+
899+ return realsize;
900+}
901+
902+static
903+size_t upload_header_read_cb(void *ptr, size_t size, size_t nmemb,
904+ void *data)
905+{
906+ connection_info *conn = (connection_info*)(data);
907+ const char *buffer = (const char *)(ptr);
908+ char etag[33];
909+
910+ if (get_http_header("Etag: ", buffer, etag, array_elements(etag)) &&
911+ strcmp(etag, conn->hash) != 0) {
912+ fprintf(stderr, "md5 of uploaded chunk doesn't match\n");
913+ fprintf(stderr, "%s vs %s\n", etag, conn->hash);
914+ }
915+
916+ return nmemb * size;
917+}
918+
919+static int conn_upload_init(connection_info *conn)
920+{
921+ char object_url[SWIFT_MAX_URL_SIZE];
922+ CURLMcode rc;
923+
924+ snprintf(object_url, array_elements(object_url), "%s/%s/%s-%020ld",
925+ conn->url, conn->container, conn->name, conn->global->chunk_no);
926+
927+ conn->filled_size = 0;
928+ conn->upload_size = 0;
929+ conn->chunk_uploaded = 0;
930+ conn->chunk_no = conn->global->chunk_no++;
931+
932+ conn->easy = curl_easy_init();
933+ if (!conn->easy) {
934+ fprintf(stderr, "curl_easy_init() failed\n");
935+ return 1;
936+ }
937+ curl_easy_setopt(conn->easy, CURLOPT_URL, object_url);
938+ curl_easy_setopt(conn->easy, CURLOPT_READFUNCTION, swift_upload_read_cb);
939+ curl_easy_setopt(conn->easy, CURLOPT_READDATA, conn);
940+ curl_easy_setopt(conn->easy, CURLOPT_VERBOSE, opt_verbose);
941+ curl_easy_setopt(conn->easy, CURLOPT_ERRORBUFFER, conn->error);
942+ curl_easy_setopt(conn->easy, CURLOPT_PRIVATE, conn);
943+ curl_easy_setopt(conn->easy, CURLOPT_NOPROGRESS, 1L);
944+ curl_easy_setopt(conn->easy, CURLOPT_LOW_SPEED_TIME, 3L);
945+ curl_easy_setopt(conn->easy, CURLOPT_LOW_SPEED_LIMIT, 10L);
946+ curl_easy_setopt(conn->easy, CURLOPT_PUT, 1L);
947+ curl_easy_setopt(conn->easy, CURLOPT_HTTPHEADER, conn->slist);
948+ curl_easy_setopt(conn->easy, CURLOPT_HEADERFUNCTION, upload_header_read_cb);
949+ curl_easy_setopt(conn->easy, CURLOPT_HEADERDATA, conn);
950+ if (opt_cacert != NULL)
951+ curl_easy_setopt(conn->easy, CURLOPT_CAINFO, opt_cacert);
952+ if (opt_insecure)
953+ curl_easy_setopt(conn->easy, CURLOPT_SSL_VERIFYPEER, FALSE);
954+
955+ rc = curl_multi_add_handle(conn->global->multi, conn->easy);
956+ mcode_or_die("conn_upload_init: curl_multi_add_handle", rc);
957+
958+#if !((LIBCURL_VERSION_MAJOR >= 7) && (LIBCURL_VERSION_MINOR >= 16))
959+ do {
960+ rc = curl_multi_socket_all(conn->global->multi,
961+ &conn->global->still_running);
962+ } while(rc == CURLM_CALL_MULTI_PERFORM);
963+#endif
964+
965+ return 0;
966+}
967+
968+static void conn_cleanup(connection_info *conn)
969+{
970+ if (conn) {
971+ free(conn->url);
972+ free(conn->container);
973+ free(conn->token);
974+ free(conn->name);
975+ free(conn->buffer);
976+ if (conn->easy)
977+ curl_easy_cleanup(conn->easy);
978+ if (conn->md5)
979+ gcry_md_close(conn->md5);
980+ }
981+ free(conn);
982+}
983+
984+static connection_info *conn_new(const char *url, const char *container,
985+ const char *name, const char *token,
986+ global_io_info *global)
987+{
988+ connection_info *conn;
989+ char token_header[SWIFT_MAX_HDR_SIZE];
990+
991+ conn = (connection_info *)(calloc(1, sizeof(connection_info)));
992+ if (conn != NULL) {
993+ if ((conn->url = strdup(url)) == NULL)
994+ goto error;
995+ if ((conn->container = strdup(container)) == NULL)
996+ goto error;
997+ if ((conn->token = strdup(token)) == NULL)
998+ goto error;
999+ if ((conn->name = strdup(name)) == NULL)
1000+ goto error;
1001+ if (gcry_md_open(&conn->md5, GCRY_MD_MD5, 0) != 0)
1002+ goto error;
1003+ conn->global = global;
1004+ conn->buffer_size = SWIFT_CHUNK_SIZE;
1005+ if ((conn->buffer = (char *)(calloc(conn->buffer_size, 1))) == NULL)
1006+ goto error;
1007+ conn->filled_size = 0;
1008+ snprintf(token_header, array_elements(token_header), "X-Auth-Token: %s",
1009+ token);
1010+ conn->slist = curl_slist_append(conn->slist, token_header);
1011+ conn->slist = curl_slist_append(conn->slist, "Connection: keep-alive");
1012+ conn->slist = curl_slist_append(conn->slist, "Content-Length: 0");
1013+ conn->slist = curl_slist_append(conn->slist,
1014+ "Content-Type: application/octet-stream");
1015+ }
1016+
1017+ return conn;
1018+error:
1019+ conn_cleanup(conn);
1020+ return NULL;
1021+}
1022+
1023+static int init_input(global_io_info *io_global)
1024+{
1025+ ev_io_init(&io_global->input_event, input_cb, STDIN_FILENO, EV_READ);
1026+ io_global->input_event.data = io_global;
1027+ ev_io_start(io_global->loop, &io_global->input_event);
1028+
1029+ return 0;
1030+}
1031+
1032+/* Update the event timer after curl_multi library calls */
1033+static int multi_timer_cb(CURLM *multi, long timeout_ms, global_io_info *global)
1034+{
1035+ ev_timer_stop(global->loop, &global->timer_event);
1036+ if (timeout_ms > 0) {
1037+ double t = timeout_ms / 1000;
1038+ ev_timer_init(&global->timer_event, timer_cb, t, 0.);
1039+ ev_timer_start(global->loop, &global->timer_event);
1040+ } else {
1041+ timer_cb(global->loop, &global->timer_event, 0);
1042+ }
1043+ return 0;
1044+}
1045+
1046+static
1047+int swift_upload_manifest(swift_auth_info *auth, const char *container,
1048+ const char *name, slo_manifest *manifest);
1049+
1050+static int cmp_chunks(const void *c1, const void *c2)
1051+{
1052+ return ((slo_chunk*)(c1))->idx - ((slo_chunk*)(c2))->idx;
1053+}
1054+
1055+static
1056+int swift_upload_parts(swift_auth_info *auth, const char *container,
1057+ const char *name)
1058+{
1059+ global_io_info io_global;
1060+ int i;
1061+#if !((LIBCURL_VERSION_MAJOR >= 7) && (LIBCURL_VERSION_MINOR >= 16))
1062+ long timeout;
1063+ CURLMcode rc;
1064+#endif
1065+
1066+ memset(&io_global, 0, sizeof(io_global));
1067+
1068+ io_global.manifest = slo_manifest_init();
1069+ io_global.loop = ev_default_loop(0);
1070+ init_input(&io_global);
1071+ io_global.multi = curl_multi_init();
1072+ ev_timer_init(&io_global.timer_event, timer_cb, 0., 0.);
1073+ io_global.timer_event.data = &io_global;
1074+ io_global.connections = (connection_info **)
1075+ (calloc(opt_parallel, sizeof(connection_info)));
1076+ for (i = 0; i < opt_parallel; i++) {
1077+ io_global.connections[i] = conn_new(auth->url, container, name,
1078+ auth->token, &io_global);
1079+ }
1080+
1081+ /* setup the generic multi interface options we want */
1082+ curl_multi_setopt(io_global.multi, CURLMOPT_SOCKETFUNCTION, sock_cb);
1083+ curl_multi_setopt(io_global.multi, CURLMOPT_SOCKETDATA, &io_global);
1084+#if ((LIBCURL_VERSION_MAJOR >= 7) && (LIBCURL_VERSION_MINOR >= 16))
1085+ curl_multi_setopt(io_global.multi, CURLMOPT_TIMERFUNCTION, multi_timer_cb);
1086+ curl_multi_setopt(io_global.multi, CURLMOPT_TIMERDATA, &io_global);
1087+#else
1088+ curl_multi_timeout(io_global.multi, &timeout);
1089+ if (timeout >= 0)
1090+ multi_timer_cb(io_global.multi, timeout, &io_global);
1091+#endif
1092+
1093+#if ((LIBCURL_VERSION_MAJOR >= 7) && (LIBCURL_VERSION_MINOR >= 16))
1094+ curl_multi_socket_action(io_global.multi, CURL_SOCKET_TIMEOUT, 0,
1095+ &io_global.still_running);
1096+#else
1097+ do {
1098+ rc = curl_multi_socket_all(io_global.multi, &io_global.still_running);
1099+ } while(rc == CURLM_CALL_MULTI_PERFORM);
1100+#endif
1101+
1102+ ev_loop(io_global.loop, 0);
1103+ curl_multi_cleanup(io_global.multi);
1104+
1105+ for (i = 0; i < opt_parallel; i++) {
1106+ connection_info *conn = io_global.connections[i];
1107+ if (conn->upload_size != conn->filled_size) {
1108+ fprintf(stderr, "upload failed: data left in the buffer\n");
1109+ return 1;
1110+ }
1111+ }
1112+
1113+ qsort(io_global.manifest->chunks.buffer,
1114+ io_global.manifest->chunks.elements,
1115+ sizeof(slo_chunk), cmp_chunks);
1116+
1117+ if (swift_upload_manifest(auth, container, name, io_global.manifest) != 0) {
1118+ fprintf(stderr, "failed to create manifest\n");
1119+ return 1;
1120+ }
1121+
1122+ slo_manifest_free(io_global.manifest);
1123+
1124+ return 0;
1125+}
1126+
1127+static
1128+size_t
1129+swift_manifest_read_cb(void *buffer, size_t size, size_t nmemb, void *data)
1130+{
1131+ slo_manifest *manifest = (slo_manifest*)(data);
1132+ const char *prefix;
1133+ const char *suffix;
1134+ slo_chunk *chunk;
1135+ size_t idx;
1136+ int len;
1137+
1138+ idx = manifest->uploaded_idx++;
1139+
1140+ if (idx >= manifest->chunks.elements)
1141+ return 0;
1142+
1143+ prefix = (idx == 0) ? "[" : "";
1144+ suffix = (idx == manifest->chunks.elements - 1) ? "]" : ",";
1145+
1146+ chunk = (slo_chunk *)(dynamic_array_ptr(&manifest->chunks, idx));
1147+ len = snprintf(
1148+ (char *)(buffer), nmemb * size,
1149+ "%s{\"path\":\"%s\", \"etag\":\"%s\", \"size_bytes\":%zu}%s",
1150+ prefix,
1151+ chunk->name,
1152+ chunk->md5,
1153+ chunk->size,
1154+ suffix);
1155+
1156+ return len;
1157+}
1158+
1159+static
1160+int swift_upload_manifest(swift_auth_info *auth, const char *container,
1161+ const char *name, slo_manifest *manifest)
1162+{
1163+ char url[SWIFT_MAX_URL_SIZE];
1164+ char auth_token[SWIFT_MAX_HDR_SIZE];
1165+ CURLcode res;
1166+ long http_code;
1167+ CURL *curl;
1168+ struct curl_slist *slist = NULL;
1169+
1170+ snprintf(url, array_elements(url),
1171+ "%s/%s/%s?multipart-manifest=put", auth->url, container, name);
1172+ snprintf(auth_token, array_elements(auth_token), "X-Auth-Token: %s",
1173+ auth->token);
1174+
1175+ curl = curl_easy_init();
1176+
1177+ if (curl != NULL) {
1178+ slist = curl_slist_append(slist, auth_token);
1179+
1180+ curl_easy_setopt(curl, CURLOPT_VERBOSE, opt_verbose);
1181+ curl_easy_setopt(curl, CURLOPT_URL, url);
1182+ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
1183+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
1184+ curl_easy_setopt(curl, CURLOPT_READFUNCTION, swift_manifest_read_cb);
1185+ curl_easy_setopt(curl, CURLOPT_READDATA, manifest);
1186+ curl_easy_setopt(curl, CURLOPT_PUT, 1L);
1187+ if (opt_cacert != NULL)
1188+ curl_easy_setopt(curl, CURLOPT_CAINFO, opt_cacert);
1189+ if (opt_insecure)
1190+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE);
1191+
1192+ res = curl_easy_perform(curl);
1193+
1194+ if (res != CURLE_OK) {
1195+ fprintf(stderr, "error: curl_easy_perform() failed: %s\n",
1196+ curl_easy_strerror(res));
1197+ goto cleanup;
1198+ }
1199+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
1200+ if (http_code != 201 && /* created */
1201+ http_code != 202 /* accepted (already exists) */) {
1202+ fprintf(stderr, "error: request failed "
1203+ "with response code: %ld\n", http_code);
1204+ res = CURLE_LOGIN_DENIED;
1205+ goto cleanup;
1206+ }
1207+ } else {
1208+ res = CURLE_FAILED_INIT;
1209+ fprintf(stderr, "error: curl_easy_init() failed\n");
1210+ goto cleanup;
1211+ }
1212+
1213+cleanup:
1214+ if (slist)
1215+ curl_slist_free_all(slist);
1216+ if (curl)
1217+ curl_easy_cleanup(curl);
1218+
1219+ return res;
1220+}
1221+
1222+static
1223+size_t
1224+write_download_cb(void *buffer, size_t size, size_t nmemb, void *stream)
1225+{
1226+ FILE *out = (FILE*)(stream);
1227+
1228+ return fwrite(buffer, size, nmemb, out);
1229+}
1230+
1231+
1232+static
1233+int swift_download(swift_auth_info *auth, const char *container,
1234+ const char *name)
1235+{
1236+ char url[SWIFT_MAX_URL_SIZE];
1237+ char auth_token[SWIFT_MAX_HDR_SIZE];
1238+ CURLcode res;
1239+ long http_code;
1240+ CURL *curl;
1241+ struct curl_slist *slist = NULL;
1242+
1243+ snprintf(url, array_elements(url), "%s/%s/%s", auth->url, container, name);
1244+ snprintf(auth_token, array_elements(auth_token), "X-Auth-Token: %s",
1245+ auth->token);
1246+
1247+ curl = curl_easy_init();
1248+
1249+ if (curl != NULL) {
1250+ slist = curl_slist_append(slist, auth_token);
1251+
1252+ curl_easy_setopt(curl, CURLOPT_VERBOSE, opt_verbose);
1253+ curl_easy_setopt(curl, CURLOPT_URL, url);
1254+ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
1255+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
1256+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_download_cb);
1257+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, stdout);
1258+ if (opt_cacert != NULL)
1259+ curl_easy_setopt(curl, CURLOPT_CAINFO, opt_cacert);
1260+ if (opt_insecure)
1261+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE);
1262+
1263+ res = curl_easy_perform(curl);
1264+
1265+ if (res != CURLE_OK) {
1266+ fprintf(stderr, "error: curl_easy_perform() failed: %s\n",
1267+ curl_easy_strerror(res));
1268+ goto cleanup;
1269+ }
1270+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
1271+ if (http_code != 200) {
1272+ fprintf(stderr, "error: request failed "
1273+ "with response code: %ld\n", http_code);
1274+ res = CURLE_LOGIN_DENIED;
1275+ goto cleanup;
1276+ }
1277+ } else {
1278+ res = CURLE_FAILED_INIT;
1279+ fprintf(stderr, "error: curl_easy_init() failed\n");
1280+ goto cleanup;
1281+ }
1282+
1283+cleanup:
1284+ if (slist)
1285+ curl_slist_free_all(slist);
1286+ if (curl)
1287+ curl_easy_cleanup(curl);
1288+
1289+ return res;
1290+}
1291+
1292+int main(int argc, char **argv)
1293+{
1294+ swift_auth_info info;
1295+ char auth_url[SWIFT_MAX_URL_SIZE];
1296+
1297+ if (parse_args(argc, argv))
1298+ return 1;
1299+
1300+ if (opt_mode == MODE_PUT) {
1301+
1302+ curl_global_init(CURL_GLOBAL_ALL);
1303+
1304+ snprintf(auth_url, SWIFT_MAX_URL_SIZE, "%sauth/v1.0/", opt_url);
1305+
1306+ if (swift_auth(auth_url, opt_user, opt_key, &info) != 0) {
1307+ fprintf(stderr, "failed to authenticate\n");
1308+ return 1;
1309+ }
1310+
1311+ if (swift_create_container(&info, opt_container) != 0) {
1312+ fprintf(stderr, "failed to create container %s\n", opt_container);
1313+ return 1;
1314+ }
1315+
1316+ if (swift_upload_parts(&info, opt_container, opt_name) != 0) {
1317+ fprintf(stderr, "upload failed\n");
1318+ return 1;
1319+ }
1320+
1321+ curl_global_cleanup();
1322+
1323+ } else {
1324+
1325+ curl_global_init(CURL_GLOBAL_ALL);
1326+
1327+ snprintf(auth_url, SWIFT_MAX_URL_SIZE, "%sauth/v1.0/", opt_url);
1328+
1329+ if (swift_auth(auth_url, opt_user, opt_key, &info) != 0) {
1330+ fprintf(stderr, "failed to authenticate\n");
1331+ return 1;
1332+ }
1333+
1334+ if (swift_download(&info, opt_container, opt_name) != 0) {
1335+ fprintf(stderr, "download failed\n");
1336+ return 1;
1337+ }
1338+
1339+ curl_global_cleanup();
1340+ }
1341+
1342+ return 0;
1343+}
1344
1345=== added file 'storage/innobase/xtrabackup/test/t/xbcloud.sh'
1346--- storage/innobase/xtrabackup/test/t/xbcloud.sh 1970-01-01 00:00:00 +0000
1347+++ storage/innobase/xtrabackup/test/t/xbcloud.sh 2014-10-23 18:15:30 +0000
1348@@ -0,0 +1,69 @@
1349+################################################################################
1350+# Test xbcloud
1351+#
1352+# Set following environment variables to enable this test:
1353+# SWIFT_URL, SWIFT_USER, SWIFT_KEY, SWIFT_CONTAINER
1354+#
1355+################################################################################
1356+
1357+. inc/common.sh
1358+
1359+[ "${SWIFT_URL:-unset}" == "unset" ] && skip_test "Requires Swift"
1360+
1361+start_server --innodb_file_per_table
1362+
1363+load_dbase_schema sakila
1364+load_dbase_data sakila
1365+
1366+full_backup_dir=$topdir/full_backup
1367+part_backup_dir=$topdir/part_backup
1368+
1369+vlog "take full backup"
1370+
1371+innobackupex --stream=xbstream $full_backup_dir --extra-lsndir=$full_backup_dir | xbcloud put --storage=Swift \
1372+ --swift-container=test_backup \
1373+ --swift-user=${SWIFT_USER} \
1374+ --swift-url=${SWIFT_URL} \
1375+ --swift-key=${SWIFT_KEY} \
1376+ --parallel=10 \
1377+ --verbose \
1378+ full_backup
1379+
1380+vlog "take incremental backup"
1381+
1382+inc_lsn=`grep to_lsn $full_backup_dir/xtrabackup_checkpoints | \
1383+ sed 's/to_lsn = //'`
1384+
1385+[ -z "$inc_lsn" ] && die "Couldn't read to_lsn from xtrabackup_checkpoints"
1386+
1387+innobackupex --incremental --incremental-lsn=$inc_lsn --stream=xbstream part_backup_dir | xbcloud put --storage=Swift \
1388+ --swift-container=test_backup \
1389+ --swift-user=${SWIFT_USER} \
1390+ --swift-url=${SWIFT_URL} \
1391+ --swift-key=${SWIFT_KEY} \
1392+ incremental
1393+
1394+vlog "download and prepare"
1395+
1396+mkdir $topdir/downloaded_full
1397+mkdir $topdir/downloaded_inc
1398+
1399+xbcloud get --storage=Swift \
1400+ --swift-container=test_backup \
1401+ --swift-user=${SWIFT_USER} \
1402+ --swift-url=${SWIFT_URL} \
1403+ --swift-key=${SWIFT_KEY} \
1404+ full_backup | xbstream -xv -C $topdir/downloaded_full
1405+
1406+innobackupex --apply-log --redo-only $topdir/downloaded_full
1407+
1408+xbcloud get --storage=Swift \
1409+ --swift-container=test_backup \
1410+ --swift-user=${SWIFT_USER} \
1411+ --swift-url=${SWIFT_URL} \
1412+ --swift-key=${SWIFT_KEY} \
1413+ incremental | xbstream -xv -C $topdir/downloaded_inc
1414+
1415+innobackupex --apply-log --redo-only $topdir/downloaded_full --incremental-dir=$topdir/downloaded_inc
1416+
1417+innobackupex --apply-log $topdir/downloaded_full
1418
1419=== modified file 'storage/innobase/xtrabackup/utils/debian/control'
1420--- storage/innobase/xtrabackup/utils/debian/control 2014-08-22 10:40:29 +0000
1421+++ storage/innobase/xtrabackup/utils/debian/control 2014-10-23 18:15:30 +0000
1422@@ -14,6 +14,8 @@
1423 libtool,
1424 libz-dev,
1425 libgcrypt-dev,
1426+ libev-dev,
1427+ libcurl-dev,
1428 lsb-release
1429 Standards-Version: 3.9.5
1430 Homepage: http://www.percona.com/software/percona-xtrabackup
1431
1432=== modified file 'storage/innobase/xtrabackup/utils/debian/rules'
1433--- storage/innobase/xtrabackup/utils/debian/rules 2014-07-22 12:46:09 +0000
1434+++ storage/innobase/xtrabackup/utils/debian/rules 2014-10-23 18:15:30 +0000
1435@@ -27,6 +27,7 @@
1436 echo 'main() { return 300; }' | gcc -x c - -o xtrabackup_56
1437 echo 'main() { return 300; }' | gcc -x c - -o xbstream
1438 echo 'main() { return 300; }' | gcc -x c - -o xbcrypt
1439+ echo 'main() { return 300; }' | gcc -x c - -o xbcloud
1440 endif
1441
1442 #docbook-to-man debian/xtrabackup.sgml > xtrabackup.1
1443
1444=== modified file 'storage/innobase/xtrabackup/utils/percona-xtrabackup.spec'
1445--- storage/innobase/xtrabackup/utils/percona-xtrabackup.spec 2014-09-26 09:09:04 +0000
1446+++ storage/innobase/xtrabackup/utils/percona-xtrabackup.spec 2014-10-23 18:15:30 +0000
1447@@ -16,7 +16,7 @@
1448 URL: http://www.percona.com/software/percona-xtrabackup
1449 Source: percona-xtrabackup-%{version}%{xb_version_extra}.tar.gz
1450
1451-BuildRequires: cmake, libaio-devel, libgcrypt-devel, ncurses-devel, readline-devel, zlib-devel
1452+BuildRequires: cmake, libaio-devel, libgcrypt-devel, ncurses-devel, readline-devel, zlib-devel, libev-devel, libcurl-devel
1453 Requires: perl(DBD::mysql), rsync
1454 BuildRoot: %{_tmppath}/%{name}-%{version}-root
1455
1456@@ -45,6 +45,7 @@
1457 echo 'main() { return 300; }' | gcc -x c - -o storage/innobase/xtrabackup/src/xtrabackup
1458 echo 'main() { return 300; }' | gcc -x c - -o storage/innobase/xtrabackup/src/xbstream
1459 echo 'main() { return 300; }' | gcc -x c - -o storage/innobase/xtrabackup/src/xbcrypt
1460+echo 'main() { return 300; }' | gcc -x c - -o storage/innobase/xtrabackup/src/xbcloud
1461 #
1462 %else
1463 #
1464@@ -73,6 +74,7 @@
1465 %{_bindir}/xtrabackup
1466 %{_bindir}/xbstream
1467 %{_bindir}/xbcrypt
1468+%{_bindir}/xbcloud
1469 %doc COPYING
1470
1471 %files -n percona-xtrabackup-test

Subscribers

People subscribed via source and target branches