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

Proposed by Alexey Kopytov
Status: Merged
Approved by: Alexey Kopytov
Approved revision: no longer in the source branch.
Merged at revision: 5038
Proposed branch: lp:~sergei.glushchenko/percona-xtrabackup/xbcloud
Merge into: lp:percona-xtrabackup/2.3
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) Approve
Review via email: mp+240707@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Alexey Kopytov (akopytov) wrote :

Will merge as is for now, but will reformat the code according to InnoDB style.

review: Approve

Preview Diff

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

Subscribers

People subscribed via source and target branches