static void tr_closeAllConnections( void * vsession ) { tr_session * session = vsession; tr_torrent * tor; int i, n; tr_torrent ** torrents; assert( tr_isSession( session ) ); tr_statsClose( session ); tr_sharedShuttingDown( session->shared ); tr_rpcClose( &session->rpcServer ); /* close the torrents. get the most active ones first so that * if we can't get them all closed in a reasonable amount of time, * at least we get the most important ones first. */ tor = NULL; n = session->torrentCount; torrents = tr_new( tr_torrent *, session->torrentCount ); for( i = 0; i < n; ++i ) torrents[i] = tor = tr_torrentNext( session, tor ); qsort( torrents, n, sizeof( tr_torrent* ), compareTorrentByCur ); for( i = 0; i < n; ++i ) tr_torrentFree( torrents[i] ); tr_free( torrents ); tr_peerMgrFree( session->peerMgr ); tr_trackerSessionClose( session ); tr_list_free( &session->blocklists, (TrListForeachFunc)_tr_blocklistFree ); tr_webClose( &session->web ); session->isClosed = TRUE; }
void tr_runInEventThread (tr_session * session, void func (void*), void * user_data) { assert (tr_isSession (session)); assert (session->events != NULL); if (tr_amInThread (session->events->thread)) { (func)(user_data); } else { int fd; char ch; ssize_t res_1; ssize_t res_2; tr_event_handle * e = session->events; struct tr_run_data data; tr_lockLock (e->lock); fd = e->fds[1]; ch = 'r'; res_1 = pipewrite (fd, &ch, 1); data.func = func; data.user_data = user_data; res_2 = pipewrite (fd, &data, sizeof (data)); tr_lockUnlock (e->lock); if ((res_1 == -1) || (res_2 == -1)) tr_logAddError ("Unable to write to libtransmisison event queue: %s", tr_strerror(errno)); } }
double tr_sessionGetRawSpeed( const tr_session * session, tr_direction dir ) { return tr_isSession( session ) ? tr_bandwidthGetPieceSpeed( session->bandwidth, 0, dir ) : 0.0; }
int tr_sessionCountTorrents( const tr_session * session ) { return tr_isSession( session ) ? session->torrentCount : 0; }
tr_bool tr_globalIsLocked( const tr_session * session ) { return tr_isSession( session ) && tr_lockHave( session->lock ); }
static void tr_sessionInitImpl( void * vdata ) { int64_t i; int64_t j; double d; tr_bool found; const char * str; tr_benc settings; char * filename; struct init_data * data = vdata; tr_benc * clientSettings = data->clientSettings; tr_session * session = data->session; assert( tr_amInEventThread( session ) ); assert( tr_bencIsDict( clientSettings ) ); dbgmsg( "tr_sessionInit: the session's top-level bandwidth object is %p", session->bandwidth ); tr_bencInitDict( &settings, 0 ); tr_sessionGetDefaultSettings( &settings ); tr_bencMergeDicts( &settings, clientSettings ); #ifndef WIN32 /* Don't exit when writing on a broken socket */ signal( SIGPIPE, SIG_IGN ); #endif found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_LIMIT_TORRENT, &i ); assert( found ); session->peerLimitPerTorrent = i; found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_MSGLEVEL, &i ); assert( found ); tr_setMessageLevel( i ); tr_setMessageQueuing( data->messageQueuingEnabled ); found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEX_ENABLED, &i ); assert( found ); session->isPexEnabled = i != 0; found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_ENCRYPTION, &i ); assert( found ); assert( tr_isEncryptionMode( i ) ); session->encryptionMode = i; found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PREALLOCATION, &i ); assert( found ); assert( tr_isPreallocationMode( i ) ); session->preallocationMode = i; found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_SOCKET_TOS, &i ); assert( found ); session->peerSocketTOS = i; found = tr_bencDictFindStr( &settings, TR_PREFS_KEY_DOWNLOAD_DIR, &str ); assert( found ); session->downloadDir = tr_strdup( str ); found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PROXY_ENABLED, &i ); assert( found ); session->isProxyEnabled = i != 0; found = tr_bencDictFindStr( &settings, TR_PREFS_KEY_PROXY, &str ); assert( found ); session->proxy = tr_strdup( str ); found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PROXY_PORT, &i ); assert( found ); session->proxyPort = i; found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PROXY_TYPE, &i ); assert( found ); session->proxyType = i; found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PROXY_AUTH_ENABLED, &i ); assert( found ); session->isProxyAuthEnabled = i != 0; found = tr_bencDictFindStr( &settings, TR_PREFS_KEY_PROXY_USERNAME, &str ); assert( found ); session->proxyUsername = tr_strdup( str ); found = tr_bencDictFindStr( &settings, TR_PREFS_KEY_PROXY_PASSWORD, &str ); assert( found ); session->proxyPassword = tr_strdup( str ); session->so_sndbuf = 1500 * 3; /* 3x MTU for most ethernet/wireless */ session->so_rcvbuf = 8192; tr_setConfigDir( session, data->configDir ); tr_trackerSessionInit( session ); assert( session->tracker != NULL ); session->peerMgr = tr_peerMgrNew( session ); found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_LAZY_BITFIELD, &i ); assert( found ); session->useLazyBitfield = i != 0; /* Initialize rate and file descripts controls */ found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_OPEN_FILE_LIMIT, &i ); assert( found ); session->openFileLimit = i; found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, &j ); assert( found ); tr_fdInit( session->openFileLimit, j ); /** *** random port **/ found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_PORT_RANDOM_ENABLED, &i ); assert( found ); session->isPortRandom = i != 0; found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_PORT_RANDOM_LOW, &i ); assert( found ); session->randomPortLow = i; found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_PORT_RANDOM_HIGH, &i ); assert( found ); session->randomPortHigh = i; found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PORT_FORWARDING, &i ) && tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_PORT, &j ); assert( found ); session->peerPort = session->isPortRandom ? getRandomPort( session ) : j; session->shared = tr_sharedInit( session, i, session->peerPort ); session->isPortSet = session->isPortRandom || j>0; /** **/ found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_UPLOAD_SLOTS_PER_TORRENT, &i ); assert( found ); session->uploadSlotsPerTorrent = i; found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_USPEED, &i ) && tr_bencDictFindInt( &settings, TR_PREFS_KEY_USPEED_ENABLED, &j ); assert( found ); tr_sessionSetSpeedLimit( session, TR_UP, i ); tr_sessionSetSpeedLimitEnabled( session, TR_UP, j ); found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_DSPEED, &i ) && tr_bencDictFindInt( &settings, TR_PREFS_KEY_DSPEED_ENABLED, &j ); assert( found ); tr_sessionSetSpeedLimit( session, TR_DOWN, i ); tr_sessionSetSpeedLimitEnabled( session, TR_DOWN, j ); found = tr_bencDictFindDouble( &settings, TR_PREFS_KEY_RATIO, &d ) && tr_bencDictFindInt( &settings, TR_PREFS_KEY_RATIO_ENABLED, &j ); assert( found ); tr_sessionSetRatioLimit( session, d ); tr_sessionSetRatioLimited( session, j ); /* initialize the blocklist */ filename = tr_buildPath( session->configDir, "blocklists", NULL ); tr_mkdirp( filename, 0777 ); tr_free( filename ); found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_BLOCKLIST_ENABLED, &i ); assert( found ); session->isBlocklistEnabled = i; loadBlocklists( session ); session->rpcServer = tr_rpcInit( session, &settings ); tr_bencFree( &settings ); assert( tr_isSession( session ) ); /* first %s is the application name second %s is the version number */ tr_inf( _( "%s %s started" ), TR_NAME, LONG_VERSION_STRING ); tr_statsInit( session ); session->web = tr_webInit( session ); metainfoLookupRescan( session ); session->isWaiting = FALSE; dbgmsg( "returning session %p; session->tracker is %p", session, session->tracker ); }
/* returns an fd on success, or a -1 on failure and sets errno */ int tr_fdFileCheckout( tr_session * session, int torrentId, tr_file_index_t fileNum, const char * filename, tr_bool doWrite, tr_preallocation_mode preallocationMode, uint64_t desiredFileSize ) { int i, winner = -1; struct tr_fdInfo * gFd; struct tr_openfile * o; assert( tr_isSession( session ) ); assert( session->fdInfo != NULL ); assert( torrentId > 0 ); assert( filename && *filename ); assert( tr_isBool( doWrite ) ); gFd = session->fdInfo; dbgmsg( "looking for file '%s', writable %c", filename, doWrite ? 'y' : 'n' ); /* is it already open? */ for( i=0; i<gFd->openFileLimit; ++i ) { o = &gFd->openFiles[i]; if( torrentId != o->torrentId ) continue; if( fileNum != o->fileNum ) continue; if( !fileIsOpen( o ) ) continue; if( doWrite && !o->isWritable ) { dbgmsg( "found it! it's open and available, but isn't writable. closing..." ); TrCloseFile( o ); 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 ) { time_t date = tr_time( ) + 1; /* look for the file that's been open longest */ for( i=0; i<gFd->openFileLimit; ++i ) { o = &gFd->openFiles[i]; if( !fileIsOpen( o ) ) { winner = i; dbgmsg( "found an empty slot in %d", winner ); break; } if( date > o->date ) { date = o->date; winner = i; } } assert( winner >= 0 ); if( fileIsOpen( &gFd->openFiles[winner] ) ) { dbgmsg( "closing file \"%s\"", gFd->openFiles[winner].filename ); TrCloseFile( &gFd->openFiles[winner] ); } } assert( winner >= 0 ); o = &gFd->openFiles[winner]; if( !fileIsOpen( o ) ) { const int err = TrOpenFile( session, winner, filename, doWrite, preallocationMode, desiredFileSize ); if( err ) { 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->torrentId = torrentId; o->fileNum = fileNum; o->date = tr_time( ); return o->fd; }
/** * returns 0 on success, or an errno value on failure. * errno values include ENOENT if the parent folder doesn't exist, * plus the errno values set by tr_mkdirp() and open(). */ static int TrOpenFile( tr_session * session, int i, const char * filename, tr_bool doWrite, tr_preallocation_mode preallocationMode, uint64_t desiredFileSize ) { int flags; struct stat sb; tr_bool alreadyExisted; struct tr_openfile * file; assert( tr_isSession( session ) ); assert( session->fdInfo != NULL ); file = &session->fdInfo->openFiles[i]; /* create subfolders, if any */ if( doWrite ) { char * dir = tr_dirname( filename ); const int err = tr_mkdirp( dir, 0777 ) ? errno : 0; if( err ) { tr_err( _( "Couldn't create \"%1$s\": %2$s" ), dir, tr_strerror( err ) ); tr_free( dir ); return err; } tr_free( dir ); } alreadyExisted = !stat( filename, &sb ) && S_ISREG( sb.st_mode ); if( doWrite && !alreadyExisted && ( preallocationMode == TR_PREALLOCATE_FULL ) ) if( preallocateFileFull( filename, desiredFileSize ) ) tr_dbg( _( "Preallocated file \"%s\"" ), filename ); /* open the file */ flags = doWrite ? ( O_RDWR | O_CREAT ) : O_RDONLY; #ifdef O_SEQUENTIAL flags |= O_SEQUENTIAL; #endif #ifdef O_LARGEFILE flags |= O_LARGEFILE; #endif #ifdef WIN32 flags |= O_BINARY; #endif file->fd = open( filename, flags, 0666 ); if( file->fd == -1 ) { const int err = errno; tr_err( _( "Couldn't open \"%1$s\": %2$s" ), filename, tr_strerror( err ) ); return err; } /* If the file already exists and it's too large, truncate it. * This is a fringe case that happens if a torrent's been updated * and one of the updated torrent's files is smaller. * http://trac.transmissionbt.com/ticket/2228 * https://bugs.launchpad.net/ubuntu/+source/transmission/+bug/318249 */ if( alreadyExisted && ( desiredFileSize < (uint64_t)sb.st_size ) ) ftruncate( file->fd, desiredFileSize ); if( doWrite && !alreadyExisted && ( preallocationMode == TR_PREALLOCATE_SPARSE ) ) preallocateFileSparse( file->fd, desiredFileSize ); #ifdef HAVE_POSIX_FADVISE /* this doubles the OS level readahead buffer, which in practice * turns out to be a good thing, because many (most?) clients request * chunks of blocks in order. * It's okay for this to fail silently, so don't let it affect errno */ { const int err = errno; posix_fadvise( file->fd, 0, 0, POSIX_FADV_SEQUENTIAL ); errno = err; } #endif #if defined( SYS_DARWIN ) /** * 1. Enable readahead for reasons described above w/POSIX_FADV_SEQUENTIAL. * * 2. Disable OS-level caching due to user reports of adverse effects of * excessive inactive memory. However this is experimental because * previous attempts at this have *also* had adverse effects (see r8198) * * It's okay for this to fail silently, so don't let it affect errno */ { const int err = errno; fcntl( file->fd, F_NOCACHE, 1 ); fcntl( file->fd, F_RDAHEAD, 1 ); errno = err; } #endif return 0; }
static void canReadWrapper( tr_peerIo * io ) { tr_bool err = 0; tr_bool done = 0; tr_session * session; dbgmsg( io, "canRead" ); assert( tr_isPeerIo( io ) ); assert( tr_isSession( io->session ) ); tr_peerIoRef( io ); session = io->session; /* try to consume the input buffer */ if( io->canRead ) { tr_sessionLock( session ); while( !done && !err ) { size_t piece = 0; const size_t oldLen = EVBUFFER_LENGTH( io->inbuf ); const int ret = io->canRead( io, io->userData, &piece ); const size_t used = oldLen - EVBUFFER_LENGTH( io->inbuf ); assert( tr_isPeerIo( io ) ); if( piece || (piece!=used) ) { const uint64_t now = tr_time_msec( ); if( piece ) tr_bandwidthUsed( &io->bandwidth, TR_DOWN, piece, TRUE, now ); if( used != piece ) tr_bandwidthUsed( &io->bandwidth, TR_DOWN, used - piece, FALSE, now ); } switch( ret ) { case READ_NOW: if( EVBUFFER_LENGTH( io->inbuf ) ) continue; done = 1; break; case READ_LATER: done = 1; break; case READ_ERR: err = 1; break; } assert( tr_isPeerIo( io ) ); } tr_sessionUnlock( session ); } /* keep the iobuf's excess capacity from growing too large */ if( EVBUFFER_LENGTH( io->inbuf ) == 0 ) { evbuffer_free( io->inbuf ); io->inbuf = evbuffer_new( ); } assert( tr_isPeerIo( io ) ); tr_peerIoUnref( io ); }