1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at http://curl.haxx.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 ***************************************************************************/ 22#include "test.h" 23 24#include <sys/types.h> 25#include <sys/stat.h> 26#include <fcntl.h> 27 28#include "testutil.h" 29#include "warnless.h" 30#include "memdebug.h" 31 32#define MAIN_LOOP_HANG_TIMEOUT 4 * 1000 33 34struct Sockets 35{ 36 curl_socket_t* sockets; 37 int count; 38}; 39 40struct ReadWriteSockets 41{ 42 struct Sockets read, write; 43}; 44 45/** 46 * Remove a file descriptor from a sockets array. 47 */ 48static void removeFd(struct Sockets* sockets, curl_socket_t fd, int mention) 49{ 50 int i; 51 52 if(mention) 53 fprintf(stderr, "Remove socket fd %d\n", (int) fd); 54 55 for (i = 0; i < sockets->count; ++i) { 56 if (sockets->sockets[i] == fd) { 57 memmove(&sockets->sockets[i], &sockets->sockets[i + 1], 58 sizeof(curl_socket_t) * (sockets->count - i - 1)); 59 --sockets->count; 60 } 61 } 62} 63 64/** 65 * Add a file descriptor to a sockets array. 66 */ 67static void addFd(struct Sockets* sockets, curl_socket_t fd, const char *what) 68{ 69 /** 70 * To ensure we only have each file descriptor once, we remove it then add 71 * it again. 72 */ 73 fprintf(stderr, "Add socket fd %d for %s\n", (int) fd, what); 74 removeFd(sockets, fd, 0); 75 sockets->sockets = realloc(sockets->sockets, 76 sizeof(curl_socket_t) * (sockets->count + 1)); 77 sockets->sockets[sockets->count] = fd; 78 ++sockets->count; 79} 80 81/** 82 * Callback invoked by curl to poll reading / writing of a socket. 83 */ 84static int curlSocketCallback(CURL *easy, curl_socket_t s, int action, 85 void *userp, void *socketp) 86{ 87 struct ReadWriteSockets* sockets = userp; 88 89 (void)easy; /* unused */ 90 (void)socketp; /* unused */ 91 92 if (action == CURL_POLL_IN || action == CURL_POLL_INOUT) 93 addFd(&sockets->read, s, "read"); 94 95 if (action == CURL_POLL_OUT || action == CURL_POLL_INOUT) 96 addFd(&sockets->write, s, "write"); 97 98 if(action == CURL_POLL_REMOVE) { 99 removeFd(&sockets->read, s, 1); 100 removeFd(&sockets->write, s, 0); 101 } 102 103 return 0; 104} 105 106/** 107 * Callback invoked by curl to set a timeout. 108 */ 109static int curlTimerCallback(CURLM *multi, long timeout_ms, void *userp) 110{ 111 struct timeval* timeout = userp; 112 113 (void)multi; /* unused */ 114 if (timeout_ms != -1) { 115 gettimeofday(timeout, 0); 116 timeout->tv_usec += timeout_ms * 1000; 117 } 118 else { 119 timeout->tv_sec = -1; 120 } 121 return 0; 122} 123 124/** 125 * Check for curl completion. 126 */ 127static int checkForCompletion(CURLM* curl, int* success) 128{ 129 int numMessages; 130 CURLMsg* message; 131 int result = 0; 132 *success = 0; 133 while ((message = curl_multi_info_read(curl, &numMessages)) != NULL) { 134 if (message->msg == CURLMSG_DONE) { 135 result = 1; 136 if (message->data.result == CURLE_OK) 137 *success = 1; 138 else 139 *success = 0; 140 } 141 else { 142 fprintf(stderr, "Got an unexpected message from curl: %i\n", 143 (int)message->msg); 144 result = 1; 145 *success = 0; 146 } 147 } 148 return result; 149} 150 151static int getMicroSecondTimeout(struct timeval* timeout) 152{ 153 struct timeval now; 154 int result; 155 156 gettimeofday(&now, 0); 157 result = (timeout->tv_sec - now.tv_sec) * 1000000 + 158 timeout->tv_usec - now.tv_usec; 159 if (result < 0) 160 result = 0; 161 162 return result; 163} 164 165/** 166 * Update a fd_set with all of the sockets in use. 167 */ 168static void updateFdSet(struct Sockets* sockets, fd_set* fdset, 169 curl_socket_t *maxFd) 170{ 171 int i; 172 for (i = 0; i < sockets->count; ++i) { 173 FD_SET(sockets->sockets[i], fdset); 174 if (*maxFd < sockets->sockets[i] + 1) { 175 *maxFd = sockets->sockets[i] + 1; 176 } 177 } 178} 179 180static void notifyCurl(CURL* curl, curl_socket_t s, int evBitmask, 181 const char* info) 182{ 183 int numhandles = 0; 184 CURLMcode result = curl_multi_socket_action(curl, s, evBitmask, &numhandles); 185 if (result != CURLM_OK && result != CURLM_CALL_MULTI_PERFORM) 186 { 187 fprintf(stderr, "Curl error on %s: %i (%s)\n", 188 info, result, curl_multi_strerror(result)); 189 } 190} 191 192/** 193 * Invoke curl when a file descriptor is set. 194 */ 195static void checkFdSet(CURL* curl, struct Sockets* sockets, fd_set* fdset, 196 int evBitmask, const char* name) 197{ 198 int i; 199 for (i = 0; i < sockets->count; ++i) 200 { 201 if (FD_ISSET(sockets->sockets[i], fdset)) 202 { 203 notifyCurl(curl, sockets->sockets[i], evBitmask, name); 204 } 205 } 206} 207 208int test(char *URL) 209{ 210 int res = 0; 211 CURL *curl; 212 FILE *hd_src ; 213 int hd ; 214 int error; 215 struct_stat file_info; 216 CURLM *m = NULL; 217 struct timeval ml_start; 218 char ml_timedout = FALSE; 219 struct ReadWriteSockets sockets = {{0, 0}, {0, 0}}; 220 struct timeval timeout = {-1, 0}; 221 int success = 0; 222 223 if (!libtest_arg3) { 224 fprintf(stderr, "Usage: lib582 [url] [filename] [username]\n"); 225 return -1; 226 } 227 228 hd_src = fopen(libtest_arg2, "rb"); 229 if(NULL == hd_src) { 230 error = ERRNO; 231 fprintf(stderr, "fopen() failed with error: %d %s\n", 232 error, strerror(error)); 233 fprintf(stderr, "Error opening file: %s\n", libtest_arg2); 234 return TEST_ERR_MAJOR_BAD; 235 } 236 237 /* get the file size of the local file */ 238 hd = fstat(fileno(hd_src), &file_info); 239 if(hd == -1) { 240 /* can't open file, bail out */ 241 error = ERRNO; 242 fprintf(stderr, "fstat() failed with error: %d %s\n", 243 error, strerror(error)); 244 fprintf(stderr, "ERROR: cannot open file %s\n", libtest_arg2); 245 fclose(hd_src); 246 return -1; 247 } 248 fprintf(stderr, "Set to upload %d bytes\n", (int)file_info.st_size); 249 250 if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { 251 fprintf(stderr, "curl_global_init() failed\n"); 252 fclose(hd_src); 253 return TEST_ERR_MAJOR_BAD; 254 } 255 256 if ((curl = curl_easy_init()) == NULL) { 257 fprintf(stderr, "curl_easy_init() failed\n"); 258 fclose(hd_src); 259 curl_global_cleanup(); 260 return TEST_ERR_MAJOR_BAD; 261 } 262 263 /* enable uploading */ 264 test_setopt(curl, CURLOPT_UPLOAD, 1L); 265 266 /* specify target */ 267 test_setopt(curl,CURLOPT_URL, URL); 268 269 /* go verbose */ 270 test_setopt(curl, CURLOPT_VERBOSE, 1L); 271 272 /* now specify which file to upload */ 273 test_setopt(curl, CURLOPT_READDATA, hd_src); 274 275 test_setopt(curl, CURLOPT_USERPWD, libtest_arg3); 276 test_setopt(curl, CURLOPT_SSH_PUBLIC_KEYFILE, "curl_client_key.pub"); 277 test_setopt(curl, CURLOPT_SSH_PRIVATE_KEYFILE, "curl_client_key"); 278 279 test_setopt(curl, CURLOPT_INFILESIZE_LARGE, 280 (curl_off_t)file_info.st_size); 281 282 if ((m = curl_multi_init()) == NULL) { 283 fprintf(stderr, "curl_multi_init() failed\n"); 284 curl_easy_cleanup(curl); 285 curl_global_cleanup(); 286 fclose(hd_src); 287 return TEST_ERR_MAJOR_BAD; 288 } 289 test_multi_setopt(m, CURLMOPT_SOCKETFUNCTION, curlSocketCallback); 290 test_multi_setopt(m, CURLMOPT_SOCKETDATA, &sockets); 291 292 test_multi_setopt(m, CURLMOPT_TIMERFUNCTION, curlTimerCallback); 293 test_multi_setopt(m, CURLMOPT_TIMERDATA, &timeout); 294 295 if ((res = (int)curl_multi_add_handle(m, curl)) != CURLM_OK) { 296 fprintf(stderr, "curl_multi_add_handle() failed, " 297 "with code %d\n", res); 298 curl_multi_cleanup(m); 299 curl_easy_cleanup(curl); 300 curl_global_cleanup(); 301 fclose(hd_src); 302 return TEST_ERR_MAJOR_BAD; 303 } 304 305 ml_timedout = FALSE; 306 ml_start = tutil_tvnow(); 307 308 while (!checkForCompletion(m, &success)) 309 { 310 fd_set readSet, writeSet; 311 curl_socket_t maxFd = 0; 312 struct timeval tv = {10, 0}; 313 314 if (tutil_tvdiff(tutil_tvnow(), ml_start) > 315 MAIN_LOOP_HANG_TIMEOUT) { 316 ml_timedout = TRUE; 317 break; 318 } 319 320 FD_ZERO(&readSet); 321 FD_ZERO(&writeSet); 322 updateFdSet(&sockets.read, &readSet, &maxFd); 323 updateFdSet(&sockets.write, &writeSet, &maxFd); 324 325 if (timeout.tv_sec != -1) 326 { 327 int usTimeout = getMicroSecondTimeout(&timeout); 328 tv.tv_sec = usTimeout / 1000000; 329 tv.tv_usec = usTimeout % 1000000; 330 } 331 else if (maxFd <= 0) 332 { 333 tv.tv_sec = 0; 334 tv.tv_usec = 100000; 335 } 336 337 select_test(maxFd, &readSet, &writeSet, NULL, &tv); 338 339 /* Check the sockets for reading / writing */ 340 checkFdSet(m, &sockets.read, &readSet, CURL_CSELECT_IN, "read"); 341 checkFdSet(m, &sockets.write, &writeSet, CURL_CSELECT_OUT, "write"); 342 343 if (timeout.tv_sec != -1 && getMicroSecondTimeout(&timeout) == 0) 344 { 345 /* Curl's timer has elapsed. */ 346 notifyCurl(m, CURL_SOCKET_TIMEOUT, 0, "timeout"); 347 } 348 } 349 350 if (!success) 351 { 352 fprintf(stderr, "Error uploading file.\n"); 353 res = TEST_ERR_MAJOR_BAD; 354 } 355 else if (ml_timedout) { 356 fprintf(stderr, "ABORTING TEST, since it seems " 357 "that it would have run forever.\n"); 358 res = TEST_ERR_RUNS_FOREVER; 359 } 360 361test_cleanup: 362 363 if(m) 364 curl_multi_remove_handle(m, curl); 365 curl_easy_cleanup(curl); 366 if(m) { 367 fprintf(stderr, "Now multi-cleanup!\n"); 368 curl_multi_cleanup(m); 369 } 370 371 fclose(hd_src); /* close the local file */ 372 if (sockets.read.sockets != 0) 373 free(sockets.read.sockets); 374 if (sockets.write.sockets != 0) 375 free(sockets.write.sockets); 376 377 curl_global_cleanup(); 378 return res; 379} 380