From de515ee33950f15f71892ed3ac19aef4fbb22abe Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 18 Jan 2008 19:13:32 +0000 Subject: [PATCH] if the torrent's download path doesn't exist, don't create it -- it's might be a removeable disk that got unplugged. --- libtransmission/fdlimit.c | 50 ++++++++++-------- libtransmission/fdlimit.h | 23 ++++++--- libtransmission/inout.c | 93 ++++++++++++++++++---------------- libtransmission/inout.h | 23 +++++---- libtransmission/ipcparse.c | 22 ++++---- libtransmission/peer-mgr.c | 14 ++--- libtransmission/peer-msgs.c | 46 +++++++---------- libtransmission/peer-msgs.h | 4 +- libtransmission/torrent.c | 36 ++++++++++--- libtransmission/torrent.h | 3 ++ libtransmission/transmission.h | 92 +++++++++++++++++++++++---------- libtransmission/utils.c | 18 +++++-- libtransmission/utils.h | 2 +- 13 files changed, 259 insertions(+), 167 deletions(-) diff --git a/libtransmission/fdlimit.c b/libtransmission/fdlimit.c index fb5736d91..2c043a0cc 100644 --- a/libtransmission/fdlimit.c +++ b/libtransmission/fdlimit.c @@ -127,19 +127,29 @@ static struct tr_fd_s * gFd = NULL; **** ***/ -static int -TrOpenFile( int i, const char * filename, int write ) +static tr_errno +TrOpenFile( int i, + const char * folder, + const char * torrentFile, + int write ) { struct tr_openfile * file = &gFd->open[i]; int flags; + char filename[MAX_PATH_LENGTH]; + struct stat sb; + + /* confirm the parent folder exists */ + if( stat( folder, &sb ) || !S_ISDIR( sb.st_mode ) ) + return TR_ERROR_IO_PARENT; /* create subfolders, if any */ + tr_buildPath ( filename, sizeof(filename), folder, torrentFile, NULL ); if( write ) { char * tmp = tr_strdup( filename ); - const int val = tr_mkdirp( dirname(tmp), 0777 ); + const int err = tr_mkdirp( dirname(tmp), 0777 ) ? errno : 0; tr_free( tmp ); - if( val ) - return tr_ioErrorFromErrno( ); + if( err ) + return tr_ioErrorFromErrno( err ); } /* open the file */ @@ -150,19 +160,14 @@ TrOpenFile( int i, const char * filename, int write ) #ifdef WIN32 flags |= O_BINARY; #endif - errno = 0; file->fd = open( filename, flags, 0666 ); - if( file->fd < 0 ) { - if( errno ) { - tr_err( "Couldn't open '%s': %s", filename, strerror(errno) ); - return tr_ioErrorFromErrno(); - } else { - tr_err( "Couldn't open '%s'", filename ); - return TR_ERROR_IO_OTHER; - } + if( file->fd == -1 ) { + const int err = errno; + tr_err( "Couldn't open '%s': %s", filename, strerror(err) ); + return tr_ioErrorFromErrno( err ); } - return TR_OK; + return 0; } static int @@ -192,14 +197,19 @@ fileIsCheckedOut( const struct tr_openfile * o ) } int -tr_fdFileCheckout( const char * filename, int write ) +tr_fdFileCheckout( const char * folder, + const char * torrentFile, + int write ) { int i, winner = -1; struct tr_openfile * o; + char filename[MAX_PATH_LENGTH]; - assert( filename && *filename ); + assert( folder && *folder ); + assert( torrentFile && *torrentFile ); assert( write==0 || write==1 ); + tr_buildPath ( filename, sizeof(filename), folder, torrentFile, NULL ); dbgmsg( "looking for file '%s', writable %c", filename, write?'y':'n' ); tr_lockLock( gFd->lock ); @@ -274,10 +284,10 @@ tr_fdFileCheckout( const char * filename, int write ) o = &gFd->open[winner]; if( !fileIsOpen( o ) ) { - const int ret = TrOpenFile( winner, filename, write ); - if( ret ) { + const tr_errno err = TrOpenFile( winner, folder, torrentFile, write ); + if( err ) { tr_lockUnlock( gFd->lock ); - return ret; + return err; } dbgmsg( "opened '%s' in slot %d, write %c", filename, winner, write?'y':'n' ); diff --git a/libtransmission/fdlimit.h b/libtransmission/fdlimit.h index 240373534..053dee940 100644 --- a/libtransmission/fdlimit.h +++ b/libtransmission/fdlimit.h @@ -36,19 +36,26 @@ void tr_fdClose( void ); /** * Returns an fd to the specified filename. * - * A small repository of open files is kept to avoid the overhead of continually - * opening and closing the same files when writing piece data during download. - * It's also used to ensure that only one client uses the file at a time. - * Clients must check out a file to use it, then return it, like a library, when done. + * A small pool of open files is kept to avoid the overhead of + * continually opening and closing the same files when downloading + * piece data. It's also used to ensure only one caller can + * write to the file at a time. Callers check out a file, use it, + * and then check it back in via tr_fdFileReturn() when done. * - * if write is nonzero and dirname(filename) doesn't exist, dirname is created. - * if write is nonzero and filename doesn't exist, filename is created. - * returns the fd if successful; otherwise, one of TR_ERROR_IO_* + * - if `folder' doesn't exist, TR_ERROR_IO_PARENT is returned. + * - if doWrite is true, subfolders in torrentFile are created if necessary. + * - if doWrite is true, the target file is created if necessary. + * + * on success, a file descriptor >= 0 is returned. + * on failure, a negative number corresponding to tr_errno is returned. * * @see tr_fdFileReturn * @see tr_fdFileClose + * @see tr_errno */ -int tr_fdFileCheckout( const char * filename, int write ); +int tr_fdFileCheckout( const char * folder, + const char * torrentFile, + int doWrite ); /** * Returns an fd from tr_fdFileCheckout() so that other clients may borrow it. diff --git a/libtransmission/inout.c b/libtransmission/inout.c index c0bb62256..b6c40d00f 100644 --- a/libtransmission/inout.c +++ b/libtransmission/inout.c @@ -41,7 +41,7 @@ enum { TR_IO_READ, TR_IO_WRITE }; -static int +static tr_errno readOrWriteBytes( const tr_torrent * tor, int ioMode, int fileIndex, @@ -56,7 +56,7 @@ readOrWriteBytes( const tr_torrent * tor, char path[MAX_PATH_LENGTH]; struct stat sb; int fd = -1; - int ret; + int err; int fileExists; assert( 0<=fileIndex && fileIndexfileCount ); @@ -68,27 +68,28 @@ readOrWriteBytes( const tr_torrent * tor, if( !file->length ) return 0; - else if ((ioMode==TR_IO_READ) && !fileExists ) /* does file exist? */ - ret = tr_ioErrorFromErrno (); - else if ((fd = tr_fdFileCheckout ( path, ioMode==TR_IO_WRITE )) < 0) - ret = fd; - else if( lseek( fd, (off_t)fileOffset, SEEK_SET ) == ((off_t)-1) ) - ret = TR_ERROR_IO_OTHER; - else if( func( fd, buf, buflen ) != buflen ) - ret = tr_ioErrorFromErrno( ); - else - ret = TR_OK; - if((ret==TR_OK) && (ioMode==TR_IO_WRITE) && !fileExists ) + if ((ioMode==TR_IO_READ) && !fileExists ) /* does file exist? */ + err = tr_ioErrorFromErrno( errno ); + else if ((fd = tr_fdFileCheckout ( tor->destination, file->name, ioMode==TR_IO_WRITE )) < 0) + err = fd; + else if( lseek( fd, (off_t)fileOffset, SEEK_SET ) == ((off_t)-1) ) + err = tr_ioErrorFromErrno( errno ); + else if( func( fd, buf, buflen ) != buflen ) + err = tr_ioErrorFromErrno( errno ); + else + err = 0; + + if( !err && !fileExists && (ioMode==TR_IO_WRITE) ) tr_statsFileCreated( tor->handle ); if( fd >= 0 ) tr_fdFileReturn( fd ); - return ret; + return err; } -static void +static tr_errno findFileLocation( const tr_torrent * tor, int pieceIndex, int pieceOffset, @@ -100,10 +101,12 @@ findFileLocation( const tr_torrent * tor, int i; uint64_t piecePos = ((uint64_t)pieceIndex * info->pieceSize) + pieceOffset; - assert( 0<=pieceIndex && pieceIndex < info->pieceCount ); - assert( 0<=tor->info.pieceSize ); - assert( pieceOffset < tr_torPieceCountBytes( tor, pieceIndex ) ); - assert( piecePos < info->totalSize ); + if( pieceIndex < 0 || pieceIndex >= info->pieceCount ) + return TR_ERROR_ASSERT; + if( pieceOffset >= tr_torPieceCountBytes( tor, pieceIndex ) ) + return TR_ERROR_ASSERT; + if( piecePos >= info->totalSize ) + return TR_ERROR_ASSERT; for( i=0; info->files[i].length<=piecePos; ++i ) piecePos -= info->files[i].length; @@ -113,45 +116,42 @@ findFileLocation( const tr_torrent * tor, assert( 0<=*fileIndex && *fileIndexfileCount ); assert( *fileOffset < info->files[i].length ); + return 0; } #ifdef WIN32 -static int +static tr_errno ensureMinimumFileSize( const tr_torrent * tor, int fileIndex, uint64_t minBytes ) { int fd; - int ret; + tr_errno err; struct stat sb; const tr_file * file = &tor->info.files[fileIndex]; - char path[MAX_PATH_LENGTH]; assert( 0<=fileIndex && fileIndexinfo.fileCount ); assert( minBytes <= file->length ); - tr_buildPath( path, sizeof(path), tor->destination, file->name, NULL ); - - fd = tr_fdFileCheckout( path, TRUE ); - if( fd < 0 ) /* bad fd */ - ret = fd; + if(( fd = tr_fdFileCheckout( tor->destination, file->name, TRUE )) < 0 ) + err = fd; else if (fstat (fd, &sb) ) /* how big is the file? */ - ret = tr_ioErrorFromErrno (); + err = tr_ioErrorFromErrno( errno ); else if (sb.st_size >= (off_t)minBytes) /* already big enough */ - ret = TR_OK; - else if (!ftruncate( fd, minBytes )) /* grow it */ - ret = TR_OK; + err = 0; + else if ( !ftruncate( fd, minBytes ) ) /* grow it */ + err = 0; else /* couldn't grow it */ - ret = tr_ioErrorFromErrno (); + err = tr_ioErrorFromErrno( errno ); if( fd >= 0 ) tr_fdFileReturn( fd ); - return ret; + return err; } #endif -static int +static tr_errno readOrWritePiece( tr_torrent * tor, int ioMode, int pieceIndex, @@ -159,28 +159,31 @@ readOrWritePiece( tr_torrent * tor, uint8_t * buf, size_t buflen ) { - int ret = 0; + tr_errno err = 0; int fileIndex; uint64_t fileOffset; const tr_info * info = &tor->info; - assert( 0<=pieceIndex && pieceIndexinfo.pieceCount ); - assert( buflen <= (size_t) tr_torPieceCountBytes( tor, pieceIndex ) ); + if( pieceIndex < 0 || pieceIndex >= tor->info.pieceCount ) + err = TR_ERROR_ASSERT; + else if( buflen > ( size_t ) tr_torPieceCountBytes( tor, pieceIndex ) ) + err = TR_ERROR_ASSERT; - findFileLocation ( tor, pieceIndex, pieceOffset, &fileIndex, &fileOffset ); + if( !err ) + err = findFileLocation ( tor, pieceIndex, pieceOffset, &fileIndex, &fileOffset ); - while( buflen && !ret ) + while( buflen && !err ) { const tr_file * file = &info->files[fileIndex]; const uint64_t bytesThisPass = MIN( buflen, file->length - fileOffset ); #ifdef WIN32 if( ioMode == TR_IO_WRITE ) - ret = ensureMinimumFileSize( tor, fileIndex, + err = ensureMinimumFileSize( tor, fileIndex, fileOffset + bytesThisPass ); - if( !ret ) + if( !err ) #endif - ret = readOrWriteBytes( tor, ioMode, + err = readOrWriteBytes( tor, ioMode, fileIndex, fileOffset, buf, bytesThisPass ); buf += bytesThisPass; buflen -= bytesThisPass; @@ -188,10 +191,10 @@ readOrWritePiece( tr_torrent * tor, fileOffset = 0; } - return ret; + return err; } -int +tr_errno tr_ioRead( tr_torrent * tor, int pieceIndex, int begin, @@ -201,7 +204,7 @@ tr_ioRead( tr_torrent * tor, return readOrWritePiece( tor, TR_IO_READ, pieceIndex, begin, buf, len ); } -int +tr_errno tr_ioWrite( tr_torrent * tor, int pieceIndex, int begin, diff --git a/libtransmission/inout.h b/libtransmission/inout.h index 4b04ecdfb..82a6033a1 100644 --- a/libtransmission/inout.h +++ b/libtransmission/inout.h @@ -27,18 +27,19 @@ struct tr_torrent; -typedef struct tr_io tr_io; - -/*********************************************************************** - * tr_ioRead, tr_ioWrite - *********************************************************************** - * Reads or writes the block specified by the piece index, the offset in - * that piece and the size of the block. Returns 0 if successful, - * TR_ERROR_ASSERT if the parameters are incorrect, one of the - * TR_ERROR_IO_* otherwise. - **********************************************************************/ +/** + * Reads the block specified by the piece index, offset, and length. + * @return 0 on success, TR_ERROR_ASSERT if the arguments are incorrect, + * or TR_ERROR_IO_* otherwise. + */ int tr_ioRead ( struct tr_torrent*, int index, int begin, int len, uint8_t * ); -int tr_ioWrite ( struct tr_torrent *, int index, int begin, int len, const uint8_t * ); + +/** + * Writes the block specified by the piece index, offset, and length. + * @return 0 on success, TR_ERROR_ASSERT if the arguments are incorrect, + * or TR_ERROR_IO_* otherwise. + */ +tr_errno tr_ioWrite ( struct tr_torrent *, int index, int begin, int len, const uint8_t * ); /* hashes the specified piece and updates the completion accordingly. */ int tr_ioHash ( tr_torrent*, int piece ); diff --git a/libtransmission/ipcparse.c b/libtransmission/ipcparse.c index 8be4fbc63..fa450309d 100644 --- a/libtransmission/ipcparse.c +++ b/libtransmission/ipcparse.c @@ -712,7 +712,7 @@ ipc_addstat( benc_val_t * list, int tor, { benc_val_t * dict, * item; int ii, used; - unsigned int error; + tr_errno error; /* always send torrent id */ types |= IPC_ST_ID; @@ -763,38 +763,42 @@ ipc_addstat( benc_val_t * list, int tor, { tr_bencInitStr( item, "", -1, 1 ); } - else if( TR_ERROR_ISSET( TR_ERROR_ASSERT, error ) ) + else if( error == TR_ERROR_ASSERT ) { tr_bencInitStr( item, "assert", -1, 1 ); } - else if( TR_ERROR_ISSET( TR_ERROR_IO_PERMISSIONS, error ) ) + else if( error == TR_ERROR_IO_PERMISSIONS ) { tr_bencInitStr( item, "io-permissions", -1, 1 ); } - else if( TR_ERROR_ISSET( TR_ERROR_IO_SPACE, error ) ) + else if( error == TR_ERROR_IO_SPACE ) { tr_bencInitStr( item, "io-space", -1, 1 ); } - else if( TR_ERROR_ISSET( TR_ERROR_IO_FILE_TOO_BIG, error ) ) + else if( error == TR_ERROR_IO_FILE_TOO_BIG ) { tr_bencInitStr( item, "io-file-too-big", -1, 1 ); } - else if( TR_ERROR_ISSET( TR_ERROR_IO_OPEN_FILES, error ) ) + else if( error == TR_ERROR_IO_OPEN_FILES ) { tr_bencInitStr( item, "io-open-files", -1, 1 ); } - else if( TR_ERROR_ISSET( TR_ERROR_IO_MASK, error ) ) + else if( TR_ERROR_IS_IO( error ) ) { tr_bencInitStr( item, "io-other", -1, 1 ); } - else if( TR_ERROR_ISSET( TR_ERROR_TC_ERROR, error ) ) + else if( error == TR_ERROR_TC_ERROR ) { tr_bencInitStr( item, "tracker-error", -1, 1 ); } - else if( TR_ERROR_ISSET( TR_ERROR_TC_WARNING, error ) ) + else if( error == TR_ERROR_TC_WARNING ) { tr_bencInitStr( item, "tracker-warning", -1, 1 ); } + else if( TR_ERROR_IS_TC( error ) ) + { + tr_bencInitStr( item, "tracker-other", -1, 1 ); + } else { tr_bencInitStr( item, "other", -1, 1 ); diff --git a/libtransmission/peer-mgr.c b/libtransmission/peer-mgr.c index 75961fa47..697afdc05 100644 --- a/libtransmission/peer-mgr.c +++ b/libtransmission/peer-mgr.c @@ -919,12 +919,14 @@ msgsCallbackFunc( void * vpeer, void * vevent, void * vt ) broadcastGotBlock( t, e->pieceIndex, e->offset, e->length ); break; - case TR_PEERMSG_GOT_ASSERT_ERROR: - addStrike( t, peer ); - peer->doPurge = 1; - break; - - case TR_PEERMSG_GOT_ERROR: + case TR_PEERMSG_ERROR: + if( TR_ERROR_IS_IO( e->err ) ) { + t->tor->error = e->err; + strlcpy( t->tor->errorString, tr_errorString( e->err ), sizeof(t->tor->errorString) ); + tr_torrentStop( t->tor ); + } else if( e->err == TR_ERROR_ASSERT ) { + addStrike( t, peer ); + } peer->doPurge = 1; break; diff --git a/libtransmission/peer-msgs.c b/libtransmission/peer-msgs.c index 8ca307142..b1089026e 100644 --- a/libtransmission/peer-msgs.c +++ b/libtransmission/peer-msgs.c @@ -262,7 +262,7 @@ protocolSendChoke( tr_peermsgs * msgs, int choke ) *** EVENTS **/ -static const tr_peermsgs_event blankEvent = { 0, 0, 0, 0, 0.0f }; +static const tr_peermsgs_event blankEvent = { 0, 0, 0, 0, 0.0f, 0 }; static void publish( tr_peermsgs * msgs, tr_peermsgs_event * e ) @@ -271,18 +271,11 @@ publish( tr_peermsgs * msgs, tr_peermsgs_event * e ) } static void -fireGotAssertError( tr_peermsgs * msgs ) +fireError( tr_peermsgs * msgs, tr_errno err ) { tr_peermsgs_event e = blankEvent; - e.eventType = TR_PEERMSG_GOT_ASSERT_ERROR; - publish( msgs, &e ); -} - -static void -fireGotError( tr_peermsgs * msgs ) -{ - tr_peermsgs_event e = blankEvent; - e.eventType = TR_PEERMSG_GOT_ERROR; + e.eventType = TR_PEERMSG_ERROR; + e.err = err; publish( msgs, &e ); } @@ -592,29 +585,29 @@ expireOldRequests( tr_peermsgs * msgs ) tr_list * prune = NULL; const time_t now = time( NULL ); - // find queued requests that are too old - // "time_requested" here is when the request was queued + /* find queued requests that are too old + "time_requested" here is when the request was queued */ for( l=msgs->clientWillAskFor; l!=NULL; l=l->next ) { struct peer_request * req = l->data; if( req->time_requested + REQUEST_TTL_SECS < now ) tr_list_prepend( &prune, req ); } - // find sent requests that are too old - // "time_requested" here is when the request was sent + /* find sent requests that are too old + "time_requested" here is when the request was sent */ for( l=msgs->clientAskedFor; l!=NULL; l=l->next ) { struct peer_request * req = l->data; if( req->time_requested + REQUEST_TTL_SECS < now ) tr_list_prepend( &prune, req ); } - // expire the old requests + /* expire the old requests */ for( l=prune; l!=NULL; l=l->next ) { struct peer_request * req = l->data; tr_peerMsgsCancel( msgs, req->index, req->offset, req->length ); } - // cleanup + /* cleanup */ tr_list_free( &prune, NULL ); } @@ -1150,7 +1143,7 @@ readBtPiece( tr_peermsgs * msgs, struct evbuffer * inbuf, size_t inlen ) if( !err ) return READ_AGAIN; else { - fireGotAssertError( msgs ); + fireError( msgs, err ); return READ_DONE; } } @@ -1164,7 +1157,7 @@ readBtMessage( tr_peermsgs * msgs, struct evbuffer * inbuf, size_t inlen ) const uint8_t id = msgs->incoming.id; const size_t startBufLen = EVBUFFER_LENGTH( inbuf ); - --msglen; // id length + --msglen; /* id length */ if( inlen < msglen ) return READ_MORE; @@ -1174,7 +1167,7 @@ readBtMessage( tr_peermsgs * msgs, struct evbuffer * inbuf, size_t inlen ) if( !messageLengthIsCorrect( msgs, id, msglen+1 ) ) { dbgmsg( msgs, "bad packet - BT message #%d with a length of %d", (int)id, (int)msglen ); - fireGotError( msgs ); + fireError( msgs, TR_ERROR ); return READ_DONE; } @@ -1386,12 +1379,12 @@ addPeerToBlamefield( tr_peermsgs * msgs, uint32_t index ) tr_bitfieldAdd( msgs->info->blame, index ); } -static int +static tr_errno clientGotBlock( tr_peermsgs * msgs, const uint8_t * data, const struct peer_request * req ) { - int i; + int err; tr_torrent * tor = msgs->torrent; const int block = _tr_block( tor, req->index, req->offset ); struct peer_request *myreq; @@ -1403,7 +1396,7 @@ clientGotBlock( tr_peermsgs * msgs, { dbgmsg( msgs, "wrong block size -- expected %u, got %d", tr_torBlockCountBytes( msgs->torrent, block ), req->length ); - return TR_ERROR_ASSERT; + return TR_ERROR; } /* save the block */ @@ -1444,9 +1437,8 @@ clientGotBlock( tr_peermsgs * msgs, **/ msgs->info->peerSentPieceDataAt = time( NULL ); - i = tr_ioWrite( tor, req->index, req->offset, req->length, data ); - if( i ) - return 0; + if(( err = tr_ioWrite( tor, req->index, req->offset, req->length, data ))) + return err; tr_cpBlockAdd( tor->completion, block ); @@ -1656,7 +1648,7 @@ gotError( struct bufferevent * evbuf UNUSED, short what, void * vmsgs ) if( what & ( EVBUFFER_EOF | EVBUFFER_ERROR ) ) { dbgmsg( vmsgs, "libevent got an error! what=%hd, errno=%d (%s)", what, errno, strerror(errno) ); - fireGotError( vmsgs ); + fireError( vmsgs, TR_ERROR ); } } diff --git a/libtransmission/peer-msgs.h b/libtransmission/peer-msgs.h index 941189c9a..1838a2225 100644 --- a/libtransmission/peer-msgs.h +++ b/libtransmission/peer-msgs.h @@ -65,8 +65,7 @@ typedef enum TR_PEERMSG_CLIENT_BLOCK, TR_PEERMSG_PIECE_DATA, TR_PEERMSG_PEER_PROGRESS, - TR_PEERMSG_GOT_ERROR, - TR_PEERMSG_GOT_ASSERT_ERROR, + TR_PEERMSG_ERROR, TR_PEERMSG_CANCEL, TR_PEERMSG_NEED_REQ } @@ -79,6 +78,7 @@ typedef struct uint32_t offset; /* for TR_PEERMSG_GOT_BLOCK */ uint32_t length; /* for TR_PEERMSG_GOT_BLOCK */ float progress; /* for TR_PEERMSG_PEER_PROGRESS */ + tr_errno err; /* for TR_PEERMSG_GOT_ERROR */ } tr_peermsgs_event; diff --git a/libtransmission/torrent.c b/libtransmission/torrent.c index e0f0312cf..8539d4c90 100644 --- a/libtransmission/torrent.c +++ b/libtransmission/torrent.c @@ -880,13 +880,35 @@ freeTorrent( tr_torrent * tor ) tr_globalUnlock( h ); } -enum +/** +*** Start/Stop Callback +**/ + +static void +fireActiveChange( tr_torrent * tor, int isRunning ) { - AFTER_RECHECK_NONE, - AFTER_RECHECK_START, - AFTER_RECHECK_STOP, - AFTER_RECHECK_CLOSE -}; + assert( tor != NULL ); + + if( tor->active_func != NULL ) + (tor->active_func)( tor, isRunning, tor->active_func_user_data ); +} + +void +tr_torrentSetActiveCallback( tr_torrent * tor, + tr_torrent_active_func func, + void * user_data ) +{ + assert( tor != NULL ); + tor->active_func = func; + tor->active_func_user_data = user_data; +} + +void +tr_torrentClearActiveCallback( tr_torrent * torrent ) +{ + tr_torrentSetActiveCallback( torrent, NULL, NULL ); +} + static void checkAndStartImpl( void * vtor ) @@ -896,6 +918,7 @@ checkAndStartImpl( void * vtor ) tr_globalLock( tor->handle ); tor->isRunning = 1; + fireActiveChange( tor, tor->isRunning ); *tor->errorString = '\0'; tr_torrentResetTransferStats( tor ); tor->cpStatus = tr_cpGetStatus( tor->completion ); @@ -959,6 +982,7 @@ stopTorrent( void * vtor ) tr_ioRecheckRemove( tor ); tr_peerMgrStopTorrent( tor->handle->peerMgr, tor->info.hash ); tr_trackerStop( tor->tracker ); + fireActiveChange( tor, 0 ); for( i=0; iinfo.fileCount; ++i ) { diff --git a/libtransmission/torrent.h b/libtransmission/torrent.h index 89a556467..9e6925856 100644 --- a/libtransmission/torrent.h +++ b/libtransmission/torrent.h @@ -147,6 +147,9 @@ struct tr_torrent tr_torrent_status_func * status_func; void * status_func_user_data; + tr_torrent_active_func * active_func; + void * active_func_user_data; + unsigned int statCur : 1; unsigned int isRunning : 1; diff --git a/libtransmission/transmission.h b/libtransmission/transmission.h index 353715833..db90af490 100644 --- a/libtransmission/transmission.h +++ b/libtransmission/transmission.h @@ -67,28 +67,6 @@ enum TR_PEER_FROM__MAX }; -/*********************************************************************** - * Error codes - **********************************************************************/ -/* General errors */ -#define TR_OK 0x00000000 -#define TR_ERROR 0x81000000 -#define TR_ERROR_ASSERT 0x82000000 -/* I/O errors */ -#define TR_ERROR_IO_MASK 0x000000FF -#define TR_ERROR_IO_PERMISSIONS 0x80000002 -#define TR_ERROR_IO_SPACE 0x80000004 -#define TR_ERROR_IO_FILE_TOO_BIG 0x80000008 -#define TR_ERROR_IO_OPEN_FILES 0x80000010 -#define TR_ERROR_IO_DUP_DOWNLOAD 0x80000020 -#define TR_ERROR_IO_OTHER 0x80000040 -/* Misc */ -#define TR_ERROR_TC_MASK 0x00000F00 -#define TR_ERROR_TC_ERROR 0x80000100 -#define TR_ERROR_TC_WARNING 0x80000200 - -#define TR_ERROR_ISSET( num, code ) ( (code) == ( (code) & (num) ) ) - /*********************************************************************** * tr_init *********************************************************************** @@ -496,7 +474,7 @@ void tr_torrentStop( tr_torrent * ); /** -*** Register to be notified whenever a torrent's state changes. +*** **/ typedef enum @@ -511,12 +489,45 @@ typedef void (tr_torrent_status_func)(tr_torrent * torrent, cp_status_t status, void * user_data ); +/** + * Register to be notified whenever a torrent's state changes. + * + * func is invoked FROM LIBTRANSMISSION'S THREAD! + * This means func must be fast (to avoid blocking peers), + * shouldn't call libtransmission functions (to avoid deadlock), + * and shouldn't modify client-level memory without using a mutex! + */ void tr_torrentSetStatusCallback( tr_torrent * torrent, tr_torrent_status_func func, void * user_data ); void tr_torrentClearStatusCallback( tr_torrent * torrent ); + + +/** +*** +**/ + +typedef void (tr_torrent_active_func)(tr_torrent * torrent, + int isRunning, + void * user_data ); + +/** + * Register to be notified whenever a torrent starts or stops. + * + * func is invoked FROM LIBTRANSMISSION'S THREAD! + * This means func must be fast (to avoid blocking peers), + * shouldn't call libtransmission functions (to avoid deadlock), + * and shouldn't modify client-level memory without using a mutex! + */ +void tr_torrentSetActiveCallback( tr_torrent * torrent, + tr_torrent_active_func func, + void * user_data ); + +void tr_torrentClearActiveCallback( tr_torrent * torrent ); + + /** * MANUAL ANNOUNCE * @@ -675,14 +686,41 @@ tr_torrent_status; #define TR_STATUS_IS_ACTIVE(s) ((s) != TR_STATUS_STOPPED) -/*********************************************************************** - * tr_stat - **********************************************************************/ +/** + * Transmission error codes + * errors are always negative, and 0 refers to no error. + */ +typedef enum tr_errno +{ + TR_OK = 0, + + /* general errors */ + TR_ERROR = -100, + TR_ERROR_ASSERT, + + /* io errors */ + TR_ERROR_IO_PARENT = -200, + TR_ERROR_IO_PERMISSIONS, + TR_ERROR_IO_SPACE, + TR_ERROR_IO_FILE_TOO_BIG, + TR_ERROR_IO_OPEN_FILES, + TR_ERROR_IO_DUP_DOWNLOAD, + TR_ERROR_IO_OTHER, + + /* tracker errors */ + TR_ERROR_TC_ERROR = -300, + TR_ERROR_TC_WARNING +} +tr_errno; + +#define TR_ERROR_IS_IO(e) (TR_ERROR_IO_PARENT<=(e) && (e)<=TR_ERROR_IO_OTHER) +#define TR_ERROR_IS_TC(e) (TR_ERROR_TC_ERROR<=(e) && (e)<=TR_ERROR_TC_WARNING) + struct tr_stat { tr_torrent_status status; - int error; + tr_errno error; char errorString[128]; const tr_tracker_info * tracker; diff --git a/libtransmission/utils.c b/libtransmission/utils.c index 136057b7f..e2a5b0dbc 100644 --- a/libtransmission/utils.c +++ b/libtransmission/utils.c @@ -489,10 +489,12 @@ tr_buildPath ( char *buf, size_t buflen, const char *first_element, ... ) } int -tr_ioErrorFromErrno( void ) +tr_ioErrorFromErrno( int err ) { - switch( errno ) + switch( err ) { + case 0: + return TR_OK; case EACCES: case EROFS: return TR_ERROR_IO_PERMISSIONS; @@ -515,24 +517,30 @@ tr_errorString( int code ) { case TR_OK: return "No error"; + case TR_ERROR: return "Generic error"; case TR_ERROR_ASSERT: return "Assert error"; + + case TR_ERROR_IO_PARENT: + return "Download folder does not exist"; case TR_ERROR_IO_PERMISSIONS: return "Insufficient permissions"; case TR_ERROR_IO_SPACE: return "Insufficient free space"; - case TR_ERROR_IO_DUP_DOWNLOAD: - return "Already active transfer with same name and download folder"; case TR_ERROR_IO_FILE_TOO_BIG: return "File too large"; case TR_ERROR_IO_OPEN_FILES: return "Too many open files"; + case TR_ERROR_IO_DUP_DOWNLOAD: + return "Already active transfer with same name and download folder"; case TR_ERROR_IO_OTHER: return "Generic I/O error"; + + default: + return "Unknown error"; } - return "Unknown error"; } /**** diff --git a/libtransmission/utils.h b/libtransmission/utils.h index 133cc8fe8..c5f839fb3 100644 --- a/libtransmission/utils.h +++ b/libtransmission/utils.h @@ -76,7 +76,7 @@ void tr_buildPath( char* buf, size_t buflen, struct timeval timevalMsec( uint64_t milliseconds ); -int tr_ioErrorFromErrno( void ); +int tr_ioErrorFromErrno( int err ); const char * tr_errorString( int code );