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