static void metainfoLookupRescan( tr_session * session ) { int i; int n; struct stat sb; const char * dirname = tr_getTorrentDir( session ); DIR * odir = NULL; tr_ctor * ctor = NULL; tr_list * list = NULL; assert( tr_isSession( session ) ); /* walk through the directory and find the mappings */ ctor = tr_ctorNew( session ); tr_ctorSetSave( ctor, FALSE ); /* since we already have them */ if( !stat( dirname, &sb ) && S_ISDIR( sb.st_mode ) && ( ( odir = opendir( dirname ) ) ) ) { struct dirent *d; for( d = readdir( odir ); d != NULL; d = readdir( odir ) ) { if( d->d_name && d->d_name[0] != '.' ) /* skip dotfiles, ., and .. */ { tr_info inf; char * path = tr_buildPath( dirname, d->d_name, NULL ); tr_ctorSetMetainfoFromFile( ctor, path ); if( !tr_torrentParse( session, ctor, &inf ) ) { tr_list_append( &list, tr_strdup( inf.hashString ) ); tr_list_append( &list, tr_strdup( path ) ); tr_metainfoFree( &inf ); } tr_free( path ); } } closedir( odir ); } tr_ctorFree( ctor ); n = tr_list_size( list ) / 2; session->metainfoLookup = tr_new0( struct tr_metainfo_lookup, n ); session->metainfoLookupCount = n; for( i = 0; i < n; ++i ) { char * hashString = tr_list_pop_front( &list ); char * filename = tr_list_pop_front( &list ); memcpy( session->metainfoLookup[i].hashString, hashString, 2 * SHA_DIGEST_LENGTH + 1 ); tr_free( hashString ); session->metainfoLookup[i].filename = filename; } metainfoLookupResort( session ); tr_dbg( "Found %d torrents in \"%s\"", n, dirname ); }
static void didWriteWrapper( tr_peerIo * io, unsigned int bytes_transferred ) { while( bytes_transferred && tr_isPeerIo( io ) ) { struct tr_datatype * next = io->outbuf_datatypes->data; const unsigned int payload = MIN( next->length, bytes_transferred ); const unsigned int overhead = guessPacketOverhead( payload ); const uint64_t now = tr_time_msec( ); tr_bandwidthUsed( &io->bandwidth, TR_UP, payload, next->isPieceData, now ); if( overhead > 0 ) tr_bandwidthUsed( &io->bandwidth, TR_UP, overhead, FALSE, now ); if( io->didWrite ) io->didWrite( io, payload, next->isPieceData, io->userData ); if( tr_isPeerIo( io ) ) { bytes_transferred -= payload; next->length -= payload; if( !next->length ) { tr_list_pop_front( &io->outbuf_datatypes ); tr_free( next ); } } } }
static void add_tasks_from_queue( tr_web * g ) { while( ( g->still_running < MAX_CONCURRENT_TASKS ) && ( tr_list_size( g->easy_queue ) > 0 ) ) { CURL * easy = tr_list_pop_front( &g->easy_queue ); if( easy ) { const CURLMcode rc = curl_multi_add_handle( g->multi, easy ); if( rc != CURLM_OK ) tr_err( "%s", curl_multi_strerror( rc ) ); else { dbgmsg( "pumped the task queue, %d remain", tr_list_size( g->easy_queue ) ); ++g->still_running; } } } }
static void tr_webThreadFunc( void * vsession ) { int unused; CURLM * multi; struct tr_web * web; int taskCount = 0; tr_session * session = vsession; /* try to enable ssl for https support; but if that fails, * try a plain vanilla init */ if( curl_global_init( CURL_GLOBAL_SSL ) ) curl_global_init( 0 ); web = tr_new0( struct tr_web, 1 ); web->close_mode = ~0; web->taskLock = tr_lockNew( ); web->tasks = NULL; multi = curl_multi_init( ); session->web = web; for( ;; ) { long msec; CURLMsg * msg; CURLMcode mcode; struct tr_web_task * task; if( web->close_mode == TR_WEB_CLOSE_NOW ) break; if( ( web->close_mode == TR_WEB_CLOSE_WHEN_IDLE ) && !taskCount ) break; /* add tasks from the queue */ tr_lockLock( web->taskLock ); while(( task = tr_list_pop_front( &web->tasks ))) { dbgmsg( "adding task to curl: [%s]", task->url ); curl_multi_add_handle( multi, createEasy( session, task )); /*fprintf( stderr, "adding a task.. taskCount is now %d\n", taskCount );*/ ++taskCount; } tr_lockUnlock( web->taskLock ); /* maybe wait a little while before calling curl_multi_perform() */ msec = 0; curl_multi_timeout( multi, &msec ); if( msec < 0 ) msec = THREADFUNC_MAX_SLEEP_MSEC; if( msec > 0 ) { int usec; int max_fd; struct timeval t; fd_set r_fd_set, w_fd_set, c_fd_set; max_fd = 0; FD_ZERO( &r_fd_set ); FD_ZERO( &w_fd_set ); FD_ZERO( &c_fd_set ); curl_multi_fdset( multi, &r_fd_set, &w_fd_set, &c_fd_set, &max_fd ); if( msec > THREADFUNC_MAX_SLEEP_MSEC ) msec = THREADFUNC_MAX_SLEEP_MSEC; usec = msec * 1000; t.tv_sec = usec / 1000000; t.tv_usec = usec % 1000000; tr_select( max_fd+1, &r_fd_set, &w_fd_set, &c_fd_set, &t ); } /* call curl_multi_perform() */ do { mcode = curl_multi_perform( multi, &unused ); } while( mcode == CURLM_CALL_MULTI_PERFORM ); /* pump completed tasks from the multi */ while(( msg = curl_multi_info_read( multi, &unused ))) { if(( msg->msg == CURLMSG_DONE ) && ( msg->easy_handle != NULL )) { struct tr_web_task * task; CURL * e = msg->easy_handle; curl_easy_getinfo( e, CURLINFO_PRIVATE, (void*)&task ); curl_easy_getinfo( e, CURLINFO_RESPONSE_CODE, &task->code ); curl_multi_remove_handle( multi, e ); curl_easy_cleanup( e ); /*fprintf( stderr, "removing a completed task.. taskCount is now %d (response code: %d, response len: %d)\n", taskCount, (int)task->code, (int)evbuffer_get_length(task->response) );*/ tr_runInEventThread( task->session, task_finish_func, task ); --taskCount; } } } /* cleanup */ curl_multi_cleanup( multi ); tr_lockFree( web->taskLock ); tr_free( web ); session->web = NULL; }
static void tr_webThreadFunc( void * vsession ) { CURLM * multi; struct tr_web * web; int taskCount = 0; struct tr_web_task * task; tr_session * session = vsession; /* try to enable ssl for https support; but if that fails, * try a plain vanilla init */ if( curl_global_init( CURL_GLOBAL_SSL ) ) curl_global_init( 0 ); web = tr_new0( struct tr_web, 1 ); web->close_mode = ~0; web->taskLock = tr_lockNew( ); web->tasks = NULL; web->curl_verbose = getenv( "TR_CURL_VERBOSE" ) != NULL; web->cookie_filename = tr_buildPath( session->configDir, "cookies.txt", NULL ); multi = curl_multi_init( ); session->web = web; for( ;; ) { long msec; int unused; CURLMsg * msg; CURLMcode mcode; if( web->close_mode == TR_WEB_CLOSE_NOW ) break; if( ( web->close_mode == TR_WEB_CLOSE_WHEN_IDLE ) && ( web->tasks == NULL ) ) break; /* add tasks from the queue */ tr_lockLock( web->taskLock ); while(( task = tr_list_pop_front( &web->tasks ))) { dbgmsg( "adding task to curl: [%s]", task->url ); curl_multi_add_handle( multi, createEasy( session, web, task )); /*fprintf( stderr, "adding a task.. taskCount is now %d\n", taskCount );*/ ++taskCount; } tr_lockUnlock( web->taskLock ); /* maybe wait a little while before calling curl_multi_perform() */ msec = 0; curl_multi_timeout( multi, &msec ); if( msec < 0 ) msec = THREADFUNC_MAX_SLEEP_MSEC; if( session->isClosed ) msec = 100; /* on shutdown, call perform() more frequently */ if( msec > 0 ) { int usec; int max_fd; struct timeval t; fd_set r_fd_set, w_fd_set, c_fd_set; max_fd = 0; FD_ZERO( &r_fd_set ); FD_ZERO( &w_fd_set ); FD_ZERO( &c_fd_set ); curl_multi_fdset( multi, &r_fd_set, &w_fd_set, &c_fd_set, &max_fd ); if( msec > THREADFUNC_MAX_SLEEP_MSEC ) msec = THREADFUNC_MAX_SLEEP_MSEC; usec = msec * 1000; t.tv_sec = usec / 1000000; t.tv_usec = usec % 1000000; tr_select( max_fd+1, &r_fd_set, &w_fd_set, &c_fd_set, &t ); } /* call curl_multi_perform() */ do { mcode = curl_multi_perform( multi, &unused ); } while( mcode == CURLM_CALL_MULTI_PERFORM ); /* pump completed tasks from the multi */ while(( msg = curl_multi_info_read( multi, &unused ))) { if(( msg->msg == CURLMSG_DONE ) && ( msg->easy_handle != NULL )) { double total_time; struct tr_web_task * task; long req_bytes_sent; CURL * e = msg->easy_handle; curl_easy_getinfo( e, CURLINFO_PRIVATE, (void*)&task ); curl_easy_getinfo( e, CURLINFO_RESPONSE_CODE, &task->code ); curl_easy_getinfo( e, CURLINFO_REQUEST_SIZE, &req_bytes_sent ); curl_easy_getinfo( e, CURLINFO_TOTAL_TIME, &total_time ); task->did_connect = task->code>0 || req_bytes_sent>0; task->did_timeout = !task->code && ( total_time >= task->timeout_secs ); curl_multi_remove_handle( multi, e ); curl_easy_cleanup( e ); /*fprintf( stderr, "removing a completed task.. taskCount is now %d (response code: %d, response len: %d)\n", taskCount, (int)task->code, (int)evbuffer_get_length(task->response) );*/ tr_runInEventThread( task->session, task_finish_func, task ); --taskCount; } } #if 0 { tr_list * l; for( l=web->tasks; l!=NULL; l=l->next ) fprintf( stderr, "still pending: %s\n", ((struct tr_web_task*)l->data)->url ); } fprintf( stderr, "loop is ending... web is closing\n" ); #endif } /* Discard any remaining tasks. * This is rare, but can happen on shutdown with unresponsive trackers. */ while(( task = tr_list_pop_front( &web->tasks ))) { dbgmsg( "Discarding task \"%s\"", task->url ); task_free( task ); } /* cleanup */ curl_multi_cleanup( multi ); tr_lockFree( web->taskLock ); tr_free( web->cookie_filename ); tr_free( web ); session->web = NULL; }
static void tr_webThreadFunc (void * vsession) { char * str; CURLM * multi; struct tr_web * web; int taskCount = 0; struct tr_web_task * task; tr_session * session = vsession; /* try to enable ssl for https support; but if that fails, * try a plain vanilla init */ if (curl_global_init (CURL_GLOBAL_SSL)) curl_global_init (0); web = tr_new0 (struct tr_web, 1); web->close_mode = ~0; web->taskLock = tr_lockNew (); web->tasks = NULL; web->curl_verbose = getenv ("TR_CURL_VERBOSE") != NULL; web->curl_ssl_verify = getenv ("TR_CURL_SSL_VERIFY") != NULL; web->curl_ca_bundle = getenv ("CURL_CA_BUNDLE"); if (web->curl_ssl_verify) { tr_logAddNamedInfo ("web", "will verify tracker certs using envvar CURL_CA_BUNDLE: %s", web->curl_ca_bundle == NULL ? "none" : web->curl_ca_bundle); tr_logAddNamedInfo ("web", "NB: this only works if you built against libcurl with openssl or gnutls, NOT nss"); tr_logAddNamedInfo ("web", "NB: invalid certs will show up as 'Could not connect to tracker' like many other errors"); } str = tr_buildPath (session->configDir, "cookies.txt", NULL); if (tr_fileExists (str, NULL)) web->cookie_filename = tr_strdup (str); tr_free (str); multi = curl_multi_init (); session->web = web; for (;;) { long msec; int unused; CURLMsg * msg; CURLMcode mcode; if (web->close_mode == TR_WEB_CLOSE_NOW) break; if ((web->close_mode == TR_WEB_CLOSE_WHEN_IDLE) && (web->tasks == NULL)) break; /* add tasks from the queue */ tr_lockLock (web->taskLock); while (web->tasks != NULL) { /* pop the task */ task = web->tasks; web->tasks = task->next; task->next = NULL; dbgmsg ("adding task to curl: [%s]", task->url); curl_multi_add_handle (multi, createEasy (session, web, task)); /*fprintf (stderr, "adding a task.. taskCount is now %d\n", taskCount);*/ ++taskCount; } tr_lockUnlock (web->taskLock); /* unpause any paused curl handles */ if (paused_easy_handles != NULL) { CURL * handle; tr_list * tmp; /* swap paused_easy_handles to prevent oscillation between writeFunc this while loop */ tmp = paused_easy_handles; paused_easy_handles = NULL; while ((handle = tr_list_pop_front (&tmp))) curl_easy_pause (handle, CURLPAUSE_CONT); } /* maybe wait a little while before calling curl_multi_perform () */ msec = 0; curl_multi_timeout (multi, &msec); if (msec < 0) msec = THREADFUNC_MAX_SLEEP_MSEC; if (session->isClosed) msec = 100; /* on shutdown, call perform () more frequently */ if (msec > 0) { int usec; int max_fd; struct timeval t; fd_set r_fd_set, w_fd_set, c_fd_set; max_fd = 0; FD_ZERO (&r_fd_set); FD_ZERO (&w_fd_set); FD_ZERO (&c_fd_set); curl_multi_fdset (multi, &r_fd_set, &w_fd_set, &c_fd_set, &max_fd); if (msec > THREADFUNC_MAX_SLEEP_MSEC) msec = THREADFUNC_MAX_SLEEP_MSEC; usec = msec * 1000; t.tv_sec = usec / 1000000; t.tv_usec = usec % 1000000; tr_select (max_fd+1, &r_fd_set, &w_fd_set, &c_fd_set, &t); } /* call curl_multi_perform () */ do mcode = curl_multi_perform (multi, &unused); while (mcode == CURLM_CALL_MULTI_PERFORM); /* pump completed tasks from the multi */ while ((msg = curl_multi_info_read (multi, &unused))) { if ((msg->msg == CURLMSG_DONE) && (msg->easy_handle != NULL)) { double total_time; struct tr_web_task * task; long req_bytes_sent; CURL * e = msg->easy_handle; curl_easy_getinfo (e, CURLINFO_PRIVATE, (void*)&task); assert (e == task->curl_easy); curl_easy_getinfo (e, CURLINFO_RESPONSE_CODE, &task->code); curl_easy_getinfo (e, CURLINFO_REQUEST_SIZE, &req_bytes_sent); curl_easy_getinfo (e, CURLINFO_TOTAL_TIME, &total_time); task->did_connect = task->code>0 || req_bytes_sent>0; task->did_timeout = !task->code && (total_time >= task->timeout_secs); curl_multi_remove_handle (multi, e); tr_list_remove_data (&paused_easy_handles, e); curl_easy_cleanup (e); tr_runInEventThread (task->session, task_finish_func, task); --taskCount; } } } /* Discard any remaining tasks. * This is rare, but can happen on shutdown with unresponsive trackers. */ while (web->tasks != NULL) { task = web->tasks; web->tasks = task->next; dbgmsg ("Discarding task \"%s\"", task->url); task_free (task); } /* cleanup */ tr_list_free (&paused_easy_handles, NULL); curl_multi_cleanup (multi); tr_lockFree (web->taskLock); tr_free (web->cookie_filename); tr_free (web); session->web = NULL; }