void tr_sessionClose( tr_session * session ) { int i; const int maxwait_msec = SHUTDOWN_MAX_SECONDS * 1000; const uint64_t deadline = tr_date( ) + maxwait_msec; assert( tr_isSession( session ) ); dbgmsg( "shutting down transmission session %p", session ); /* close the session */ tr_runInEventThread( session, tr_closeAllConnections, session ); while( !session->isClosed && !deadlineReached( deadline ) ) { dbgmsg( "waiting for the shutdown commands to run in the main thread" ); tr_wait( 100 ); } /* "shared" and "tracker" have live sockets, * so we need to keep the transmission thread alive * for a bit while they tell the router & tracker * that we're closing now */ while( ( session->shared || session->tracker ) && !deadlineReached( deadline ) ) { dbgmsg( "waiting on port unmap (%p) or tracker (%p)", session->shared, session->tracker ); tr_wait( 100 ); } tr_fdClose( ); /* close the libtransmission thread */ tr_eventClose( session ); while( session->events && !deadlineReached( deadline ) ) { dbgmsg( "waiting for the libevent thread to shutdown cleanly" ); tr_wait( 100 ); } /* free the session memory */ tr_bandwidthFree( session->bandwidth ); tr_lockFree( session->lock ); for( i = 0; i < session->metainfoLookupCount; ++i ) tr_free( session->metainfoLookup[i].filename ); tr_free( session->metainfoLookup ); tr_free( session->tag ); tr_free( session->configDir ); tr_free( session->resumeDir ); tr_free( session->torrentDir ); tr_free( session->downloadDir ); tr_free( session->proxy ); tr_free( session->proxyUsername ); tr_free( session->proxyPassword ); tr_free( session ); }
tr_session * tr_sessionInit( const char * tag, const char * configDir, tr_bool messageQueuingEnabled, tr_benc * clientSettings ) { tr_session * session; struct init_data data; assert( tr_bencIsDict( clientSettings ) ); /* initialize the bare skeleton of the session object */ session = tr_new0( tr_session, 1 ); session->bandwidth = tr_bandwidthNew( session, NULL ); session->lock = tr_lockNew( ); session->tag = tr_strdup( tag ); session->magicNumber = SESSION_MAGIC_NUMBER; /* start the libtransmission thread */ tr_netInit( ); /* must go before tr_eventInit */ tr_eventInit( session ); assert( session->events != NULL ); /* run the rest in the libtransmission thread */ session->isWaiting = TRUE; data.session = session; data.configDir = configDir; data.messageQueuingEnabled = messageQueuingEnabled; data.clientSettings = clientSettings; tr_runInEventThread( session, tr_sessionInitImpl, &data ); while( session->isWaiting ) tr_wait( 100 ); return session; }
/*********************************************************************** * tr_netResolveThreadClose *********************************************************************** * Notices the gethostbyname thread that is should terminate. Doesn't * wait until it does, in case it is stuck in a resolution: we let it * die and clean itself up. **********************************************************************/ void tr_netResolveThreadClose() { tr_lockLock( &resolveLock ); resolveDie = 1; tr_lockUnlock( &resolveLock ); tr_condSignal( &resolveCond ); tr_wait( 200 ); }
void tr_eventInit( tr_session * session ) { tr_event_handle * eh; session->events = NULL; eh = tr_new0( tr_event_handle, 1 ); eh->lock = tr_lockNew( ); pipe( eh->fds ); eh->session = session; eh->thread = tr_threadNew( libeventThreadFunc, eh ); /* wait until the libevent thread is running */ while( session->events == NULL ) tr_wait( 100 ); }
/* returns an fd on success, or a -1 on failure and sets errno */ int tr_fdFileCheckout( const char * folder, const char * torrentFile, int doWrite, int doPreallocate, uint64_t desiredFileSize ) { int i, winner = -1; struct tr_openfile * o; char * filename; assert( folder && *folder ); assert( torrentFile && *torrentFile ); assert( doWrite == 0 || doWrite == 1 ); filename = tr_buildPath( folder, torrentFile, NULL ); dbgmsg( "looking for file '%s', writable %c", filename, doWrite ? 'y' : 'n' ); tr_lockLock( gFd->lock ); /* Is it already open? */ for( i = 0; i < TR_MAX_OPEN_FILES; ++i ) { o = &gFd->open[i]; if( !fileIsOpen( o ) ) continue; if( strcmp( filename, o->filename ) ) continue; if( fileIsCheckedOut( o ) ) { dbgmsg( "found it! it's open, but checked out. waiting..." ); tr_lockUnlock( gFd->lock ); tr_wait( 200 ); tr_lockLock( gFd->lock ); i = -1; /* reloop */ continue; } if( doWrite && !o->isWritable ) { dbgmsg( "found it! it's open and available, but isn't writable. closing..." ); TrCloseFile( i ); break; } dbgmsg( "found it! it's ready for use!" ); winner = i; break; } dbgmsg( "it's not already open. looking for an open slot or an old file." ); while( winner < 0 ) { uint64_t date = tr_date( ) + 1; /* look for the file that's been open longest */ for( i = 0; i < TR_MAX_OPEN_FILES; ++i ) { o = &gFd->open[i]; if( !fileIsOpen( o ) ) { winner = i; dbgmsg( "found an empty slot in %d", winner ); break; } if( date > o->date ) { date = o->date; winner = i; } } if( winner >= 0 ) { if( fileIsOpen( &gFd->open[winner] ) ) { dbgmsg( "closing file '%s', slot #%d", gFd->open[winner].filename, winner ); TrCloseFile( winner ); } } else { dbgmsg( "everything's full! waiting for someone else to finish something" ); tr_lockUnlock( gFd->lock ); tr_wait( 200 ); tr_lockLock( gFd->lock ); } } assert( winner >= 0 ); o = &gFd->open[winner]; if( !fileIsOpen( o ) ) { const int err = TrOpenFile( winner, folder, torrentFile, doWrite, doPreallocate, desiredFileSize ); if( err ) { tr_lockUnlock( gFd->lock ); tr_free( filename ); errno = err; return -1; } dbgmsg( "opened '%s' in slot %d, doWrite %c", filename, winner, doWrite ? 'y' : 'n' ); tr_strlcpy( o->filename, filename, sizeof( o->filename ) ); o->isWritable = doWrite; } dbgmsg( "checking out '%s' in slot %d", filename, winner ); o->isCheckedOut = 1; o->closeWhenDone = 0; o->date = tr_date( ); tr_free( filename ); tr_lockUnlock( gFd->lock ); return o->fd; }
int tr_trackerScrape( tr_torrent_t * tor, int * seeders, int * leechers ) { tr_info_t * inf = &tor->info; int s, i, ret; uint8_t buf[1024]; benc_val_t scrape, * val1, * val2; struct in_addr addr; uint64_t date; int pos, len; tr_resolve_t * resolve; if( !tor->scrape[0] ) { /* scrape not supported */ return 1; } resolve = tr_netResolveInit( inf->trackerAddress ); for( date = tr_date();; ) { ret = tr_netResolvePulse( resolve, &addr ); if( ret == TR_RESOLVE_OK ) { tr_netResolveClose( resolve ); break; } if( ret == TR_RESOLVE_ERROR || ( ret == TR_RESOLVE_WAIT && tr_date() > date + 10000 ) ) { fprintf( stderr, "Could not resolve %s\n", inf->trackerAddress ); tr_netResolveClose( resolve ); return 1; } tr_wait( 10 ); } s = tr_netOpen( addr, htons( inf->trackerPort ) ); if( s < 0 ) { return 1; } len = snprintf( (char *) buf, sizeof( buf ), "GET %s?info_hash=%s HTTP/1.1\r\n" "Host: %s\r\n" "Connection: close\r\n\r\n", tor->scrape, tor->hashString, inf->trackerAddress ); for( date = tr_date();; ) { ret = tr_netSend( s, buf, len ); if( ret & TR_NET_CLOSE ) { fprintf( stderr, "Could not connect to tracker\n" ); tr_netClose( s ); return 1; } else if( ret & TR_NET_BLOCK ) { if( tr_date() > date + 10000 ) { fprintf( stderr, "Could not connect to tracker\n" ); tr_netClose( s ); return 1; } } else { break; } tr_wait( 10 ); } pos = 0; for( date = tr_date();; ) { ret = tr_netRecv( s, &buf[pos], sizeof( buf ) - pos ); if( ret & TR_NET_CLOSE ) { break; } else if( ret & TR_NET_BLOCK ) { if( tr_date() > date + 10000 ) { fprintf( stderr, "Could not read from tracker\n" ); tr_netClose( s ); return 1; } } else { pos += ret; } tr_wait( 10 ); } if( pos < 1 ) { fprintf( stderr, "Could not read from tracker\n" ); tr_netClose( s ); return 1; } for( i = 0; i < pos - 8; i++ ) { if( !memcmp( &buf[i], "d5:files", 8 ) ) { break; } } if( i >= pos - 8 ) { return 1; } if( tr_bencLoad( &buf[i], pos - i, &scrape, NULL ) ) { return 1; } val1 = tr_bencDictFind( &scrape, "files" ); if( !val1 ) { return 1; } val1 = &val1->val.l.vals[1]; if( !val1 ) { return 1; } val2 = tr_bencDictFind( val1, "complete" ); if( !val2 ) { return 1; } *seeders = val2->val.i; val2 = tr_bencDictFind( val1, "incomplete" ); if( !val2 ) { return 1; } *leechers = val2->val.i; tr_bencFree( &scrape ); return 0; }
static tr_bool verifyTorrent( tr_torrent * tor, tr_bool * stopFlag ) { SHA_CTX sha; int fd = -1; int64_t filePos = 0; tr_bool changed = 0; tr_bool hadPiece = 0; uint32_t piecePos = 0; uint32_t pieceBytesRead = 0; tr_file_index_t fileIndex = 0; tr_piece_index_t pieceIndex = 0; const int64_t buflen = tor->info.pieceSize; uint8_t * buffer = tr_new( uint8_t, buflen ); #ifdef STOPWATCH time_t now = time( NULL ); #endif SHA1_Init( &sha ); while( !*stopFlag && ( pieceIndex < tor->info.pieceCount ) ) { int64_t leftInPiece; int64_t leftInFile; int64_t bytesThisPass; const tr_file * file = &tor->info.files[fileIndex]; /* if we're starting a new piece... */ if( piecePos == 0 ) { hadPiece = tr_cpPieceIsComplete( &tor->completion, pieceIndex ); /* fprintf( stderr, "starting piece %d of %d\n", (int)pieceIndex, (int)tor->info.pieceCount ); */ } /* if we're starting a new file... */ if( !filePos && (fd<0) ) { char * filename = tr_buildPath( tor->downloadDir, file->name, NULL ); fd = tr_open_file_for_scanning( filename ); /* fprintf( stderr, "opening file #%d (%s) -- %d\n", fileIndex, filename, fd ); */ tr_free( filename ); } /* figure out how much we can read this pass */ leftInPiece = tr_torPieceCountBytes( tor, pieceIndex ) - piecePos; leftInFile = file->length - filePos; bytesThisPass = MIN( leftInFile, leftInPiece ); bytesThisPass = MIN( bytesThisPass, buflen ); /* fprintf( stderr, "reading this pass: %d\n", (int)bytesThisPass ); */ /* read a bit */ if( (fd>=0) && tr_lseek( fd, filePos, SEEK_SET ) != -1 ) { const int64_t numRead = read( fd, buffer, bytesThisPass ); if( numRead > 0 ) pieceBytesRead += numRead; if( numRead == bytesThisPass ) SHA1_Update( &sha, buffer, numRead ); } /* move our offsets */ leftInPiece -= bytesThisPass; leftInFile -= bytesThisPass; piecePos += bytesThisPass; filePos += bytesThisPass; /* if we're finishing a piece... */ if( leftInPiece == 0 ) { tr_bool hasPiece; uint8_t hash[SHA_DIGEST_LENGTH]; SHA1_Final( hash, &sha ); hasPiece = !memcmp( hash, tor->info.pieces[pieceIndex].hash, SHA_DIGEST_LENGTH ); /* fprintf( stderr, "do the hashes match? %s\n", (hasPiece?"yes":"no") ); */ if( hasPiece ) { tr_torrentSetHasPiece( tor, pieceIndex, TRUE ); if( !hadPiece ) changed = TRUE; } else if( hadPiece ) { tr_torrentSetHasPiece( tor, pieceIndex, FALSE ); changed = TRUE; } tr_torrentSetPieceChecked( tor, pieceIndex, TRUE ); tor->anyDate = time( NULL ); /* going full-throttle on a verify can choke other processes' * disk IO, so wait a fwe msec between pieces. * The msec is arbitrary, and the "if" clause is to make sure we * don't slow down verification of files that don't exist */ if( pieceBytesRead == tr_torPieceCountBytes( tor, pieceIndex ) ) tr_wait( 50 ); SHA1_Init( &sha ); ++pieceIndex; piecePos = 0; pieceBytesRead = 0; } /* if we're finishing a file... */ if( leftInFile == 0 ) { /* fprintf( stderr, "closing file\n" ); */ if( fd >= 0 ) { tr_close_file( fd ); fd = -1; } ++fileIndex; filePos = 0; } } /* cleanup */ if( fd >= 0 ) tr_close_file( fd ); tr_free( buffer ); #ifdef STOPWATCH { time_t now2 = time( NULL ); fprintf( stderr, "it took %d seconds to verify %"PRIu64" bytes (%"PRIu64" bytes per second)\n", (int)(now2-now), tor->info.totalSize, (uint64_t)(tor->info.totalSize/(1+(now2-now))) ); } #endif return changed; }