partial implementation of json-ipc backend: torrent start/stop/close/verify/stat/info, as are getting & setting per-torrent peer limits, upload speeds, and download speeds. this is a work in progress.

This commit is contained in:
Charles Kerr 2008-05-12 00:41:55 +00:00
parent 6eb8c5132e
commit 63b82714f8
6 changed files with 462 additions and 27 deletions

View File

@ -12,7 +12,7 @@
2. Message Format
The entire protocol is formatted in a subset of JSON that understands
Messages are formatted in a subset of JSON that understands
arrays, maps, strings, and whole numbers with no exponentials --
in short, the subset of JSON easily represented in benc.
floating-point numbers are represented as strings.
@ -20,7 +20,7 @@
There are only two message types: request and response. Both
are JSON objects with two members: "headers" (described in 2.1)
and "body" (described in 2.2-2.3).
and "body" (described in 2.2 - 2.3).
2.1. Headers
@ -45,21 +45,17 @@
3. Torrent Requests
3.1. Common Arguments
Most torrent requests support an "ids" argument, which is a list
containing unique torrent ids, or torrent sha1 hash strings, or both.
These are the torrents that the request will be applied to.
If "ids" is omitted, the request is applied to all torrents.
3.2. Torrent Action Requests
3.1. Torrent Action Requests
Request names: "torrent-start", "torrent-stop",
"torrent-remove", "torrent-verify"
Request arguments: 3.1's optional "ids" argument.
Request arguments: "ids", a list of unique torrent ids, sha1 hash strings,
or both. These are the torrents that the request will
be applied to. If "ids" is ommitted, the request is
applied to all torrents.
Response arguments: none.
3.3. Torrent Info Requests
3.2. Torrent Info Requests
Request name: "torrent-info".
Request arguments: 3.1's optional "ids" argument.
@ -118,7 +114,7 @@
}
}
3.4. Torrent Status Requests
3.3. Torrent Status Requests
Request name is "torrent-status".
Request arguments: 3.1's optional "ids" argument.
@ -130,7 +126,7 @@
(2) a new string, "announce-url", is added
(3) a new string, "scrape-url", is added
3.5. Adding a Torrent
3.4. Adding a Torrent
Request name: "torrent-add"
Request arguments:
@ -146,55 +142,58 @@
The "filename" argument is required; all others are optional.
Response arguments: 3.1's "error" if the request failed.
Response arguments: none.
3.6. Other torrent settings
3.5. Other torrent settings
Common arguments:
string | value type & description
-------------------+-------------------------------------------------
"id" | int or string torrent id or hash string
"max-peers" | int maximum number of peers
"speed-limit-down" | int maximum download speed (in KiB/s)
"speed-limit-up" | int maximum upload speed (in KiB/s)
3.6.1. Mutators
3.5.1. Mutators
Request name: "torrent-set"
Request arguments: "id" from 3.6, plus one or more of 3.6's other arguments
Request arguments: 3.1's "ids", plus one or more of 3.5's arguments
Response arguments: none
3.6.2. Accessors
3.5.2. Accessors
Request name: "torrent-get"
Request arguments: none
Response arguments: all of 3.6's arguments
Response arguments: "torrents", a list of torrent objects containing
all of 3.5's arguments plus the id passed in
by "torrent-set".
3.7 File Priorities
3.6 File Priorities
Common arguments:
string | value type & description
-------------------+-------------------------------------------------
"id" | int or string torrent id or hash string
"priority-high" | array indices of one or more high-priority files
"priority-low" | array indices of one or more low-priority files
"priority-normal" | array indices of one or more normal-priority files
"download" | array indices of one or more file to download
"no-download" | array indices of one or more file to not download
3.7.1. Mutators
3.6.1. Mutators
Request name: "torrent-set-file"
Request arguments: "id" from 3.7, plus one or more of 3.7's other arguments
Request arguments: 3.1's "ids", plus one or more of 3.6's arguments
Response arguments: none
3.7.2. Accessors
3.6.2. Accessors
Request name: "torrent-get-file"
Request arguments: none
Response arguments: all of 3.7's arguments
Response arguments: list of objects, one per torrent, containing
all of 3.6's arguments plus the id passed in
by "torrent-set".
4. Session Status Requests

View File

@ -16,6 +16,7 @@ libtransmission_a_SOURCES = \
ggets.c \
handshake.c \
inout.c \
ipc.c \
ipcparse.c \
json.c \
list.c \
@ -56,6 +57,7 @@ noinst_HEADERS = \
ggets.h \
handshake.h \
inout.h \
ipc.h \
ipcparse.h \
list.h \
makemeta.h \

380
libtransmission/ipc.c Normal file
View File

@ -0,0 +1,380 @@
/*
* This file Copyright (C) 2008 Charles Kerr <charles@rebelbase.com>
*
* This file is licensed by the GPL version 2. Works owned by the
* Transmission project are granted a special exemption to clause 2(b)
* so that the bulk of its code can remain under the MIT license.
* This exemption does not extend to derived works not owned by
* the Transmission project.
*
* $Id:$
*/
#include "transmission.h"
#include "bencode.h"
#include "ipc.h"
#include "torrent.h"
#include "utils.h"
/***
****
***/
static tr_torrent **
getTorrents( tr_handle * handle, tr_benc * args, int * setmeCount )
{
tr_torrent ** torrents = NULL;
int torrentCount = 0;
tr_benc * ids;
if( tr_bencDictFindList( args, "ids", &ids ) )
{
int i;
const int n = tr_bencListSize( ids );
torrents = tr_new0( tr_torrent*, n );
for( i=0; i<n; ++i )
{
tr_torrent * tor = NULL;
tr_benc * node = tr_bencListChild( ids, i );
int64_t id;
const char * str;
if( tr_bencGetInt( node, &id ) )
tor = tr_torrentFindFromId( handle, id );
else if( tr_bencGetStr( node, &str ) )
tor = tr_torrentFindFromHashString( handle, str );
if( tor )
torrents[torrentCount++] = tor;
}
}
else /* all of them */
{
tr_torrent * tor = NULL;
const int n = tr_torrentCount( handle );
torrents = tr_new0( tr_torrent*, n );
while(( tor = tr_torrentNext( handle, tor )))
torrents[torrentCount++] = tor;
}
*setmeCount = torrentCount;
return torrents;
}
static const char*
torrentStart( tr_handle * handle, tr_benc * args_in, tr_benc * args_out UNUSED )
{
int i, torrentCount;
tr_torrent ** torrents = getTorrents( handle, args_in, &torrentCount );
for( i=0; i<torrentCount; ++i )
tr_torrentStart( torrents[i] );
tr_free( torrents );
return NULL;
}
static const char*
torrentStop( tr_handle * handle, tr_benc * args_in, tr_benc * args_out UNUSED )
{
int i, torrentCount;
tr_torrent ** torrents = getTorrents( handle, args_in, &torrentCount );
for( i=0; i<torrentCount; ++i )
tr_torrentStop( torrents[i] );
tr_free( torrents );
return NULL;
}
static const char*
torrentClose( tr_handle * handle, tr_benc * args_in, tr_benc * args_out UNUSED )
{
int i, torrentCount;
tr_torrent ** torrents = getTorrents( handle, args_in, &torrentCount );
for( i=0; i<torrentCount; ++i )
tr_torrentClose( torrents[i] );
tr_free( torrents );
return NULL;
}
static const char*
torrentVerify( tr_handle * handle, tr_benc * args_in, tr_benc * args_out UNUSED )
{
int i, torrentCount;
tr_torrent ** torrents = getTorrents( handle, args_in, &torrentCount );
for( i=0; i<torrentCount; ++i )
tr_torrentVerify( torrents[i] );
tr_free( torrents );
return NULL;
}
static const char*
torrentStatus( tr_handle * handle, tr_benc * args_in, tr_benc * args_out )
{
int i, torrentCount;
tr_torrent ** torrents = getTorrents( handle, args_in, &torrentCount );
tr_benc * list = tr_bencDictAddList( args_out, "status", torrentCount );
for( i=0; i<torrentCount; ++i )
{
tr_torrent * tor = torrents[i];
const tr_stat * st = tr_torrentStat( tor );
tr_benc * d = tr_bencListAddDict( list, 32 );
tr_benc * t;
tr_bencDictAddInt( d, "id", tor->uniqueId );
tr_bencDictAddInt( d, "status", st->status );
tr_bencDictAddInt( d, "error", st->error );
tr_bencDictAddStr( d, "errorString", st->errorString );
tr_bencDictAddDouble( d, "recheckProgress", st->recheckProgress );
tr_bencDictAddDouble( d, "percentComplete", st->percentComplete );
tr_bencDictAddDouble( d, "percentDone", st->percentDone );
tr_bencDictAddDouble( d, "rateDownload", st->rateDownload );
tr_bencDictAddDouble( d, "rateUpload", st->rateUpload );
tr_bencDictAddDouble( d, "swarmspeed", st->swarmspeed );
tr_bencDictAddDouble( d, "ratio", st->ratio );
tr_bencDictAddInt( d, "eta", st->eta );
tr_bencDictAddInt( d, "peersKnown", st->peersKnown );
tr_bencDictAddInt( d, "peersConnected", st->peersConnected );
tr_bencDictAddInt( d, "peersSendingToUs", st->peersSendingToUs );
tr_bencDictAddInt( d, "peersGettingFromUs", st->peersGettingFromUs );
tr_bencDictAddInt( d, "seeders", st->seeders );
tr_bencDictAddInt( d, "leechers", st->leechers );
tr_bencDictAddInt( d, "completedFromTracker", st->completedFromTracker );
tr_bencDictAddInt( d, "manualAnnounceTime", st->manualAnnounceTime );
tr_bencDictAddInt( d, "sizeWhenDone", st->sizeWhenDone );
tr_bencDictAddInt( d, "leftUntilDone", st->leftUntilDone );
tr_bencDictAddInt( d, "desiredAvailable", st->desiredAvailable );
tr_bencDictAddInt( d, "corruptEver", st->corruptEver );
tr_bencDictAddInt( d, "uploadedEver", st->uploadedEver );
tr_bencDictAddInt( d, "downloadedEver", st->downloadedEver );
tr_bencDictAddInt( d, "haveValid", st->haveValid );
tr_bencDictAddInt( d, "haveUnchecked", st->haveUnchecked );
tr_bencDictAddInt( d, "startDate", st->startDate );
tr_bencDictAddInt( d, "activityDate", st->activityDate );
t = tr_bencDictAddDict( d, "peersFrom", 4 );
tr_bencDictAddInt( t, "cache", st->peersFrom[TR_PEER_FROM_CACHE] );
tr_bencDictAddInt( t, "incoming", st->peersFrom[TR_PEER_FROM_INCOMING] );
tr_bencDictAddInt( t, "pex", st->peersFrom[TR_PEER_FROM_PEX] );
tr_bencDictAddInt( t, "tracker", st->peersFrom[TR_PEER_FROM_TRACKER] );
t = tr_bencDictAddDict( d, "tracker_stat", 7 );
tr_bencDictAddStr( t, "scrapeResponse", st->tracker_stat.scrapeResponse );
tr_bencDictAddStr( t, "announceResponse", st->tracker_stat.announceResponse );
tr_bencDictAddInt( t, "lastScrapeTime", st->tracker_stat.lastScrapeTime );
tr_bencDictAddInt( t, "nextScrapeTime", st->tracker_stat.nextScrapeTime );
tr_bencDictAddInt( t, "lastAnnounceTime", st->tracker_stat.lastAnnounceTime );
tr_bencDictAddInt( t, "nextAnnounceTime", st->tracker_stat.nextAnnounceTime );
tr_bencDictAddInt( t, "nextManualAnnounceTime", st->tracker_stat.nextManualAnnounceTime );
}
/* cleanup */
tr_free( torrents );
return NULL;
}
static void
addFiles( const tr_info * info, tr_benc * files )
{
unsigned int i;
for( i=0; i<info->fileCount; ++i )
{
const tr_file * file = &info->files[i];
tr_benc * d = tr_bencListAddDict( files, 4 );
tr_bencDictAddInt( d, "length", file->length );
tr_bencDictAddStr( d, "name", file->name );
tr_bencDictAddInt( d, "priority", file->priority );
tr_bencDictAddInt( d, "dnd", file->dnd );
}
}
static void
addTrackers( const tr_info * info, tr_benc * trackers )
{
int i;
for( i=0; i<info->trackerCount; ++i )
{
const tr_tracker_info * t = &info->trackers[i];
tr_benc * d = tr_bencListAddDict( trackers, 3 );
tr_bencDictAddInt( d, "tier", t->tier );
tr_bencDictAddStr( d, "announce", t->announce );
tr_bencDictAddStr( d, "scrape", t->scrape );
}
}
static const char*
torrentInfo( tr_handle * handle, tr_benc * args_in, tr_benc * args_out )
{
int i, torrentCount;
tr_torrent ** torrents = getTorrents( handle, args_in, &torrentCount );
tr_benc * list = tr_bencDictAddList( args_out, "status", torrentCount );
for( i=0; i<torrentCount; ++i )
{
tr_torrent * tor = torrents[i];
const tr_info * inf = tr_torrentInfo( tor );
tr_benc * d = tr_bencListAddDict( list, 13 );
tr_bencDictAddStr( d, "torrent", inf->torrent );
tr_bencDictAddStr( d, "hashString", inf->hashString );
tr_bencDictAddStr( d, "name", inf->name );
tr_bencDictAddStr( d, "comment", inf->comment ? inf->comment : "" );
tr_bencDictAddStr( d, "creator", inf->creator ? inf->creator : "" );
tr_bencDictAddInt( d, "isPrivate", inf->isPrivate );
tr_bencDictAddInt( d, "isMultifile", inf->isMultifile );
tr_bencDictAddInt( d, "dateCreated", inf->dateCreated );
tr_bencDictAddInt( d, "pieceSize", inf->pieceSize );
tr_bencDictAddInt( d, "pieceCount", inf->pieceCount );
tr_bencDictAddInt( d, "totalSize", inf->totalSize );
addFiles ( inf, tr_bencDictAddList( d, "files", inf->fileCount ) );
addTrackers( inf, tr_bencDictAddList( d, "files", inf->trackerCount ) );
}
/* cleanup */
tr_free( torrents );
return NULL;
}
static const char*
torrentGet( tr_handle * handle, tr_benc * args_in, tr_benc * args_out )
{
int i, torrentCount;
tr_torrent ** torrents = getTorrents( handle, args_in, &torrentCount );
tr_benc * list = tr_bencDictAddList( args_out, "torrents", torrentCount );
for( i=0; i<torrentCount; ++i )
{
tr_torrent * tor = torrents[i];
tr_benc * d = tr_bencListAddDict( list, 3 );
tr_bencDictAddInt( d, "max-peers", tr_torrentGetMaxConnectedPeers( tor ) );
tr_bencDictAddInt( d, "speed-limit-down", tr_torrentGetSpeedLimit( tor, TR_DOWN ) );
tr_bencDictAddInt( d, "speed-limit-up", tr_torrentGetSpeedLimit( tor, TR_UP ) );
}
tr_free( torrents );
return NULL;
}
static const char*
torrentSet( tr_handle * handle, tr_benc * args_in, tr_benc * args_out UNUSED )
{
int i, torrentCount;
tr_torrent ** torrents = getTorrents( handle, args_in, &torrentCount );
for( i=0; i<torrentCount; ++i )
{
int64_t tmp;
tr_torrent * tor = torrents[i];
if( tr_bencDictFindInt( args_in, "max-peers", &tmp ) )
tr_torrentSetMaxConnectedPeers( tor, tmp );
if( tr_bencDictFindInt( args_in, "speed-limit-down", &tmp ) )
tr_torrentSetSpeedLimit( tor, TR_DOWN, tmp );
if( tr_bencDictFindInt( args_in, "speed-limit-up", &tmp ) )
tr_torrentSetSpeedLimit( tor, TR_UP, tmp );
}
tr_free( torrents );
return NULL;
}
static const char*
torrentGetFile( tr_handle * handle UNUSED, tr_benc * args_in UNUSED, tr_benc * args_out UNUSED )
{
return NULL;
}
static const char*
torrentSetFile( tr_handle * handle UNUSED, tr_benc * args_in UNUSED, tr_benc * args_out UNUSED )
{
return NULL;
}
static const char*
sessionSet( tr_handle * handle UNUSED, tr_benc * args_in UNUSED, tr_benc * args_out UNUSED )
{
return NULL;
}
static const char*
sessionGet( tr_handle * handle UNUSED, tr_benc * args_in UNUSED, tr_benc * args_out UNUSED )
{
return NULL;
}
static const char*
torrentAdd( tr_handle * handle UNUSED, tr_benc * args_in UNUSED, tr_benc * args_out UNUSED )
{
return NULL;
}
/***
****
***/
static char*
request_exec( struct tr_handle * handle,
tr_benc * request,
int * response_len )
{
int64_t i;
const char * str;
char * out;
tr_benc response;
tr_benc * headers_in = NULL;
tr_benc * body_in = NULL;
tr_benc * args_in = NULL;
tr_benc * headers_out = NULL;
tr_benc * body_out = NULL;
tr_benc * args_out = NULL;
const char * result = NULL;
headers_in = tr_bencDictFind( request, "headers" );
body_in = tr_bencDictFind( request, "body" );
args_in = tr_bencDictFind( body_in, "args" );
/* build the response skeleton */
tr_bencInitDict( &response, 2 );
headers_out = tr_bencDictAddDict( &response, "headers", 2 );
tr_bencDictAddStr( headers_out, "type", "response" );
if( tr_bencDictFindInt( headers_in, "tag", &i ) )
tr_bencDictAddInt( headers_out, "tag", i );
body_out = tr_bencDictAddDict( &response, "body", 2 );
args_out = tr_bencDictAddDict( body_out, "args", 0 );
/* parse the request */
if( !tr_bencDictFindStr( body_in, "name", &str ) )
result = "no request name given";
else {
if( !strcmp( str, "torrent-start" ) ) result = torrentStart ( handle, args_in, args_out );
else if( !strcmp( str, "torrent-stop" ) ) result = torrentStop ( handle, args_in, args_out );
else if( !strcmp( str, "torrent-close" ) ) result = torrentClose ( handle, args_in, args_out );
else if( !strcmp( str, "torrent-verify" ) ) result = torrentVerify ( handle, args_in, args_out );
else if( !strcmp( str, "torrent-status" ) ) result = torrentStatus ( handle, args_in, args_out );
else if( !strcmp( str, "torrent-info" ) ) result = torrentInfo ( handle, args_in, args_out );
else if( !strcmp( str, "torrent-add" ) ) result = torrentAdd ( handle, args_in, args_out );
else if( !strcmp( str, "torrent-set" ) ) result = torrentSet ( handle, args_in, args_out );
else if( !strcmp( str, "torrent-get" ) ) result = torrentGet ( handle, args_in, args_out );
else if( !strcmp( str, "torrent-set-file" ) ) result = torrentSetFile( handle, args_in, args_out );
else if( !strcmp( str, "torrent-get-file" ) ) result = torrentGetFile( handle, args_in, args_out );
else if( !strcmp( str, "session-set" ) ) result = sessionSet ( handle, args_in, args_out );
else if( !strcmp( str, "session-get" ) ) result = sessionGet ( handle, args_in, args_out );
else result = "request name not recognized";
}
if( !result )
result = "success";
tr_bencDictAddStr( body_out, "result", result );
out = tr_bencSave( &response, response_len ); /* TODO: json, not benc */
tr_bencFree( &response );
return out;
}
char*
tr_ipc_request_exec( struct tr_handle * handle,
const void * request_json,
int request_len,
int * response_len )
{
tr_benc top;
int have_content = !tr_jsonParse( request_json, (const char*)request_json + request_len, &top, NULL );
char * ret = request_exec( handle, have_content ? &top : NULL, response_len );
if( have_content )
tr_bencFree( &top );
return ret;
}

24
libtransmission/ipc.h Normal file
View File

@ -0,0 +1,24 @@
/*
* This file Copyright (C) 2008 Charles Kerr <charles@rebelbase.com>
*
* This file is licensed by the GPL version 2. Works owned by the
* Transmission project are granted a special exemption to clause 2(b)
* so that the bulk of its code can remain under the MIT license.
* This exemption does not extend to derived works not owned by
* the Transmission project.
*
* $Id:$
*/
#ifndef TR_IPC_H
#define TR_IPC_H
struct tr_handle;
char*
tr_ipc_request_exec( struct tr_handle * handle,
const void * request_json,
int request_len,
int * response_len );
#endif

View File

@ -53,6 +53,30 @@
****
***/
tr_torrent*
tr_torrentFindFromId( tr_handle * handle, int id )
{
tr_torrent * tor = NULL;
while(( tor = tr_torrentNext( handle, tor )))
if( tor->uniqueId == id )
return tor;
return NULL;
}
tr_torrent*
tr_torrentFindFromHashString( tr_handle * handle, const char * str )
{
tr_torrent * tor = NULL;
while(( tor = tr_torrentNext( handle, tor )))
if( !strcmp( str, tor->info.hashString ) )
return tor;
return NULL;
}
int
tr_torrentExists( const tr_handle * handle,
const uint8_t * torrentHash )
@ -368,10 +392,12 @@ torrentRealInit( tr_handle * h,
uint64_t loaded;
uint64_t t;
tr_info * info = &tor->info;
static int nextUniqueId = 1;
tr_globalLock( h );
tor->handle = h;
tor->uniqueId = nextUniqueId++;
randomizeTiers( info );

View File

@ -49,7 +49,9 @@ int tr_torrentIsSeed ( const tr_torrent * );
void tr_torrentChangeMyPort ( tr_torrent * );
int tr_torrentExists( const tr_handle *, const uint8_t * );
tr_torrent* tr_torrentFindFromId( tr_handle *, int id );
tr_torrent* tr_torrentFindFromHash( tr_handle *, const uint8_t * );
tr_torrent* tr_torrentFindFromHashString( tr_handle *, const char * );
tr_torrent* tr_torrentFindFromObfuscatedHash( tr_handle *, const uint8_t* );
void tr_torrentGetRates( const tr_torrent *, float * toClient, float * toPeer );
@ -178,6 +180,8 @@ struct tr_torrent
tr_stat stats;
tr_torrent * next;
int uniqueId;
};
#endif