Merge lp:~sergei.glushchenko/percona-xtrabackup/xbcloud into lp:percona-xtrabackup/2.2
- xbcloud
- Merge into 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 |
Related bugs: | |
Related blueprints: |
Support backups to swift
(Essential)
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Alexey Kopytov (community) | Needs Resubmitting | ||
Review via email: mp+239220@code.launchpad.net |
Commit message
Description of the change
Implementation of https:/
Jenkins:
http://
CentOS 5 failed because of ancient libcurl. I'll fix it ASAP.
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 |
I builds on CentOS 5 now http:// jenkins. percona. com/view/ PXB%202. 2/job/percona- xtrabackup- 2.2-param/ 242/