mirror of
https://github.com/transmission/transmission.git
synced 2025-12-24 12:28:52 +00:00
Merge daemon branch to trunk.
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -6,6 +6,10 @@ beos/Transmission
|
||||
/build
|
||||
cli/.depend
|
||||
cli/transmissioncli
|
||||
daemon/.depend
|
||||
daemon/transmission-daemon
|
||||
daemon/transmission-proxy
|
||||
daemon/transmission-remote
|
||||
gtk/.depend
|
||||
gtk/defines.h
|
||||
gtk/po/*.mo
|
||||
|
||||
1
AUTHORS
1
AUTHORS
@@ -10,6 +10,7 @@ Eric Petit <titer@m0k.org>
|
||||
Josh Elsasser <josh@elsasser.org>
|
||||
+ Back-end
|
||||
+ GTK+ interface
|
||||
+ Daemon interface
|
||||
|
||||
Mitchell Livingston <livings124@gmail.com>
|
||||
+ OS X interface
|
||||
|
||||
@@ -92,6 +92,9 @@ program was written by
|
||||
and
|
||||
.An Mitchell Livingston Aq livings124@gmail.com .
|
||||
.Sh SEE ALSO
|
||||
.Xr transmission-gtk 1
|
||||
.Xr transmission-daemon 1 ,
|
||||
.Xr transmission-gtk 1 ,
|
||||
.Xr transmission-proxy 1 ,
|
||||
.Xr transmission-remote 1
|
||||
.Pp
|
||||
http://transmission.m0k.org
|
||||
|
||||
107
configure
vendored
107
configure
vendored
@@ -12,6 +12,7 @@ MATH=no
|
||||
PTHREAD=no
|
||||
OPENSSL=
|
||||
GTK=
|
||||
LIBEVENT=
|
||||
PREFIX=/usr/local
|
||||
CC="${CC-cc}"
|
||||
CFLAGS="${CFLAGS}"
|
||||
@@ -30,6 +31,7 @@ usage()
|
||||
Options:
|
||||
--disable-openssl Disable OpenSSL, use built-in SHA1 implementation
|
||||
--disable-gtk Don't build the GTK+ GUI
|
||||
--disable-daemon Don't build the daemon
|
||||
--prefix=PATH Installation path
|
||||
--verbose Display additional information for debugging
|
||||
|
||||
@@ -138,6 +140,46 @@ EOF
|
||||
rm -f testconf*
|
||||
}
|
||||
|
||||
strlcpy_test()
|
||||
{
|
||||
verbose strlcpy_test
|
||||
cat > testconf.c << EOF
|
||||
#include <string.h>
|
||||
int main()
|
||||
{
|
||||
char buf[] = "1234:p";
|
||||
strlcpy( buf, "foo", sizeof buf );
|
||||
return ( 0 == memcmp( buf, "foo\0:p", 6 ) ? 0 : 1 );
|
||||
}
|
||||
EOF
|
||||
if runcmd $CC -o testconf testconf.c && runcmd ./testconf
|
||||
then
|
||||
CFLAGS="$CFLAGS -DHAVE_STRLCPY"
|
||||
fi
|
||||
rm -f testconf*
|
||||
}
|
||||
|
||||
strlcat_test()
|
||||
{
|
||||
verbose strlcat_test
|
||||
cat > testconf.c << EOF
|
||||
#include <string.h>
|
||||
int main()
|
||||
{
|
||||
char buf[] = "1234567:p";
|
||||
buf[0] = '\0';
|
||||
strlcat( buf, "foo", sizeof buf );
|
||||
strlcat( buf, "bar", sizeof buf );
|
||||
return ( 0 == memcmp( buf, "foobar\0:p", 6 ) ? 0 : 1 );
|
||||
}
|
||||
EOF
|
||||
if runcmd $CC -o testconf testconf.c && runcmd ./testconf
|
||||
then
|
||||
CFLAGS="$CFLAGS -DHAVE_STRLCAT"
|
||||
fi
|
||||
rm -f testconf*
|
||||
}
|
||||
|
||||
gettext_test()
|
||||
{
|
||||
verbose gettext_test
|
||||
@@ -212,6 +254,45 @@ EOF
|
||||
fi
|
||||
}
|
||||
|
||||
libevent_test()
|
||||
{
|
||||
verbose libevent_test
|
||||
cat > testconf.c <<EOF
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <event.h>
|
||||
int main()
|
||||
{
|
||||
event_init();
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
|
||||
if runcmd $CC $CFLAGS -levent $LDFLAGS -o testconf testconf.c
|
||||
then
|
||||
LIBEVENT=yes
|
||||
LDFLAGS_EVENT="-levent"
|
||||
rm -f testconf*
|
||||
return 0
|
||||
fi
|
||||
|
||||
for event_testdir in $PREFIX /usr/local /usr/X11R6 /usr/pkg
|
||||
do
|
||||
if runcmd $CC $CFLAGS -I$event_testdir/include $LDFLAGS -levent \
|
||||
-L$event_testdir/lib -o testconf testconf.c
|
||||
then
|
||||
LIBEVENT=yes
|
||||
CFLAGS_EVENT="-I$event_testdir/include"
|
||||
LDFLAGS_EVENT="-levent -L$event_testdir/lib"
|
||||
rm -f testconf*
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
LIBEVENT=no
|
||||
rm -f testconf*
|
||||
return 1
|
||||
}
|
||||
|
||||
#
|
||||
# Parse options
|
||||
#
|
||||
@@ -222,6 +303,9 @@ while [ $# -ne 0 ]; do
|
||||
x--disable-openssl|x--without-openssl)
|
||||
OPENSSL=no
|
||||
;;
|
||||
x--disable-daemon|x--without-daemon)
|
||||
LIBEVENT=no
|
||||
;;
|
||||
x--disable-gtk|x--without-gtk)
|
||||
GTK=no
|
||||
;;
|
||||
@@ -341,12 +425,32 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
#
|
||||
# libevent settings
|
||||
#
|
||||
echo -n "Daemon: "
|
||||
if [ "$LIBEVENT" = no ]; then
|
||||
echo "disabled"
|
||||
else
|
||||
if libevent_test; then
|
||||
echo "yes"
|
||||
else
|
||||
echo "no (can't find libevent)"
|
||||
fi
|
||||
fi
|
||||
|
||||
#
|
||||
# Math functions
|
||||
#
|
||||
lm_test
|
||||
lrintf_test
|
||||
|
||||
#
|
||||
# String functions
|
||||
#
|
||||
strlcpy_test
|
||||
strlcat_test
|
||||
|
||||
#
|
||||
# Generate config.mk
|
||||
#
|
||||
@@ -359,6 +463,7 @@ BEOS_NETSERVER = $BEOS_NETSERVER
|
||||
PTHREAD = $PTHREAD
|
||||
OPENSSL = $OPENSSL
|
||||
GTK = $GTK
|
||||
DAEMON = $LIBEVENT
|
||||
CC = $CC
|
||||
CFLAGS = $CFLAGS
|
||||
CXX = $CXX
|
||||
@@ -366,6 +471,8 @@ CXXFLAGS = $CXXFLAGS
|
||||
LDFLAGS = $LDFLAGS
|
||||
CFLAGS_GTK = $CFLAGS_GTK
|
||||
LDFLAGS_GTK = $LDFLAGS_GTK
|
||||
CFLAGS_EVENT = $CFLAGS_EVENT
|
||||
LDFLAGS_EVENT = $LDFLAGS_EVENT
|
||||
EOF
|
||||
|
||||
echo
|
||||
|
||||
1055
daemon/client.c
Normal file
1055
daemon/client.c
Normal file
File diff suppressed because it is too large
Load Diff
75
daemon/client.h
Normal file
75
daemon/client.h
Normal file
@@ -0,0 +1,75 @@
|
||||
/******************************************************************************
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2007 Joshua Elsasser
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TR_DAEMON_CLIENT_H
|
||||
#define TR_DAEMON_CLIENT_H
|
||||
|
||||
struct event_base;
|
||||
struct strlist;
|
||||
|
||||
struct cl_info
|
||||
{
|
||||
int id;
|
||||
const char * name;
|
||||
const char * hash;
|
||||
int64_t size;
|
||||
};
|
||||
|
||||
struct cl_stat
|
||||
{
|
||||
int id;
|
||||
const char * state;
|
||||
int64_t eta;
|
||||
int64_t done;
|
||||
int64_t ratedown;
|
||||
int64_t rateup;
|
||||
int64_t totaldown;
|
||||
int64_t totalup;
|
||||
const char * error;
|
||||
const char * errmsg;
|
||||
};
|
||||
|
||||
typedef void ( * cl_infofunc )( const struct cl_info * );
|
||||
typedef void ( * cl_statfunc )( const struct cl_stat * );
|
||||
|
||||
int client_init ( struct event_base * );
|
||||
int client_new_sock ( const char * );
|
||||
int client_new_cmd ( char * const * );
|
||||
int client_quit ( void );
|
||||
int client_addfiles ( struct strlist * );
|
||||
int client_port ( int );
|
||||
int client_automap ( int );
|
||||
int client_pex ( int );
|
||||
int client_downlimit( int );
|
||||
int client_uplimit ( int );
|
||||
int client_dir ( const char * );
|
||||
int client_start ( size_t, const int * );
|
||||
int client_stop ( size_t, const int * );
|
||||
int client_remove ( size_t, const int * );
|
||||
int client_list ( cl_infofunc );
|
||||
int client_info ( cl_infofunc );
|
||||
int client_hashids ( cl_infofunc );
|
||||
int client_status ( cl_statfunc );
|
||||
|
||||
#endif /* TR_DAEMON_CLIENT_H */
|
||||
323
daemon/daemon.c
Normal file
323
daemon/daemon.c
Normal file
@@ -0,0 +1,323 @@
|
||||
/******************************************************************************
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2007 Joshua Elsasser
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/un.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <event.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "errors.h"
|
||||
#include "misc.h"
|
||||
#include "server.h"
|
||||
#include "torrents.h"
|
||||
#include "trcompat.h"
|
||||
#include "version.h"
|
||||
|
||||
static void usage ( const char *, ... );
|
||||
static int readargs ( int, char ** );
|
||||
static int trylocksock ( void );
|
||||
static int getlock ( const char * );
|
||||
static int getsock ( const char * );
|
||||
static void exitcleanup ( void );
|
||||
static void setupsigs ( struct event_base * );
|
||||
static void gotsig ( int, short, void * );
|
||||
|
||||
static int gl_lockfd = -1;
|
||||
static char gl_lockpath[MAXPATHLEN] = "";
|
||||
static int gl_sockfd = -1;
|
||||
static char gl_sockpath[MAXPATHLEN] = "";
|
||||
|
||||
int
|
||||
main( int argc, char ** argv )
|
||||
{
|
||||
struct event_base * evbase;
|
||||
int nofork, sockfd;
|
||||
|
||||
setmyname( argv[0] );
|
||||
nofork = readargs( argc, argv );
|
||||
|
||||
if( !nofork )
|
||||
{
|
||||
if( 0 > daemon( 1, 0 ) )
|
||||
{
|
||||
errnomsg( "failed to daemonize" );
|
||||
exit( 1 );
|
||||
}
|
||||
errsyslog( 1 );
|
||||
}
|
||||
|
||||
atexit( exitcleanup );
|
||||
sockfd = trylocksock();
|
||||
if( 0 > sockfd )
|
||||
{
|
||||
exit( 1 );
|
||||
}
|
||||
|
||||
evbase = event_init();
|
||||
setupsigs( evbase );
|
||||
torrent_init( evbase );
|
||||
server_init( evbase );
|
||||
server_listen( sockfd );
|
||||
|
||||
event_dispatch();
|
||||
/* XXX event_base_dispatch( evbase ); */
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
usage( const char * msg, ... )
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if( NULL != msg )
|
||||
{
|
||||
printf( "%s: ", getmyname() );
|
||||
va_start( ap, msg );
|
||||
vprintf( msg, ap );
|
||||
va_end( ap );
|
||||
printf( "\n" );
|
||||
}
|
||||
|
||||
printf(
|
||||
"usage: %s [-fh]\n"
|
||||
"\n"
|
||||
"Transmission %s (r%d) http://transmission.m0k.org/\n"
|
||||
"A free, lightweight BitTorrent client with a simple, intuitive interface\n"
|
||||
"\n"
|
||||
" -f --foreground Run in the foreground and log to stderr\n"
|
||||
" -h --help Display this message and exit\n"
|
||||
"\n"
|
||||
"To add torrents or set options, use the transmission-remote program.\n",
|
||||
getmyname(), VERSION_STRING, VERSION_REVISION );
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
int
|
||||
readargs( int argc, char ** argv )
|
||||
{
|
||||
char optstr[] = "fh";
|
||||
struct option longopts[] =
|
||||
{
|
||||
{ "foreground", no_argument, NULL, 'f' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
int opt, nofork;
|
||||
|
||||
nofork = 0;
|
||||
|
||||
while( 0 <= ( opt = getopt_long( argc, argv, optstr, longopts, NULL ) ) )
|
||||
{
|
||||
switch( opt )
|
||||
{
|
||||
case 'f':
|
||||
nofork = 1;
|
||||
break;
|
||||
default:
|
||||
usage( NULL );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return nofork;
|
||||
}
|
||||
|
||||
int
|
||||
trylocksock( void )
|
||||
{
|
||||
char path[MAXPATHLEN];
|
||||
int fd;
|
||||
|
||||
confpath( path, sizeof path, NULL, CONF_PATH_TYPE_DAEMON );
|
||||
if( 0 > mkdir( path, 0777 ) && EEXIST != errno )
|
||||
{
|
||||
errnomsg( "failed to create directory: %s", path );
|
||||
return -1;
|
||||
}
|
||||
|
||||
confpath( path, sizeof path, CONF_FILE_LOCK, 0 );
|
||||
fd = getlock( path );
|
||||
if( 0 > fd )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
gl_lockfd = fd;
|
||||
strlcpy( gl_lockpath, path, sizeof gl_lockpath );
|
||||
|
||||
confpath( path, sizeof path, CONF_FILE_SOCKET, 0 );
|
||||
fd = getsock( path );
|
||||
if( 0 > fd )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
gl_sockfd = fd;
|
||||
strlcpy( gl_sockpath, path, sizeof gl_sockpath );
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int
|
||||
getlock( const char * path )
|
||||
{
|
||||
struct flock lk;
|
||||
int fd;
|
||||
char pid[64];
|
||||
|
||||
fd = open( path, O_RDWR | O_CREAT, 0666 );
|
||||
if( 0 > fd )
|
||||
{
|
||||
errnomsg( "failed to open file: %s", path );
|
||||
return -1;
|
||||
}
|
||||
|
||||
bzero( &lk, sizeof lk );
|
||||
lk.l_start = 0;
|
||||
lk.l_len = 0;
|
||||
lk.l_type = F_WRLCK;
|
||||
lk.l_whence = SEEK_SET;
|
||||
if( 0 > fcntl( fd, F_SETLK, &lk ) )
|
||||
{
|
||||
if( EAGAIN == errno )
|
||||
{
|
||||
errnomsg( "another copy of %s is already running", getmyname() );
|
||||
}
|
||||
else
|
||||
{
|
||||
errnomsg( "failed to obtain lock on file: %s", path );
|
||||
}
|
||||
close( fd );
|
||||
return -1;
|
||||
}
|
||||
|
||||
ftruncate( fd, 0 );
|
||||
snprintf( pid, sizeof pid, "%i\n", getpid() );
|
||||
write( fd, pid, strlen( pid ) );
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int
|
||||
getsock( const char * path )
|
||||
{
|
||||
struct sockaddr_un sun;
|
||||
int fd;
|
||||
|
||||
fd = socket( PF_LOCAL, SOCK_STREAM, 0 );
|
||||
if( 0 > fd )
|
||||
{
|
||||
errnomsg( "failed to create socket file: %s", path );
|
||||
return -1;
|
||||
}
|
||||
|
||||
bzero( &sun, sizeof sun );
|
||||
sun.sun_family = AF_LOCAL;
|
||||
strlcpy( sun.sun_path, path, sizeof sun.sun_path );
|
||||
unlink( path );
|
||||
if( 0 > bind( fd, ( struct sockaddr * )&sun, SUN_LEN( &sun ) ) )
|
||||
{
|
||||
/* bind can sometimes fail on the first call */
|
||||
unlink( path );
|
||||
if( 0 > bind( fd, ( struct sockaddr * )&sun, SUN_LEN( &sun ) ) )
|
||||
{
|
||||
errnomsg( "failed to bind socket file: %s", path );
|
||||
close( fd );
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
void
|
||||
exitcleanup( void )
|
||||
{
|
||||
if( 0 <= gl_sockfd )
|
||||
{
|
||||
unlink( gl_sockpath );
|
||||
close( gl_sockfd );
|
||||
}
|
||||
if( 0 <= gl_lockfd )
|
||||
{
|
||||
unlink( gl_lockpath );
|
||||
close( gl_lockfd );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
setupsigs( struct event_base * base /* XXX */ UNUSED )
|
||||
{
|
||||
static struct event ev_int;
|
||||
static struct event ev_quit;
|
||||
static struct event ev_term;
|
||||
|
||||
signal_set( &ev_int, SIGINT, gotsig, NULL );
|
||||
/* XXX event_base_set( base, &ev_int ); */
|
||||
signal_add( &ev_int, NULL );
|
||||
|
||||
signal_set( &ev_quit, SIGINT, gotsig, NULL );
|
||||
/* XXX event_base_set( base, &ev_quit ); */
|
||||
signal_add( &ev_quit, NULL );
|
||||
|
||||
signal_set( &ev_term, SIGINT, gotsig, NULL );
|
||||
/* XXX event_base_set( base, &ev_term ); */
|
||||
signal_add( &ev_term, NULL );
|
||||
|
||||
signal( SIGPIPE, SIG_IGN );
|
||||
signal( SIGHUP, SIG_IGN );
|
||||
}
|
||||
|
||||
void
|
||||
gotsig( int sig, short what UNUSED, void * arg UNUSED )
|
||||
{
|
||||
static int exiting = 0;
|
||||
|
||||
if( !exiting )
|
||||
{
|
||||
exiting = 1;
|
||||
errmsg( "received fatal signal %i, attempting to exit cleanly", sig );
|
||||
torrent_exit( 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
errmsg( "received fatal signal %i while exiting, exiting immediately",
|
||||
sig );
|
||||
signal( sig, SIG_DFL );
|
||||
raise( sig );
|
||||
}
|
||||
}
|
||||
112
daemon/errors.c
Normal file
112
daemon/errors.c
Normal file
@@ -0,0 +1,112 @@
|
||||
/******************************************************************************
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2007 Joshua Elsasser
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include "errors.h"
|
||||
|
||||
static void verrmsg( int, const char *, va_list );
|
||||
|
||||
static int gl_syslog = 0;
|
||||
|
||||
void
|
||||
errsyslog( int useit )
|
||||
{
|
||||
gl_syslog = useit;
|
||||
|
||||
if( useit )
|
||||
{
|
||||
openlog( getmyname(), 0, LOG_DAEMON );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
errmsg( const char * fmt, ... )
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start( ap, fmt );
|
||||
verrmsg( -1, fmt, ap );
|
||||
va_end( ap );
|
||||
}
|
||||
|
||||
void
|
||||
errnomsg( const char * fmt, ... )
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start( ap, fmt );
|
||||
verrmsg( errno, fmt, ap );
|
||||
va_end( ap );
|
||||
}
|
||||
|
||||
void
|
||||
verrmsg( int errnum, const char * fmt, va_list ap )
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
vsnprintf( buf, sizeof buf, fmt, ap );
|
||||
|
||||
if( gl_syslog )
|
||||
{
|
||||
if( 0 > errnum )
|
||||
{
|
||||
syslog( LOG_ERR, "%s", buf );
|
||||
}
|
||||
else
|
||||
{
|
||||
syslog( LOG_ERR, "%s: %s", buf, strerror( errnum ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( 0 > errnum )
|
||||
{
|
||||
fprintf( stderr, "%s: %s\n", getmyname(), buf );
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( stderr, "%s: %s: %s\n", getmyname(), buf,
|
||||
strerror( errnum ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mallocmsg( size_t size )
|
||||
{
|
||||
if( 0 < size )
|
||||
{
|
||||
errmsg( "failed to allocate %i bytes of memory", ( int )size );
|
||||
}
|
||||
else
|
||||
{
|
||||
errmsg( "failed to allocate memory" );
|
||||
}
|
||||
}
|
||||
35
daemon/errors.h
Normal file
35
daemon/errors.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/******************************************************************************
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2007 Joshua Elsasser
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TR_DAEMON_ERRORS_H
|
||||
#define TR_DAEMON_ERRORS_H
|
||||
|
||||
#include "misc.h"
|
||||
|
||||
void errsyslog( int );
|
||||
void errmsg( const char *, ... ) PRINTF( 1, 2 );
|
||||
void errnomsg( const char *, ... ) PRINTF( 1, 2 );
|
||||
void mallocmsg( size_t );
|
||||
|
||||
#endif /* TR_DAEMON_ERRORS_H */
|
||||
1214
daemon/ipc.c
Normal file
1214
daemon/ipc.c
Normal file
File diff suppressed because it is too large
Load Diff
168
daemon/ipc.h
Normal file
168
daemon/ipc.h
Normal file
@@ -0,0 +1,168 @@
|
||||
/******************************************************************************
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2007 Joshua Elsasser
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TR_DAEMON_IPC_H
|
||||
#define TR_DAEMON_IPC_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* yay for typedefs, we can't forward declare benc_val_t or tr_info_t
|
||||
like with structs */
|
||||
#include "bencode.h"
|
||||
#include "transmission.h"
|
||||
|
||||
#define IPC_MIN_MSG_LEN ( 8 )
|
||||
#define IPC_MAX_MSG_LEN ( 0x7fffffff - IPC_MIN_MSG_LEN )
|
||||
|
||||
enum ipc_msg
|
||||
{
|
||||
IPC_MSG_ADDMANYFILES = 0,
|
||||
IPC_MSG_ADDONEFILE,
|
||||
IPC_MSG_AUTOMAP,
|
||||
IPC_MSG_AUTOSTART,
|
||||
IPC_MSG_DIR,
|
||||
IPC_MSG_DOWNLIMIT,
|
||||
IPC_MSG_FAIL,
|
||||
IPC_MSG_GETAUTOMAP,
|
||||
IPC_MSG_GETAUTOSTART,
|
||||
IPC_MSG_GETDIR,
|
||||
IPC_MSG_GETDOWNLIMIT,
|
||||
IPC_MSG_GETINFO,
|
||||
IPC_MSG_GETINFOALL,
|
||||
IPC_MSG_GETPEX,
|
||||
IPC_MSG_GETPORT,
|
||||
IPC_MSG_GETSTAT,
|
||||
IPC_MSG_GETSTATALL,
|
||||
IPC_MSG_GETSUP,
|
||||
IPC_MSG_GETUPLIMIT,
|
||||
IPC_MSG_LOOKUP,
|
||||
IPC_MSG_INFO,
|
||||
IPC_MSG_NOOP,
|
||||
IPC_MSG_NOTSUP,
|
||||
IPC_MSG_PEX,
|
||||
IPC_MSG_PORT,
|
||||
IPC_MSG_QUIT,
|
||||
IPC_MSG_REMOVE,
|
||||
IPC_MSG_REMOVEALL,
|
||||
IPC_MSG_START,
|
||||
IPC_MSG_STARTALL,
|
||||
IPC_MSG_STAT,
|
||||
IPC_MSG_STOP,
|
||||
IPC_MSG_STOPALL,
|
||||
IPC_MSG_OK,
|
||||
IPC_MSG_SUP,
|
||||
IPC_MSG_UPLIMIT,
|
||||
IPC_MSG_VERSION,
|
||||
IPC__MSG_COUNT
|
||||
};
|
||||
|
||||
#define IPC_INF_COMMENT ( 1 << 0 )
|
||||
#define IPC_INF_CREATOR ( 1 << 1 )
|
||||
#define IPC_INF_DATE ( 1 << 2 )
|
||||
#define IPC_INF_FILES ( 1 << 3 )
|
||||
#define IPC_INF_HASH ( 1 << 4 )
|
||||
#define IPC_INF_ID ( 1 << 5 )
|
||||
#define IPC_INF_NAME ( 1 << 6 )
|
||||
#define IPC_INF_PATH ( 1 << 7 )
|
||||
#define IPC_INF_PRIVATE ( 1 << 8 )
|
||||
#define IPC_INF_SAVED ( 1 << 9 )
|
||||
#define IPC_INF_SIZE ( 1 << 10 )
|
||||
#define IPC_INF_TRACKERS ( 1 << 11 )
|
||||
#define IPC_INF__MAX ( 1 << 12 )
|
||||
|
||||
#define IPC_ST_COMPLETED ( 1 << 0 )
|
||||
#define IPC_ST_DOWNSPEED ( 1 << 1 )
|
||||
#define IPC_ST_DOWNTOTAL ( 1 << 2 )
|
||||
#define IPC_ST_ERROR ( 1 << 3 )
|
||||
#define IPC_ST_ERRMSG ( 1 << 4 )
|
||||
#define IPC_ST_ETA ( 1 << 5 )
|
||||
#define IPC_ST_ID ( 1 << 6 )
|
||||
#define IPC_ST_PEERDOWN ( 1 << 7 )
|
||||
#define IPC_ST_PEERFROM ( 1 << 8 )
|
||||
#define IPC_ST_PEERTOTAL ( 1 << 9 )
|
||||
#define IPC_ST_PEERUP ( 1 << 10 )
|
||||
#define IPC_ST_RUNNING ( 1 << 11 )
|
||||
#define IPC_ST_STATE ( 1 << 12 )
|
||||
#define IPC_ST_SWARM ( 1 << 13 )
|
||||
#define IPC_ST_TRACKER ( 1 << 14 )
|
||||
#define IPC_ST_TKDONE ( 1 << 15 )
|
||||
#define IPC_ST_TKLEECH ( 1 << 16 )
|
||||
#define IPC_ST_TKSEED ( 1 << 17 )
|
||||
#define IPC_ST_UPSPEED ( 1 << 18 )
|
||||
#define IPC_ST_UPTOTAL ( 1 << 19 )
|
||||
#define IPC_ST__MAX ( 1 << 20 )
|
||||
|
||||
struct ipc_funcs;
|
||||
struct ipc_info;
|
||||
struct strlist;
|
||||
|
||||
struct ipc_info
|
||||
{
|
||||
struct ipc_funcs * funcs;
|
||||
int vers;
|
||||
};
|
||||
|
||||
#define HASVERS( info ) ( 0 < (info)->vers )
|
||||
|
||||
typedef void ( *trd_msgfunc )( enum ipc_msg, benc_val_t *, int64_t, void * );
|
||||
|
||||
/* any of these functions that can fail may set errno for any of the
|
||||
errors set by malloc() or calloc() */
|
||||
|
||||
/* setup */
|
||||
struct ipc_funcs * ipc_initmsgs ( void );
|
||||
int ipc_addmsg ( struct ipc_funcs *, enum ipc_msg, trd_msgfunc );
|
||||
void ipc_setdefmsg( struct ipc_funcs *, trd_msgfunc );
|
||||
void ipc_freemsgs ( struct ipc_funcs * );
|
||||
void ipc_newcon ( struct ipc_info *, struct ipc_funcs * );
|
||||
|
||||
/* message creation */
|
||||
/* sets errno to EPERM if requested message not supported by protocol vers */
|
||||
benc_val_t * ipc_initval ( struct ipc_info *, enum ipc_msg, int64_t,
|
||||
benc_val_t *, int );
|
||||
uint8_t * ipc_mkval ( benc_val_t *, size_t * );
|
||||
uint8_t * ipc_mkempty ( struct ipc_info *, size_t *, enum ipc_msg,
|
||||
int64_t );
|
||||
uint8_t * ipc_mkint ( struct ipc_info *, size_t *, enum ipc_msg, int64_t,
|
||||
int64_t );
|
||||
uint8_t * ipc_mkstr ( struct ipc_info *, size_t *, enum ipc_msg, int64_t,
|
||||
const char * );
|
||||
uint8_t * ipc_mkvers ( size_t * );
|
||||
uint8_t * ipc_mkgetinfo( struct ipc_info *, size_t *, enum ipc_msg, int64_t,
|
||||
int, const int * );
|
||||
int ipc_addinfo ( benc_val_t *, int, tr_info_t *, int );
|
||||
int ipc_addstat ( benc_val_t *, int, tr_info_t *, tr_stat_t *, int );
|
||||
|
||||
/* sets errno to EINVAL on parse error or
|
||||
EPERM for unsupported protocol version */
|
||||
ssize_t ipc_parse ( struct ipc_info *, uint8_t *, ssize_t, void * );
|
||||
|
||||
/* misc info functions, these will always succeed */
|
||||
int ipc_havemsg ( struct ipc_info *, enum ipc_msg );
|
||||
enum ipc_msg ipc_msgid ( struct ipc_info *, const char * );
|
||||
int ipc_havetags ( struct ipc_info * );
|
||||
int ipc_infotypes( enum ipc_msg, benc_val_t * );
|
||||
const char * ipc_infoname ( enum ipc_msg, int );
|
||||
|
||||
#endif /* TR_DAEMON_IPC_H */
|
||||
454
daemon/ipcproto.txt
Normal file
454
daemon/ipcproto.txt
Normal file
@@ -0,0 +1,454 @@
|
||||
It is assumed the reader is familiar with bencoding, as described in
|
||||
the BitTorrent protocol specification at
|
||||
http://www.bittorrent.org/protocol.html
|
||||
|
||||
Dictionary keys used below will be enclosed in quotation marks, these
|
||||
are used for clarity and are not part of the actual key.
|
||||
|
||||
The IPC protocol is used to allow processes to control or retrieve
|
||||
information from a Transmission frontend, such as transmission-daemon
|
||||
or transmission-gtk. This communication is done over a unix-domain
|
||||
socket file, such as ~/.transmission/daemon/socket. In this document
|
||||
the Transmission frontend will be referred to as the server and the
|
||||
process connecting to it as the client.
|
||||
|
||||
Once a client connects to the server's socket, messages may be
|
||||
exchanged until either end closes the connection. Messages contain an
|
||||
32-bit payload length, encoded as 8 bytes of ASCII hexidecimal,
|
||||
followed by the payload. Upper, lower, or mixed case for the length
|
||||
are all accpetable and must be handled correctly. Payload lengths
|
||||
greater than 2^31 - 8 (ie: 2147483640) are not allowed.
|
||||
|
||||
For version 1, the message payload is a bencoded dictionary, the
|
||||
valid keys and value formats for which are described below. Multiple
|
||||
keys may be used in one message.
|
||||
|
||||
An example version 1 message:
|
||||
|
||||
0000000Ed4:porti9090ee
|
||||
|
||||
For version 2 the message payload is a bencoded list containing a
|
||||
message id string followed by a bencoded value, the format of which is
|
||||
the same for version 1. The value may be followed by an optional
|
||||
bencoded integer, this is a message tag and is described in more
|
||||
detail below.
|
||||
|
||||
An example version 2 message:
|
||||
|
||||
0000001El12:get-info-alll4:hashee
|
||||
|
||||
The same message with a tag:
|
||||
|
||||
00000021l12:get-info-alll4:hashei5ee
|
||||
|
||||
Once the connection is made both the client and server must send a
|
||||
version 1 style message (ie: the payload is a dictionary and may not
|
||||
contain a tag) with the dictionary key "version" and a value formatted
|
||||
as described below. The version should be the first but not
|
||||
necessarily only key in the dictionary. Any other keys should be
|
||||
ignored and not processed as messages. Neither the client nor the
|
||||
server should wait to receive a version message before sending one, it
|
||||
must be sent immediately. No other messages should be sent until the
|
||||
version is received.
|
||||
|
||||
The version value should be a bencoded dictionary containing two keys,
|
||||
"max" and "min". These are the minimum and maximum supported protocol
|
||||
versions, respectively. Communication will take place using the
|
||||
highest protocol version supported by both the client and the server,
|
||||
and is not possible at all if there is no common version. A client may
|
||||
receive a version value that is an integer instead of a dictionary
|
||||
with "min" and "max" keys. This deprecated version format indicates
|
||||
the only version supported by the server.
|
||||
|
||||
An example message containing minimum and maximum versions 1 and 2:
|
||||
|
||||
0000001Dd7:versiond3:mini1e3:maxi2eee
|
||||
|
||||
Tagged messages, only supported in version 2, allow a client to tag a
|
||||
message with a positive non-zero integer. The server is then required
|
||||
to send a response to the message even if none would normally be
|
||||
required, and to tag the response with the same integer. When the
|
||||
server receives a tagged message it must send exactly one message back
|
||||
with the same tag. The client is allowed to use the same tag for
|
||||
multiple messages, even if a response to the first is not received
|
||||
before the second it sent. If a tagged message does not normally
|
||||
require a response then a "succeeded", "failed" or "not-supported"
|
||||
message will be sent back.
|
||||
|
||||
An example tagged message and response:
|
||||
|
||||
00000010l5:startli8eei15ee
|
||||
00000011l8:succeeded0:i15ee
|
||||
|
||||
Some example sessions, including version handshake, are found at the
|
||||
end of this file.
|
||||
|
||||
Dictionary keys are encoded in UTF-8 and case sensitive. Any character
|
||||
except a NUL (0x00) is valid. A server or client need not support
|
||||
every possible key and should silently ignore any that it does not
|
||||
understand.
|
||||
|
||||
If a reference to a boolean is seen, it should be taken to mean an
|
||||
integer with a value of 0 representing false, 1 representing true, and
|
||||
any other value undefined.
|
||||
|
||||
Individual torrents are identified by a unique integer. This integer
|
||||
is only valid for the current connection and may be invalid or refer
|
||||
to another torrent in a future connection. If a torrent is closed it's
|
||||
ID will never be reused to refer to another torrent for at least the
|
||||
duration of the connection. Negative integers or 0 are not valid IDs.
|
||||
|
||||
A list of keys and the format of their values follows. Also listed is
|
||||
the minimum protocol version that the key may be used with.
|
||||
|
||||
|
||||
Key: "addfiles"
|
||||
Version: 1
|
||||
Format: list of strings
|
||||
Example: 8:addfilesl21:/torrents/foo.torrent20:/home/me/bar.torrente
|
||||
Details: Each string is the absolute path to a torrent metainfo file
|
||||
for the server to add. Note that whether or not the torrent
|
||||
metainfo file is copied (allowing the original to be moved or
|
||||
deleted safely) is implementation dependent and may not
|
||||
currently be known or changed with this protocol.
|
||||
|
||||
Key: "addfile-detailed"
|
||||
Version: 2
|
||||
Format: dict
|
||||
Example: 16:addfile-detailedde
|
||||
Details: Dictionary containing information about a torrent for the
|
||||
server to add. Valid keys include:
|
||||
"file" string, filename of torrent metainfo file
|
||||
"data" string, contents of a torrent metainfo file
|
||||
"directory" string, directory for data files for the torrent
|
||||
"autostart" boolean, start the torrent automatically
|
||||
Either "file" or "data" is required, but both are not allowed.
|
||||
|
||||
Key: "automap"
|
||||
Version: 2
|
||||
Format: boolean
|
||||
Example: 7:automapi1e
|
||||
Details: Enable (1) or disable (0) automatic port mapping on the
|
||||
server. Other integer values will likely be treated as 1 but
|
||||
this shold not be relied upon.
|
||||
|
||||
Key: "autostart"
|
||||
Version: 2
|
||||
Format: boolean
|
||||
Example: 9:autostarti0e
|
||||
Details: Enable (1) or disable (0) automatic starting of new torrents
|
||||
added via "addfiles" message.
|
||||
|
||||
Key: "directory"
|
||||
Version: 2
|
||||
Format: string
|
||||
Example: 9:directory21:/home/roger/downloads
|
||||
Details: Set the default directory used for any torrents added in the future.
|
||||
|
||||
Key: "downlimit"
|
||||
Version: 2
|
||||
Format: int
|
||||
Example: 9:downlimiti100e
|
||||
Details: Set the server's download limit in kilobytes per second.
|
||||
Negative values are interpreted as no limit.
|
||||
|
||||
Key: "failed"
|
||||
Version: 2
|
||||
Format: string
|
||||
Example: 6:failed17:permission denied
|
||||
Details: Sent in response to a tagged message to indicate failure.
|
||||
|
||||
Key: "get-automap"
|
||||
Version: 2
|
||||
Format: value is ignored
|
||||
Example: 11:get-automap0:
|
||||
Details: Requests that an "automap" message be sent back.
|
||||
|
||||
Key: "get-autostart"
|
||||
Version: 2
|
||||
Format: value is ignored
|
||||
Example: 13:get-autostart0:
|
||||
Details: Requests that an "autostart" message be sent back.
|
||||
|
||||
Key: "get-directory"
|
||||
Version: 2
|
||||
Format: value is ignored
|
||||
Example: 13:get-directory
|
||||
Details: Requests that an "directory" message be sent back.
|
||||
|
||||
Key: "get-downlimit"
|
||||
Version: 2
|
||||
Format: value is ignored
|
||||
Example: 13:get-downlimit0:
|
||||
Details: Requests that a "downlimit" message be sent back.
|
||||
|
||||
Key: "get-info"
|
||||
Version: 2
|
||||
Format: dict with keys "id" and "type" for lists of ints and strings
|
||||
Example: 8:get-infod2:idli4ei7ei2ee4:typel4:hash4:nameee
|
||||
Details: Requests that the server send back an "info" message with
|
||||
info on all the torrent IDs in "id". The "type" key requests
|
||||
what info will be returned. See below for valid values to use
|
||||
here. Since the torrent ID is always included in an "info"
|
||||
message an empty or missing "type" key will cause only the ID
|
||||
to be returned. An "info" message will always be sent back,
|
||||
even if it is empty.
|
||||
|
||||
Key: "get-info-all"
|
||||
Version: 2
|
||||
Format: list of strings
|
||||
Example: 12:get-info-alll4:hash4:namee
|
||||
Details: Same as "getinfo" message with all torrent IDs specified.
|
||||
|
||||
Key: "get-pex"
|
||||
Version: 2
|
||||
Format: value is ignored
|
||||
Example: 7:get-pex0:
|
||||
Details: Requests that a "pex" message be sent back.
|
||||
|
||||
Key: "get-port"
|
||||
Version: 2
|
||||
Format: value is ignored
|
||||
Example: 8:get-port0:
|
||||
Details: Requests that a "port" message be sent back.
|
||||
|
||||
Key: "get-status"
|
||||
Version: 2
|
||||
Format: dict with keys "id" and "type" for lists of ints and strings
|
||||
Example: 10:get-statusd2:idli4ei7ei2ee4:typel4:hash4:nameee
|
||||
Details: Same as "get-info" message except status type strings are used
|
||||
instead and the server sends back a "status" message.
|
||||
|
||||
Key: "get-status-all"
|
||||
Version: 2
|
||||
Format: list of strings
|
||||
Example: 14:get-status-alll4:hash4:namee
|
||||
Details: Same as "get-status" message with all torrent IDs specified.
|
||||
|
||||
Key: "get-supported"
|
||||
Version: 2
|
||||
Format: list of strings
|
||||
Example: 13:get-supportedl6:lookup8:get-porte
|
||||
Details: Request that a "supported" message be returned with whichever
|
||||
of the given message keys are supported.
|
||||
|
||||
Key: "get-uplimit"
|
||||
Version: 2
|
||||
Format: value is ignored
|
||||
Example: 11:get-uplimit0:
|
||||
Details: Requests that an "uplimit" message be sent back.
|
||||
|
||||
Key: "lookup"
|
||||
Version: 2
|
||||
Format: list of strings
|
||||
Example: 6:lookupl40:0f16ea6965ee5133ea4dbb1e7f516e9fcf3d899ee
|
||||
Details: Request that the server send back an "info" message with "id"
|
||||
and "hash" keys for any torrents with the given hashes.
|
||||
|
||||
Key: "info"
|
||||
Version: 2
|
||||
Format: list of dictionaries
|
||||
Example: 4:infold2:idi3e4:name3:fooed2:idi9e4:name3:baree
|
||||
Details: A list containing information for several torrents. The
|
||||
dictionaries always contain at least an "id" key with the
|
||||
integer ID for the torrent, other possible values are listed
|
||||
below.
|
||||
|
||||
Key: "noop"
|
||||
Version: 2
|
||||
Format: value is ignored
|
||||
Example: 4:noop0:
|
||||
Details: This does nothing but keep the connection alive. With a tag
|
||||
it may be used as a ping.
|
||||
|
||||
Key: "not-supported"
|
||||
Version: 2
|
||||
Format: value is ignored
|
||||
Example: 13:not-supported0:
|
||||
Details: Sent in response to a tagged message to indicated that the
|
||||
message was not supported.
|
||||
|
||||
Key: "pex"
|
||||
Version: 2
|
||||
Format: boolean
|
||||
Example: 3:pexi0e
|
||||
Details: Enables or disables peer exchange.
|
||||
|
||||
Key: "port"
|
||||
Version: 2
|
||||
Format: int between 0 and 65535
|
||||
Example: 4:porti9090e
|
||||
Details: Change the port the server uses to listen for incoming peer
|
||||
connections.
|
||||
|
||||
Key: "quit"
|
||||
Version: 1
|
||||
Format: value is ignored
|
||||
Example: 4:quit0:
|
||||
Details: Cause the server to quit.
|
||||
|
||||
Key: "remove"
|
||||
Version: 2
|
||||
Format: list of torrent ID ints
|
||||
Example: 5:removeli3ei8ei6ee
|
||||
Details: Stop and remove the specified torrents. Note that whether or
|
||||
not the downloaded data or the original torrent files will be
|
||||
removed is implementation dependent and may not currently be
|
||||
known or changed with this protocol. If a saved copy of the
|
||||
torrent file has been made then it will always be deleted.
|
||||
|
||||
Key: "remove-all"
|
||||
Version: 2
|
||||
Format: value is ignored
|
||||
Example: 10:remove-all0:
|
||||
Details: Like "remove" with all torrent IDs specified.
|
||||
|
||||
Key: "start"
|
||||
Version: 2
|
||||
Format: list of torrent ID ints
|
||||
Example: 5:startli3ei8ei6ee
|
||||
Details: List of torrent IDs to start.
|
||||
|
||||
Key: "start-all"
|
||||
Version: 2
|
||||
Format: value is ignored
|
||||
Example: 9:start-all0:
|
||||
Details: Start all torrents.
|
||||
|
||||
Key: "status"
|
||||
Version: 2
|
||||
Format: list of dictionaries
|
||||
Example: 4:infold2:idi3e4:name3:fooed2:idi9e4:name3:baree
|
||||
Details: Same as "info" message except status type keys are used.
|
||||
|
||||
Key: "stop"
|
||||
Version: 2
|
||||
Format: list of torrent ID ints
|
||||
Example: 4:stopli3ei8ei6ee
|
||||
Details: List of torrent IDs to stop.
|
||||
|
||||
Key: "stop-all"
|
||||
Version: 2
|
||||
Format: value is ignored
|
||||
Example: 8:stop-all0:
|
||||
Details: Stop all torrents.
|
||||
|
||||
Key: "succeeded"
|
||||
Version: 2
|
||||
Format: value is ignored
|
||||
Example: 8:succeeded0:
|
||||
Details: This is used to indicate that a tagged message was processed
|
||||
successfully.
|
||||
|
||||
Key: "supported"
|
||||
Version: 2
|
||||
Format: list of strings
|
||||
Example: 9:supportedl6:lookupe
|
||||
Details: Sent in response to a "get-supported" message, indicates that
|
||||
the given messages ate supported.
|
||||
|
||||
Key: "uplimit"
|
||||
Version: 2
|
||||
Format: int
|
||||
Example: 7:uplimiti20e
|
||||
Details: Set the server's upload limit in kilobytes per second.
|
||||
Negative values are interpreted as no limit.
|
||||
|
||||
|
||||
Info types for "get-info" and "info" messages. The only type for which
|
||||
support is mandatory is "id".
|
||||
|
||||
"id" integer, torrent's ID for this connection
|
||||
"hash" SHA-1 info hash as a 40-char hex string
|
||||
"name" string, torrent name
|
||||
"path" string, path to .torrent file
|
||||
"saved" boolean, true if a copy of this torrent was saved
|
||||
"private" boolean, true if the torrent is private
|
||||
"trackers" a list of lists of dictionaries containing tracker info:
|
||||
"address" string, hostname or ip address of tracker
|
||||
"port" integer, port for tracker
|
||||
"announce" string, announce url on tracker
|
||||
"scrape" string, scrape url on tracker, may be absent
|
||||
"comment" string, comment from torrent file
|
||||
"creator" string, creator of torrent file
|
||||
"date" integer, date of torrent creation (unix time_t format)
|
||||
"size" integer, total size of all files in bytes
|
||||
"files" list of dictionaries for the files in this torrent:
|
||||
"name" string, name of file
|
||||
"size" integer, size of file in bytes
|
||||
|
||||
|
||||
Status types for "get-status" and "status" messages. The only type for
|
||||
which support is mandatory is "id".
|
||||
|
||||
"completed" integer, bytes of data downloaded and verified
|
||||
"download-speed" integer, download speed in bytes per second
|
||||
"download-total" integer, total bytes downloaded so far
|
||||
"error" string, one of the following:
|
||||
"assert" something happened that shouldn't
|
||||
"io-parent" I dunno
|
||||
"io-permissions" filesystem permission error probably
|
||||
"io-other" other filesystem i/o error
|
||||
"tracker-error" tracker returned error message
|
||||
"tracker-warning" tracker returned warning message
|
||||
"other" other error
|
||||
zero-length or missing string indicates no error
|
||||
"error-message" string, printable error message
|
||||
"eta" integer, estimated seconds until downloading is finished
|
||||
"id" integer, torrent's ID for this connection
|
||||
"peers-downloading" integer, peers downloading from us
|
||||
"peers-from" dict with the following int keys, peer connection sources:
|
||||
"incoming" peers connected to our listening port
|
||||
"tracker" peers discovered from tracker
|
||||
"cache" peers retrieved from on-disk cache
|
||||
"pex" peers discovered via peer exchange
|
||||
"peers-total" integer, total connected peers
|
||||
"peers-uploading" integer, peers uploading to us
|
||||
"running" boolean, false if torrent is stopped or stopping
|
||||
"state" string, one of the following:
|
||||
"checking" performing hash check on file data
|
||||
"downloading" downloading file data
|
||||
"seeding" seeding file data to peers
|
||||
"stopping" contacting tracker to send 'stopped' event
|
||||
"paused" torrent is not active
|
||||
"swarm-speed" integer, swarm speed in bytes per second
|
||||
"tracker" dict with the following keys, current active tracker
|
||||
"address" string, hostname or ip address of tracker
|
||||
"port" integer, port for tracker
|
||||
"announce" string, tracker announce url
|
||||
"scrape" string, tracker scrape url, may be absent
|
||||
"scrape-completed" integer, total completed peers as reported by tracker
|
||||
"scrape-leechers" integer, current leechers as reported by tracker
|
||||
"scrape-seeders" integer, current, seeders as reported by tracker
|
||||
"upload-speed" integer, upload speed in bytes per second
|
||||
"upload-total" integer, total bytes uploaded so far
|
||||
|
||||
|
||||
Examples:
|
||||
|
||||
Data from the client to the server is prefixed with >>> and from
|
||||
server to client with <<<. These prefixes and newlines are added for
|
||||
clarity, they are not actually sent over the socket.
|
||||
|
||||
Quit the server. Note that this is a version 1 client and so version 1
|
||||
messages are used. The value for the quit message here is i0e instead
|
||||
of 0: as used above, however since it it unused any value is allowed.
|
||||
>>> 0000001Dd7:versiond3:mini1e3:maxi1eee
|
||||
<<< 0000001Dd7:versiond3:mini1e3:maxi2eee
|
||||
>>> 0000000Bd4:quiti0ee
|
||||
|
||||
Pause all torrents and disable automapping. Note the server happens to
|
||||
have sendt it's version before the client.
|
||||
<<< 0000001Dd7:versiond3:mini1e3:maxi2eee
|
||||
>>> 0000001Dd7:versiond3:mini1e3:maxi2eee
|
||||
>>> 0000000El8:stop-all0:e
|
||||
|
||||
Change upload and download limits with tagged responses. Note that the
|
||||
server is allowed to return responses out of order, even when the
|
||||
messages are not tagged.
|
||||
>>> 0000001Dd7:versiond3:mini1e3:maxi2eee
|
||||
<<< 0000001Dd7:versiond3:mini1e3:maxi2eee
|
||||
>>> 00000017l9:downlimiti100ei100el
|
||||
>>> 00000017l9:downlimiti100ei101el
|
||||
<<< 00000014l8:succeeded0:i101el
|
||||
<<< 00000014l8:succeeded0:i100el
|
||||
203
daemon/misc.c
Normal file
203
daemon/misc.c
Normal file
@@ -0,0 +1,203 @@
|
||||
/******************************************************************************
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2007 Joshua Elsasser
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "errors.h"
|
||||
#include "misc.h"
|
||||
#include "transmission.h"
|
||||
#include "trcompat.h"
|
||||
|
||||
static void pushdir ( char *, const char *, size_t );
|
||||
|
||||
static char gl_myname[256];
|
||||
|
||||
void
|
||||
setmyname( const char * argv0 )
|
||||
{
|
||||
const char * name;
|
||||
|
||||
name = strrchr( argv0, '/' );
|
||||
if( NULL == name || '\0' == *(++name) )
|
||||
{
|
||||
name = argv0;
|
||||
}
|
||||
strlcpy( gl_myname, name, sizeof gl_myname );
|
||||
}
|
||||
|
||||
const char *
|
||||
getmyname( void )
|
||||
{
|
||||
return gl_myname;
|
||||
}
|
||||
|
||||
void
|
||||
pushdir( char * path, const char * file, size_t size )
|
||||
{
|
||||
size_t off;
|
||||
|
||||
off = strlen( path );
|
||||
if( 0 < off && off + 1 < size && '/' != path[off-1] )
|
||||
{
|
||||
path[off] = '/';
|
||||
path[off+1] = '\0';
|
||||
}
|
||||
strlcat( path, file, size );
|
||||
}
|
||||
|
||||
void
|
||||
confpath( char * buf, size_t len, const char * file, enum confpathtype type )
|
||||
{
|
||||
const char * typestr;
|
||||
|
||||
switch( type )
|
||||
{
|
||||
case CONF_PATH_TYPE_DAEMON:
|
||||
typestr = "daemon";
|
||||
break;
|
||||
case CONF_PATH_TYPE_GTK:
|
||||
typestr = "gtk";
|
||||
break;
|
||||
default:
|
||||
assert( 0 );
|
||||
break;
|
||||
}
|
||||
|
||||
strlcpy( buf, tr_getPrefsDirectory(), len );
|
||||
pushdir( buf, typestr, len );
|
||||
if( NULL != file )
|
||||
{
|
||||
pushdir( buf, file, len );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
absolutify( char * buf, size_t len, const char * path )
|
||||
{
|
||||
size_t off;
|
||||
|
||||
if( '/' == path[0] )
|
||||
{
|
||||
strlcpy( buf, path, len );
|
||||
return;
|
||||
}
|
||||
|
||||
getcwd( buf, len );
|
||||
off = strlen( buf );
|
||||
if( 0 < off && len > off + 1 && '/' != buf[off] )
|
||||
{
|
||||
buf[off] = '/';
|
||||
off++;
|
||||
buf[off] = '\0';
|
||||
}
|
||||
strlcat( buf, path, len );
|
||||
}
|
||||
|
||||
int
|
||||
writefile( const char * name, uint8_t * buf, ssize_t len )
|
||||
{
|
||||
int fd;
|
||||
ssize_t res;
|
||||
|
||||
fd = open( name, O_WRONLY | O_CREAT | O_TRUNC, 0666 );
|
||||
if( 0 > fd )
|
||||
{
|
||||
errnomsg( "failed to open %s for writing", name );
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = write( fd, buf, len );
|
||||
if( 0 > res )
|
||||
{
|
||||
errnomsg( "failed to write to %s", name );
|
||||
return -1;
|
||||
}
|
||||
if( len > res )
|
||||
{
|
||||
errmsg( "failed to write all data to %s", name );
|
||||
return -1;
|
||||
}
|
||||
|
||||
close( fd );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t *
|
||||
readfile( const char * name, size_t * len )
|
||||
{
|
||||
struct stat sb;
|
||||
int fd;
|
||||
uint8_t * buf;
|
||||
ssize_t res;
|
||||
|
||||
fd = open( name, O_RDONLY );
|
||||
if( 0 > fd )
|
||||
{
|
||||
if( ENOENT != errno )
|
||||
{
|
||||
errnomsg( "failed to open %s for reading", name );
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if( 0 > fstat( fd, &sb ) )
|
||||
{
|
||||
errnomsg( "failed to stat %s", name );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buf = malloc( sb.st_size );
|
||||
if( NULL == buf )
|
||||
{
|
||||
mallocmsg( sb.st_size );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
res = read( fd, buf, sb.st_size );
|
||||
if( 0 > res )
|
||||
{
|
||||
errnomsg( "failed to read from %s", name );
|
||||
free( buf );
|
||||
return NULL;
|
||||
}
|
||||
if( res < sb.st_size )
|
||||
{
|
||||
errmsg( "failed to read all data from %s", name );
|
||||
free( buf );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
close( fd );
|
||||
*len = res;
|
||||
|
||||
return buf;
|
||||
}
|
||||
139
daemon/misc.h
Normal file
139
daemon/misc.h
Normal file
@@ -0,0 +1,139 @@
|
||||
/******************************************************************************
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2007 Joshua Elsasser
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TR_DAEMON_MISC_H
|
||||
#define TR_DAEMON_MISC_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "bsdqueue.h"
|
||||
|
||||
#define CONF_FILE_LOCK "lock"
|
||||
#define CONF_FILE_SOCKET "socket"
|
||||
#define CONF_FILE_STATE "state"
|
||||
|
||||
enum confpathtype
|
||||
{
|
||||
CONF_PATH_TYPE_DAEMON,
|
||||
CONF_PATH_TYPE_GTK,
|
||||
};
|
||||
|
||||
struct bufferevent;
|
||||
|
||||
#ifdef __GNUC__
|
||||
# define UNUSED __attribute__((unused))
|
||||
#else
|
||||
# define UNUSED
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
# define PRINTF( fmt, args ) __attribute__((format (printf, fmt, args)))
|
||||
#else
|
||||
# define PRINTF( fmt, args )
|
||||
#endif
|
||||
|
||||
#define ARRAYLEN( ary ) ( sizeof( ary ) / sizeof( (ary)[0] ) )
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN( aa, bb ) ( (aa) < (bb) ? (aa) : (bb) )
|
||||
#endif
|
||||
#ifndef MAX
|
||||
#define MAX( aa, bb ) ( (aa) > (bb) ? (aa) : (bb) )
|
||||
#endif
|
||||
|
||||
#undef NULL
|
||||
#define NULL ( ( void * )0 )
|
||||
|
||||
#define TORRENT_ID_VALID( id ) ( 0 < (id) && INT_MAX > (id) )
|
||||
|
||||
#define SAFEFREE( ptr ) \
|
||||
do \
|
||||
{ \
|
||||
int saved = errno; \
|
||||
free( ptr ); \
|
||||
errno = saved; \
|
||||
} \
|
||||
while( 0 )
|
||||
#define SAFEFREESTRLIST( ptr ) \
|
||||
do \
|
||||
{ \
|
||||
int saved = errno; \
|
||||
FREESTRLIST( ptr ); \
|
||||
errno = saved; \
|
||||
} \
|
||||
while( 0 )
|
||||
#define SAFEBENCFREE( val ) \
|
||||
do \
|
||||
{ \
|
||||
int saved = errno; \
|
||||
tr_bencFree( val ); \
|
||||
errno = saved; \
|
||||
} \
|
||||
while( 0 )
|
||||
|
||||
#define INTCMP_FUNC( name, type, id ) \
|
||||
int \
|
||||
name( struct type * _icf_first, struct type * _icf_second ) \
|
||||
{ \
|
||||
if( _icf_first->id < _icf_second->id ) \
|
||||
{ \
|
||||
return -1; \
|
||||
} \
|
||||
else if( _icf_first->id > _icf_second->id ) \
|
||||
{ \
|
||||
return 1; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
return 0; \
|
||||
} \
|
||||
}
|
||||
|
||||
struct stritem
|
||||
{
|
||||
char * str;
|
||||
SLIST_ENTRY( stritem ) next;
|
||||
};
|
||||
|
||||
SLIST_HEAD( strlist, stritem );
|
||||
|
||||
#define FREESTRLIST( _fl_head ) \
|
||||
while( !SLIST_EMPTY( _fl_head ) ) \
|
||||
{ \
|
||||
struct stritem * _fl_dead = SLIST_FIRST( _fl_head ); \
|
||||
SLIST_REMOVE_HEAD( _fl_head, next ); \
|
||||
free( _fl_dead->str ); \
|
||||
free( _fl_dead ); \
|
||||
}
|
||||
|
||||
void setmyname ( const char * );
|
||||
const char * getmyname ( void );
|
||||
void confpath ( char *, size_t, const char *, enum confpathtype );
|
||||
void absolutify( char *, size_t, const char * );
|
||||
int writefile ( const char *, uint8_t *, ssize_t );
|
||||
uint8_t * readfile ( const char *, size_t * );
|
||||
|
||||
#endif /* TR_DAEMON_MISC_H */
|
||||
324
daemon/proxy.c
Normal file
324
daemon/proxy.c
Normal file
@@ -0,0 +1,324 @@
|
||||
/******************************************************************************
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2007 Joshua Elsasser
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/un.h>
|
||||
#include <event.h>
|
||||
#include <getopt.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "errors.h"
|
||||
#include "misc.h"
|
||||
#include "transmission.h"
|
||||
|
||||
static void usage ( const char *, ... );
|
||||
static enum confpathtype readargs ( int, char ** );
|
||||
static int makesock ( enum confpathtype );
|
||||
static void inread ( struct bufferevent *, void * );
|
||||
static void noop ( struct bufferevent *, void * );
|
||||
static void inerr ( struct bufferevent *, short, void * );
|
||||
static void wtf ( struct bufferevent *, void * );
|
||||
static void outerr ( struct bufferevent *, short, void * );
|
||||
static void sockread ( struct bufferevent *, void * );
|
||||
static void sockwrite( struct bufferevent *, void * );
|
||||
static void sockerr ( struct bufferevent *, short, void * );
|
||||
|
||||
static struct bufferevent * gl_in = NULL;
|
||||
static struct bufferevent * gl_out = NULL;
|
||||
static struct bufferevent * gl_sock = NULL;
|
||||
|
||||
int
|
||||
main( int argc, char ** argv )
|
||||
{
|
||||
struct event_base * base;
|
||||
enum confpathtype type;
|
||||
int sockfd;
|
||||
|
||||
setmyname( argv[0] );
|
||||
type = readargs( argc, argv );
|
||||
base = event_init();
|
||||
|
||||
sockfd = makesock( type );
|
||||
if( 0 > sockfd )
|
||||
{
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
gl_in = bufferevent_new( STDIN_FILENO, inread, noop, inerr, NULL );
|
||||
if( NULL == gl_in )
|
||||
{
|
||||
errnomsg( "failed to set up event buffer for stdin" );
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
/* XXX bufferevent_base_set( base, gl_in ); */
|
||||
bufferevent_enable( gl_in, EV_READ );
|
||||
bufferevent_disable( gl_in, EV_WRITE );
|
||||
|
||||
gl_out = bufferevent_new( STDOUT_FILENO, wtf, noop, outerr, NULL );
|
||||
if( NULL == gl_in )
|
||||
{
|
||||
errnomsg( "failed to set up event buffer for stdin" );
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
/* XXX bufferevent_base_set( base, gl_out ); */
|
||||
bufferevent_disable( gl_out, EV_READ );
|
||||
bufferevent_enable( gl_out, EV_WRITE );
|
||||
|
||||
gl_sock = bufferevent_new( sockfd, sockread, sockwrite, sockerr, NULL );
|
||||
if( NULL == gl_in )
|
||||
{
|
||||
errnomsg( "failed to set up event buffer for stdin" );
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
/* XXX bufferevent_base_set( base, gl_sock ); */
|
||||
bufferevent_enable( gl_sock, EV_READ );
|
||||
bufferevent_enable( gl_sock, EV_WRITE );
|
||||
|
||||
event_dispatch();
|
||||
/* XXX event_base_dispatch( base ); */
|
||||
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
void
|
||||
usage( const char * msg, ... )
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if( NULL != msg )
|
||||
{
|
||||
printf( "%s: ", getmyname() );
|
||||
va_start( ap, msg );
|
||||
vprintf( msg, ap );
|
||||
va_end( ap );
|
||||
printf( "\n" );
|
||||
}
|
||||
|
||||
printf(
|
||||
"usage: %s [options] [files]...\n"
|
||||
"\n"
|
||||
"Transmission %s (r%d) http://transmission.m0k.org/\n"
|
||||
"A free, lightweight BitTorrent client with a simple, intuitive interface.\n"
|
||||
"\n"
|
||||
" -h --help Display this message and exit\n"
|
||||
" -t --type daemon Use the daemon frontend, transmission-daemon\n"
|
||||
" -t --type gtk Use the GTK+ frontend, transmission-gtk\n",
|
||||
getmyname(), VERSION_STRING, VERSION_REVISION );
|
||||
exit( EXIT_SUCCESS );
|
||||
}
|
||||
|
||||
enum confpathtype
|
||||
readargs( int argc, char ** argv )
|
||||
{
|
||||
char optstr[] = "ht:";
|
||||
struct option longopts[] =
|
||||
{
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "type", required_argument, NULL, 't' },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
enum confpathtype type;
|
||||
int opt;
|
||||
|
||||
type = CONF_PATH_TYPE_DAEMON;
|
||||
|
||||
while( 0 <= ( opt = getopt_long( argc, argv, optstr, longopts, NULL ) ) )
|
||||
{
|
||||
switch( opt )
|
||||
{
|
||||
case 't':
|
||||
if( 0 == strcasecmp( "daemon", optarg ) )
|
||||
{
|
||||
type = CONF_PATH_TYPE_DAEMON;
|
||||
}
|
||||
else if( 0 == strcasecmp( "gtk", optarg ) )
|
||||
{
|
||||
type = CONF_PATH_TYPE_GTK;
|
||||
}
|
||||
else
|
||||
{
|
||||
usage( "invalid type: %s", optarg );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
usage( NULL );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
int
|
||||
makesock( enum confpathtype type )
|
||||
{
|
||||
struct sockaddr_un sun;
|
||||
int fd;
|
||||
|
||||
bzero( &sun, sizeof sun );
|
||||
sun.sun_family = AF_LOCAL;
|
||||
confpath( sun.sun_path, sizeof sun.sun_path, CONF_FILE_SOCKET, type );
|
||||
|
||||
fd = socket( AF_UNIX, SOCK_STREAM, 0 );
|
||||
if( 0 > fd )
|
||||
{
|
||||
errnomsg( "failed to create socket" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
if( 0 > connect( fd, ( struct sockaddr * )&sun, SUN_LEN( &sun ) ) )
|
||||
{
|
||||
errnomsg( "failed to connect to socket file: %s", sun.sun_path );
|
||||
close( fd );
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
void
|
||||
inread( struct bufferevent * ev UNUSED, void * arg UNUSED )
|
||||
{
|
||||
bufferevent_write_buffer( gl_sock, EVBUFFER_INPUT( gl_in ) );
|
||||
}
|
||||
|
||||
void
|
||||
noop( struct bufferevent * ev UNUSED, void * arg UNUSED )
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
inerr( struct bufferevent * ev UNUSED, short what, void * arg UNUSED )
|
||||
{
|
||||
if( EVBUFFER_EOF & what )
|
||||
{
|
||||
bufferevent_free( gl_in );
|
||||
gl_in = NULL;
|
||||
sockwrite( NULL, NULL );
|
||||
return;
|
||||
}
|
||||
|
||||
if( EVBUFFER_TIMEOUT & what )
|
||||
{
|
||||
errmsg( "timed out reading from stdin" );
|
||||
}
|
||||
else if( EVBUFFER_READ & what )
|
||||
{
|
||||
errmsg( "read error on stdin" );
|
||||
}
|
||||
else if( EVBUFFER_ERROR & what )
|
||||
{
|
||||
errmsg( "error on stdin" );
|
||||
}
|
||||
else
|
||||
{
|
||||
errmsg( "unknown error on stdin: 0x%x", what );
|
||||
}
|
||||
|
||||
exit( EXIT_FAILURE );
|
||||
}
|
||||
|
||||
void
|
||||
wtf( struct bufferevent * ev, void * arg UNUSED )
|
||||
{
|
||||
/* this shouldn't happen, but let's drain the buffer anyway */
|
||||
evbuffer_drain( EVBUFFER_INPUT( ev ),
|
||||
EVBUFFER_LENGTH( EVBUFFER_INPUT( ev ) ) );
|
||||
}
|
||||
|
||||
void
|
||||
outerr( struct bufferevent * ev UNUSED, short what, void * arg UNUSED )
|
||||
{
|
||||
if( EVBUFFER_TIMEOUT & what )
|
||||
{
|
||||
errmsg( "timed out writing to stdout" );
|
||||
}
|
||||
else if( EVBUFFER_WRITE & what )
|
||||
{
|
||||
errmsg( "write error on stdout" );
|
||||
}
|
||||
else if( EVBUFFER_ERROR & what )
|
||||
{
|
||||
errmsg( "error on client stdout" );
|
||||
}
|
||||
else
|
||||
{
|
||||
errmsg( "unknown error on stdout connection: 0x%x", what );
|
||||
}
|
||||
|
||||
exit( EXIT_FAILURE );
|
||||
}
|
||||
|
||||
void
|
||||
sockread( struct bufferevent * ev UNUSED, void * arg UNUSED )
|
||||
{
|
||||
bufferevent_write_buffer( gl_out, EVBUFFER_INPUT( gl_sock ) );
|
||||
}
|
||||
|
||||
void
|
||||
sockwrite( struct bufferevent * ev UNUSED, void * arg UNUSED )
|
||||
{
|
||||
if( NULL == gl_in && 0 == EVBUFFER_LENGTH( EVBUFFER_OUTPUT( gl_sock ) ) )
|
||||
{
|
||||
exit( EXIT_SUCCESS );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sockerr( struct bufferevent * ev UNUSED, short what, void * arg UNUSED )
|
||||
{
|
||||
if( EVBUFFER_EOF & what )
|
||||
{
|
||||
errmsg( "server closed connection" );
|
||||
}
|
||||
else if( EVBUFFER_TIMEOUT & what )
|
||||
{
|
||||
errmsg( "server connection timed out" );
|
||||
}
|
||||
else if( EVBUFFER_READ & what )
|
||||
{
|
||||
errmsg( "read error on server connection" );
|
||||
}
|
||||
else if( EVBUFFER_WRITE & what )
|
||||
{
|
||||
errmsg( "write error on server connection" );
|
||||
}
|
||||
else if( EVBUFFER_ERROR & what )
|
||||
{
|
||||
errmsg( "error on server connection" );
|
||||
}
|
||||
else
|
||||
{
|
||||
errmsg( "unknown error on server connection: 0x%x", what );
|
||||
}
|
||||
|
||||
exit( EXIT_FAILURE );
|
||||
}
|
||||
1027
daemon/remote.c
Normal file
1027
daemon/remote.c
Normal file
File diff suppressed because it is too large
Load Diff
960
daemon/server.c
Normal file
960
daemon/server.c
Normal file
@@ -0,0 +1,960 @@
|
||||
/******************************************************************************
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2007 Joshua Elsasser
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/un.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <event.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "bsdtree.h"
|
||||
#include "bencode.h"
|
||||
#include "errors.h"
|
||||
#include "ipc.h"
|
||||
#include "misc.h"
|
||||
#include "server.h"
|
||||
#include "torrents.h"
|
||||
|
||||
/* time out clients after this many seconds */
|
||||
#define CLIENT_TIMEOUT ( 60 )
|
||||
|
||||
struct client
|
||||
{
|
||||
int fd;
|
||||
struct bufferevent * ev;
|
||||
struct ipc_info ipc;
|
||||
RB_ENTRY( client ) link;
|
||||
};
|
||||
|
||||
RB_HEAD( allclients, client );
|
||||
|
||||
static void newclient( int, short, void * );
|
||||
static void noop ( struct bufferevent *, void * );
|
||||
static void byebye ( struct bufferevent *, short, void * );
|
||||
static void doread ( struct bufferevent *, void * );
|
||||
static int queuemsg ( struct client *, uint8_t *, size_t );
|
||||
static void msgresp ( struct client *, int64_t, enum ipc_msg );
|
||||
static void defmsg ( enum ipc_msg, benc_val_t *, int64_t, void * );
|
||||
static void noopmsg ( enum ipc_msg, benc_val_t *, int64_t, void * );
|
||||
static void addmsg1 ( enum ipc_msg, benc_val_t *, int64_t, void * );
|
||||
static void addmsg2 ( enum ipc_msg, benc_val_t *, int64_t, void * );
|
||||
static void quitmsg ( enum ipc_msg, benc_val_t *, int64_t, void * );
|
||||
static void intmsg ( enum ipc_msg, benc_val_t *, int64_t, void * );
|
||||
static void strmsg ( enum ipc_msg, benc_val_t *, int64_t, void * );
|
||||
static void infomsg ( enum ipc_msg, benc_val_t *, int64_t, void * );
|
||||
static int addinfo ( benc_val_t *, int, int );
|
||||
static int addstat ( benc_val_t *, int, int );
|
||||
static void tormsg ( enum ipc_msg, benc_val_t *, int64_t, void * );
|
||||
static void lookmsg ( enum ipc_msg, benc_val_t *, int64_t, void * );
|
||||
static void prefmsg ( enum ipc_msg, benc_val_t *, int64_t, void * );
|
||||
static void supmsg ( enum ipc_msg, benc_val_t *, int64_t, void * );
|
||||
static int clientcmp( struct client *, struct client * );
|
||||
|
||||
RB_GENERATE_STATIC( allclients, client, link, clientcmp )
|
||||
INTCMP_FUNC( clientcmp, client, ev )
|
||||
|
||||
static struct event_base * gl_base = NULL;
|
||||
static struct ipc_funcs * gl_tree = NULL;
|
||||
static int gl_exiting = 0;
|
||||
static struct allclients gl_clients = RB_INITIALIZER( &gl_clients );
|
||||
|
||||
int
|
||||
server_init( struct event_base * base )
|
||||
{
|
||||
assert( NULL == gl_base && NULL == gl_tree );
|
||||
gl_base = base;
|
||||
gl_tree = ipc_initmsgs();
|
||||
if( NULL == gl_tree )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if( 0 > ipc_addmsg( gl_tree, IPC_MSG_ADDMANYFILES, addmsg1 ) ||
|
||||
0 > ipc_addmsg( gl_tree, IPC_MSG_ADDONEFILE, addmsg2 ) ||
|
||||
0 > ipc_addmsg( gl_tree, IPC_MSG_AUTOMAP, intmsg ) ||
|
||||
0 > ipc_addmsg( gl_tree, IPC_MSG_AUTOSTART, intmsg ) ||
|
||||
0 > ipc_addmsg( gl_tree, IPC_MSG_DOWNLIMIT, intmsg ) ||
|
||||
0 > ipc_addmsg( gl_tree, IPC_MSG_DIR, strmsg ) ||
|
||||
0 > ipc_addmsg( gl_tree, IPC_MSG_GETAUTOMAP, prefmsg ) ||
|
||||
0 > ipc_addmsg( gl_tree, IPC_MSG_GETAUTOSTART, prefmsg ) ||
|
||||
0 > ipc_addmsg( gl_tree, IPC_MSG_GETDOWNLIMIT, prefmsg ) ||
|
||||
0 > ipc_addmsg( gl_tree, IPC_MSG_GETDIR, prefmsg ) ||
|
||||
0 > ipc_addmsg( gl_tree, IPC_MSG_GETINFO, infomsg ) ||
|
||||
0 > ipc_addmsg( gl_tree, IPC_MSG_GETINFOALL, infomsg ) ||
|
||||
0 > ipc_addmsg( gl_tree, IPC_MSG_GETPEX, prefmsg ) ||
|
||||
0 > ipc_addmsg( gl_tree, IPC_MSG_GETPORT, prefmsg ) ||
|
||||
0 > ipc_addmsg( gl_tree, IPC_MSG_GETSTAT, infomsg ) ||
|
||||
0 > ipc_addmsg( gl_tree, IPC_MSG_GETSTATALL, infomsg ) ||
|
||||
0 > ipc_addmsg( gl_tree, IPC_MSG_GETUPLIMIT, prefmsg ) ||
|
||||
0 > ipc_addmsg( gl_tree, IPC_MSG_GETSUP, supmsg ) ||
|
||||
0 > ipc_addmsg( gl_tree, IPC_MSG_LOOKUP, lookmsg ) ||
|
||||
0 > ipc_addmsg( gl_tree, IPC_MSG_NOOP, noopmsg ) ||
|
||||
0 > ipc_addmsg( gl_tree, IPC_MSG_PEX, intmsg ) ||
|
||||
0 > ipc_addmsg( gl_tree, IPC_MSG_PORT, intmsg ) ||
|
||||
0 > ipc_addmsg( gl_tree, IPC_MSG_QUIT, quitmsg ) ||
|
||||
0 > ipc_addmsg( gl_tree, IPC_MSG_REMOVE, tormsg ) ||
|
||||
0 > ipc_addmsg( gl_tree, IPC_MSG_REMOVEALL, tormsg ) ||
|
||||
0 > ipc_addmsg( gl_tree, IPC_MSG_START, tormsg ) ||
|
||||
0 > ipc_addmsg( gl_tree, IPC_MSG_STARTALL, tormsg ) ||
|
||||
0 > ipc_addmsg( gl_tree, IPC_MSG_STOP, tormsg ) ||
|
||||
0 > ipc_addmsg( gl_tree, IPC_MSG_STOPALL, tormsg ) ||
|
||||
0 > ipc_addmsg( gl_tree, IPC_MSG_UPLIMIT, intmsg ) )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
ipc_setdefmsg( gl_tree, defmsg );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
server_listen( int fd )
|
||||
{
|
||||
struct event * ev;
|
||||
int flags;
|
||||
|
||||
assert( NULL != gl_base );
|
||||
|
||||
flags = fcntl( fd, F_GETFL );
|
||||
if( 0 > flags )
|
||||
{
|
||||
errnomsg( "failed to get flags on socket" );
|
||||
return -1;
|
||||
}
|
||||
if( 0 > fcntl( fd, F_SETFL, flags | O_NONBLOCK ) )
|
||||
{
|
||||
errnomsg( "failed to set flags on socket" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
if( 0 > listen( fd, 5 ) )
|
||||
{
|
||||
errnomsg( "failed to listen on socket" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
ev = malloc( sizeof *ev );
|
||||
if( NULL == ev )
|
||||
{
|
||||
mallocmsg( sizeof *ev );
|
||||
return -1;
|
||||
}
|
||||
|
||||
event_set( ev, fd, EV_READ | EV_PERSIST, newclient, ev );
|
||||
/* XXX event_base_set( gl_base, ev ); */
|
||||
event_add( ev, NULL );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
newclient( int fd, short event UNUSED, void * arg )
|
||||
{
|
||||
struct sockaddr_un sun;
|
||||
struct client * client, * old;
|
||||
socklen_t socklen;
|
||||
struct bufferevent * clev;
|
||||
int clfd;
|
||||
size_t buflen;
|
||||
uint8_t * buf;
|
||||
|
||||
if( gl_exiting )
|
||||
{
|
||||
event_del( arg );
|
||||
return;
|
||||
}
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
client = calloc( 1, sizeof *client );
|
||||
if( NULL == client )
|
||||
{
|
||||
mallocmsg( sizeof *client );
|
||||
return;
|
||||
}
|
||||
|
||||
socklen = sizeof sun;
|
||||
clfd = accept( fd, ( struct sockaddr * )&sun, &socklen );
|
||||
if( 0 > clfd )
|
||||
{
|
||||
if( EWOULDBLOCK != errno && EAGAIN != errno &&
|
||||
ECONNABORTED != errno )
|
||||
{
|
||||
errnomsg( "failed to accept ipc connection" );
|
||||
}
|
||||
free( client );
|
||||
break;
|
||||
}
|
||||
|
||||
clev = bufferevent_new( clfd, doread, noop, byebye, client );
|
||||
if( NULL == clev )
|
||||
{
|
||||
close( clfd );
|
||||
free( client );
|
||||
mallocmsg( -1 );
|
||||
return;
|
||||
}
|
||||
/* XXX bufferevent_base_set( gl_base, clev ); */
|
||||
bufferevent_settimeout( clev, CLIENT_TIMEOUT, CLIENT_TIMEOUT );
|
||||
|
||||
client->fd = clfd;
|
||||
ipc_newcon( &client->ipc, gl_tree );
|
||||
client->ev = clev;
|
||||
old = RB_INSERT( allclients, &gl_clients, client );
|
||||
assert( NULL == old );
|
||||
|
||||
bufferevent_enable( clev, EV_READ );
|
||||
buf = ipc_mkvers( &buflen );
|
||||
if( 0 > queuemsg( client, buf, buflen ) )
|
||||
{
|
||||
free( buf );
|
||||
return;
|
||||
}
|
||||
free( buf );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
noop( struct bufferevent * ev UNUSED, void * arg UNUSED )
|
||||
{
|
||||
/* libevent prior to 1.2 couldn't handle a NULL write callback */
|
||||
}
|
||||
|
||||
void
|
||||
byebye( struct bufferevent * ev, short what, void * arg UNUSED )
|
||||
{
|
||||
struct client * client, key;
|
||||
|
||||
if( !( EVBUFFER_EOF & what ) )
|
||||
{
|
||||
if( EVBUFFER_TIMEOUT & what )
|
||||
{
|
||||
errmsg( "client connection timed out" );
|
||||
}
|
||||
else if( EVBUFFER_READ & what )
|
||||
{
|
||||
errmsg( "read error on client connection" );
|
||||
}
|
||||
else if( EVBUFFER_WRITE & what )
|
||||
{
|
||||
errmsg( "write error on client connection" );
|
||||
}
|
||||
else if( EVBUFFER_ERROR & what )
|
||||
{
|
||||
errmsg( "error on client connection" );
|
||||
}
|
||||
else
|
||||
{
|
||||
errmsg( "unknown error on client connection: 0x%x", what );
|
||||
}
|
||||
}
|
||||
|
||||
bzero( &key, sizeof key );
|
||||
key.ev = ev;
|
||||
client = RB_FIND( allclients, &gl_clients, &key );
|
||||
assert( NULL != client );
|
||||
RB_REMOVE( allclients, &gl_clients, client );
|
||||
bufferevent_free( ev );
|
||||
close( client->fd );
|
||||
free( client );
|
||||
}
|
||||
|
||||
void
|
||||
doread( struct bufferevent * ev, void * arg )
|
||||
{
|
||||
struct client * client = arg;
|
||||
ssize_t res;
|
||||
uint8_t * buf;
|
||||
size_t len;
|
||||
|
||||
assert( !gl_exiting );
|
||||
|
||||
buf = EVBUFFER_DATA( EVBUFFER_INPUT( ev ) );
|
||||
len = EVBUFFER_LENGTH( EVBUFFER_INPUT( ev ) );
|
||||
|
||||
if( IPC_MIN_MSG_LEN > len )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
res = ipc_parse( &client->ipc, buf, len, client );
|
||||
|
||||
if( gl_exiting )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if( 0 > res )
|
||||
{
|
||||
switch( errno )
|
||||
{
|
||||
case EPERM:
|
||||
errmsg( "unsupported protocol version" );
|
||||
break;
|
||||
case EINVAL:
|
||||
errmsg( "protocol parse error" );
|
||||
break;
|
||||
default:
|
||||
errnomsg( "parsing failed" );
|
||||
break;
|
||||
}
|
||||
byebye( ev, EVBUFFER_ERROR, NULL );
|
||||
}
|
||||
else if( 0 < res )
|
||||
{
|
||||
evbuffer_drain( EVBUFFER_INPUT( ev ), res );
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
queuemsg( struct client * client, uint8_t * buf, size_t buflen )
|
||||
{
|
||||
if( NULL == buf )
|
||||
{
|
||||
if( EPERM != errno )
|
||||
{
|
||||
errnomsg( "failed to build message" );
|
||||
byebye( client->ev, EVBUFFER_EOF, NULL );
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if( 0 > bufferevent_write( client->ev, buf, buflen ) )
|
||||
{
|
||||
errnomsg( "failed to buffer %zd bytes of data for write", buflen );
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
msgresp( struct client * client, int64_t tag, enum ipc_msg id )
|
||||
{
|
||||
uint8_t * buf;
|
||||
size_t buflen;
|
||||
|
||||
if( 0 >= tag )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
buf = ipc_mkempty( &client->ipc, &buflen, id, tag );
|
||||
queuemsg( client, buf, buflen );
|
||||
free( buf );
|
||||
}
|
||||
|
||||
void
|
||||
defmsg( enum ipc_msg id UNUSED, benc_val_t * val UNUSED, int64_t tag,
|
||||
void * arg )
|
||||
{
|
||||
struct client * client = arg;
|
||||
|
||||
msgresp( client, tag, IPC_MSG_NOTSUP );
|
||||
}
|
||||
|
||||
void
|
||||
noopmsg( enum ipc_msg id UNUSED, benc_val_t * val UNUSED, int64_t tag,
|
||||
void * arg )
|
||||
{
|
||||
struct client * client = arg;
|
||||
|
||||
msgresp( client, tag, IPC_MSG_OK );
|
||||
}
|
||||
|
||||
void
|
||||
addmsg1( enum ipc_msg id UNUSED, benc_val_t * val, int64_t tag, void * arg )
|
||||
{
|
||||
struct client * client = arg;
|
||||
benc_val_t pk, * added, * file;
|
||||
int ii, tor;
|
||||
size_t buflen;
|
||||
uint8_t * buf;
|
||||
tr_info_t * inf;
|
||||
|
||||
if( NULL == val || TYPE_LIST != val->type )
|
||||
{
|
||||
msgresp( client, tag, IPC_MSG_NOTSUP );
|
||||
return;
|
||||
}
|
||||
|
||||
added = ipc_initval( &client->ipc, IPC_MSG_INFO, tag, &pk, TYPE_LIST );
|
||||
if( NULL == added )
|
||||
{
|
||||
errnomsg( "failed to build message" );
|
||||
byebye( client->ev, EVBUFFER_EOF, NULL );
|
||||
return;
|
||||
}
|
||||
|
||||
for( ii = 0; ii < val->val.l.count; ii++ )
|
||||
{
|
||||
file = &val->val.l.vals[ii];
|
||||
if( TYPE_STR != file->type )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
/* XXX need to somehow inform client of skipped or failed files */
|
||||
tor = torrent_add_file( file->val.s.s, NULL, -1 );
|
||||
if( TORRENT_ID_VALID( tor ) )
|
||||
{
|
||||
inf = torrent_info( tor );
|
||||
if( 0 > ipc_addinfo( added, tor, inf, 0 ) )
|
||||
{
|
||||
errnomsg( "failed to build message" );
|
||||
tr_bencFree( &pk );
|
||||
byebye( client->ev, EVBUFFER_EOF, NULL );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buf = ipc_mkval( &pk, &buflen );
|
||||
tr_bencFree( &pk );
|
||||
queuemsg( client, buf, buflen );
|
||||
free( buf );
|
||||
}
|
||||
|
||||
void
|
||||
addmsg2( enum ipc_msg id UNUSED, benc_val_t * dict, int64_t tag, void * arg )
|
||||
{
|
||||
struct client * client = arg;
|
||||
benc_val_t * val, pk;
|
||||
int tor, start;
|
||||
size_t buflen;
|
||||
uint8_t * buf;
|
||||
const char * dir;
|
||||
tr_info_t * inf;
|
||||
|
||||
if( NULL == dict || TYPE_DICT != dict->type )
|
||||
{
|
||||
msgresp( client, tag, IPC_MSG_NOTSUP );
|
||||
return;
|
||||
}
|
||||
|
||||
val = tr_bencDictFind( dict, "directory" );
|
||||
dir = ( NULL == val || TYPE_STR != val->type ? NULL : val->val.s.s );
|
||||
val = tr_bencDictFind( dict, "autostart" );
|
||||
start = ( NULL == val || TYPE_INT != val->type ? -1 :
|
||||
( val->val.i ? 1 : 0 ) );
|
||||
val = tr_bencDictFind( dict, "data" );
|
||||
if( NULL != val && TYPE_STR == val->type )
|
||||
{
|
||||
/* XXX detect duplicates and return a message indicating so */
|
||||
tor = torrent_add_data( ( uint8_t * )val->val.s.s, val->val.s.i,
|
||||
dir, start );
|
||||
}
|
||||
else
|
||||
{
|
||||
val = tr_bencDictFind( dict, "file" );
|
||||
if( NULL == val || TYPE_STR != val->type )
|
||||
{
|
||||
msgresp( client, tag, IPC_MSG_NOTSUP );
|
||||
return;
|
||||
}
|
||||
/* XXX detect duplicates and return a message indicating so */
|
||||
tor = torrent_add_file( val->val.s.s, dir, start );
|
||||
}
|
||||
|
||||
if( TORRENT_ID_VALID( tor ) )
|
||||
{
|
||||
val = ipc_initval( &client->ipc, IPC_MSG_INFO, tag, &pk, TYPE_LIST );
|
||||
if( NULL == val )
|
||||
{
|
||||
errnomsg( "failed to build message" );
|
||||
byebye( client->ev, EVBUFFER_EOF, NULL );
|
||||
return;
|
||||
}
|
||||
inf = torrent_info( tor );
|
||||
if( 0 > ipc_addinfo( val, tor, inf, 0 ) )
|
||||
{
|
||||
errnomsg( "failed to build message" );
|
||||
tr_bencFree( &pk );
|
||||
byebye( client->ev, EVBUFFER_EOF, NULL );
|
||||
return;
|
||||
}
|
||||
buf = ipc_mkval( &pk, &buflen );
|
||||
tr_bencFree( &pk );
|
||||
queuemsg( client, buf, buflen );
|
||||
free( buf );
|
||||
}
|
||||
else
|
||||
{
|
||||
msgresp( client, tag, IPC_MSG_FAIL );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
quitmsg( enum ipc_msg id UNUSED, benc_val_t * val UNUSED, int64_t tag UNUSED,
|
||||
void * arg UNUSED )
|
||||
{
|
||||
struct client * ii, * next;
|
||||
|
||||
torrent_exit( 0 );
|
||||
gl_exiting = 1;
|
||||
|
||||
for( ii = RB_MIN( allclients, &gl_clients ); NULL != ii; ii = next )
|
||||
{
|
||||
next = RB_NEXT( allclients, &gl_clients, ii );
|
||||
byebye( ii->ev, EVBUFFER_EOF, NULL );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
intmsg( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg )
|
||||
{
|
||||
struct client * client = arg;
|
||||
int num;
|
||||
|
||||
if( NULL == val || TYPE_INT != val->type )
|
||||
{
|
||||
msgresp( client, tag, IPC_MSG_NOTSUP );
|
||||
return;
|
||||
}
|
||||
|
||||
num = MAX( INT_MIN, MIN( INT_MAX, val->val.i ) );
|
||||
switch( id )
|
||||
{
|
||||
case IPC_MSG_AUTOMAP:
|
||||
torrent_enable_port_mapping( num ? 1 : 0 );
|
||||
break;
|
||||
case IPC_MSG_AUTOSTART:
|
||||
torrent_set_autostart( num ? 1 : 0 );
|
||||
break;
|
||||
case IPC_MSG_DOWNLIMIT:
|
||||
torrent_set_downlimit( num );
|
||||
break;
|
||||
case IPC_MSG_PEX:
|
||||
torrent_set_pex( num ? 1 : 0 );
|
||||
break;
|
||||
case IPC_MSG_PORT:
|
||||
torrent_set_port( num );
|
||||
break;
|
||||
case IPC_MSG_UPLIMIT:
|
||||
torrent_set_uplimit( num );
|
||||
break;
|
||||
default:
|
||||
assert( 0 );
|
||||
return;
|
||||
}
|
||||
|
||||
msgresp( client, tag, IPC_MSG_OK );
|
||||
}
|
||||
|
||||
void
|
||||
strmsg( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg )
|
||||
{
|
||||
struct client * client = arg;
|
||||
|
||||
if( NULL == val || TYPE_STR != val->type )
|
||||
{
|
||||
msgresp( client, tag, IPC_MSG_NOTSUP );
|
||||
return;
|
||||
}
|
||||
|
||||
switch( id )
|
||||
{
|
||||
case IPC_MSG_DIR:
|
||||
torrent_set_directory( val->val.s.s );
|
||||
break;
|
||||
default:
|
||||
assert( 0 );
|
||||
return;
|
||||
}
|
||||
|
||||
msgresp( client, tag, IPC_MSG_OK );
|
||||
}
|
||||
|
||||
void
|
||||
infomsg( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg )
|
||||
{
|
||||
struct client * client = arg;
|
||||
uint8_t * buf;
|
||||
size_t buflen;
|
||||
benc_val_t pk, * pkinf, * typelist, * idlist, * idval;
|
||||
int all, types, ii, tor;
|
||||
void * iter;
|
||||
enum ipc_msg respid;
|
||||
int ( * addfunc )( benc_val_t *, int, int );
|
||||
|
||||
all = 0;
|
||||
switch( id )
|
||||
{
|
||||
case IPC_MSG_GETINFOALL:
|
||||
all = 1;
|
||||
/* FALLTHROUGH; */
|
||||
case IPC_MSG_GETINFO:
|
||||
respid = IPC_MSG_INFO;
|
||||
addfunc = addinfo;
|
||||
break;
|
||||
case IPC_MSG_GETSTATALL:
|
||||
all = 1;
|
||||
/* FALLTHROUGH */
|
||||
case IPC_MSG_GETSTAT:
|
||||
respid = IPC_MSG_STAT;
|
||||
addfunc = addstat;
|
||||
break;
|
||||
default:
|
||||
assert( 0 );
|
||||
return;
|
||||
}
|
||||
|
||||
/* initialize packet */
|
||||
pkinf = ipc_initval( &client->ipc, respid, tag, &pk, TYPE_LIST );
|
||||
if( NULL == pkinf )
|
||||
{
|
||||
errnomsg( "failed to build message" );
|
||||
byebye( client->ev, EVBUFFER_EOF, NULL );
|
||||
return;
|
||||
}
|
||||
|
||||
/* add info/status for all torrents */
|
||||
if( all )
|
||||
{
|
||||
if( NULL == val || TYPE_LIST != val->type )
|
||||
{
|
||||
msgresp( client, tag, IPC_MSG_NOTSUP );
|
||||
tr_bencFree( &pk );
|
||||
return;
|
||||
}
|
||||
types = ipc_infotypes( respid, val );
|
||||
iter = NULL;
|
||||
while( NULL != ( iter = torrent_iter( iter, &tor ) ) )
|
||||
{
|
||||
if( 0 > addfunc( pkinf, tor, types ) )
|
||||
{
|
||||
errnomsg( "failed to build message" );
|
||||
tr_bencFree( &pk );
|
||||
byebye( client->ev, EVBUFFER_EOF, NULL );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* add info/status for the requested IDs */
|
||||
else
|
||||
{
|
||||
if( NULL == val || TYPE_DICT != val->type )
|
||||
{
|
||||
msgresp( client, tag, IPC_MSG_NOTSUP );
|
||||
tr_bencFree( &pk );
|
||||
return;
|
||||
}
|
||||
typelist = tr_bencDictFind( val, "type" );
|
||||
idlist = tr_bencDictFind( val, "id" );
|
||||
if( NULL == typelist || TYPE_LIST != typelist->type ||
|
||||
NULL == idlist || TYPE_LIST != idlist->type )
|
||||
{
|
||||
msgresp( client, tag, IPC_MSG_NOTSUP );
|
||||
tr_bencFree( &pk );
|
||||
return;
|
||||
}
|
||||
types = ipc_infotypes( respid, typelist );
|
||||
for( ii = 0; idlist->val.l.count > ii; ii++ )
|
||||
{
|
||||
idval = &idlist->val.l.vals[ii];
|
||||
if( TYPE_INT != idval->type || !TORRENT_ID_VALID( idval->val.i ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
tor = idval->val.i;
|
||||
if( 0 > addfunc( pkinf, idval->val.i, types ) )
|
||||
{
|
||||
errnomsg( "failed to build message" );
|
||||
tr_bencFree( &pk );
|
||||
byebye( client->ev, EVBUFFER_EOF, NULL );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* generate packet data and send it */
|
||||
buf = ipc_mkval( &pk, &buflen );
|
||||
tr_bencFree( &pk );
|
||||
queuemsg( client, buf, buflen );
|
||||
free( buf );
|
||||
}
|
||||
|
||||
int
|
||||
addinfo( benc_val_t * list, int id, int types )
|
||||
{
|
||||
tr_info_t * inf;
|
||||
|
||||
inf = torrent_info( id );
|
||||
if( NULL == inf )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ipc_addinfo( list, id, inf, types );
|
||||
}
|
||||
|
||||
int
|
||||
addstat( benc_val_t * list, int id, int types )
|
||||
{
|
||||
tr_stat_t * st;
|
||||
tr_info_t * inf;
|
||||
|
||||
st = torrent_stat( id );
|
||||
if( NULL == st )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
inf = torrent_info( id );
|
||||
assert( NULL != inf );
|
||||
|
||||
return ipc_addstat( list, id, inf, st, types );
|
||||
}
|
||||
|
||||
void
|
||||
tormsg( enum ipc_msg id, benc_val_t * val, int64_t tag, void * arg )
|
||||
{
|
||||
struct client * client = arg;
|
||||
benc_val_t * idval;
|
||||
int ii, all;
|
||||
void * iter;
|
||||
void ( * func )( int );
|
||||
|
||||
all = 0;
|
||||
switch( id )
|
||||
{
|
||||
case IPC_MSG_REMOVEALL:
|
||||
all = 1;
|
||||
/* FALLTHROUGH */
|
||||
case IPC_MSG_REMOVE:
|
||||
func = torrent_remove;
|
||||
break;
|
||||
case IPC_MSG_STARTALL:
|
||||
all = 1;
|
||||
/* FALLTHROUGH */
|
||||
case IPC_MSG_START:
|
||||
func = torrent_start;
|
||||
break;
|
||||
case IPC_MSG_STOPALL:
|
||||
all = 1;
|
||||
/* FALLTHROUGH */
|
||||
case IPC_MSG_STOP:
|
||||
func = torrent_stop;
|
||||
break;
|
||||
default:
|
||||
assert( 0 );
|
||||
return;
|
||||
}
|
||||
|
||||
/* remove/start/stop all torrents */
|
||||
if( all )
|
||||
{
|
||||
iter = NULL;
|
||||
while( NULL != ( iter = torrent_iter( iter, &ii ) ) )
|
||||
{
|
||||
func( ii );
|
||||
}
|
||||
}
|
||||
/* remove/start/stop requested list of torrents */
|
||||
else
|
||||
{
|
||||
if( NULL == val || TYPE_LIST != val->type )
|
||||
{
|
||||
msgresp( client, tag, IPC_MSG_NOTSUP );
|
||||
return;
|
||||
}
|
||||
for( ii = 0; val->val.l.count > ii; ii++ )
|
||||
{
|
||||
idval = &val->val.l.vals[ii];
|
||||
if( TYPE_INT != idval->type || !TORRENT_ID_VALID( idval->val.i ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
func( idval->val.i );
|
||||
}
|
||||
}
|
||||
|
||||
msgresp( client, tag, IPC_MSG_OK );
|
||||
}
|
||||
|
||||
void
|
||||
lookmsg( enum ipc_msg id UNUSED, benc_val_t * val, int64_t tag, void * arg )
|
||||
{
|
||||
struct client * client = arg;
|
||||
uint8_t * buf;
|
||||
size_t buflen;
|
||||
int ii;
|
||||
benc_val_t * hash, pk, * pkinf;
|
||||
int64_t found;
|
||||
tr_info_t * inf;
|
||||
|
||||
if( NULL == val || TYPE_LIST != val->type )
|
||||
{
|
||||
msgresp( client, tag, IPC_MSG_NOTSUP );
|
||||
return;
|
||||
}
|
||||
|
||||
pkinf = ipc_initval( &client->ipc, IPC_MSG_INFO, tag, &pk, TYPE_LIST );
|
||||
if( NULL == pkinf )
|
||||
{
|
||||
errnomsg( "failed to build message" );
|
||||
byebye( client->ev, EVBUFFER_EOF, NULL );
|
||||
return;
|
||||
}
|
||||
|
||||
for( ii = 0; val->val.l.count > ii; ii++ )
|
||||
{
|
||||
hash = &val->val.l.vals[ii];
|
||||
if( NULL == hash || TYPE_STR != hash->type ||
|
||||
SHA_DIGEST_LENGTH * 2 != hash->val.s.i )
|
||||
{
|
||||
tr_bencFree( &pk );
|
||||
msgresp( client, tag, IPC_MSG_NOTSUP );
|
||||
return;
|
||||
}
|
||||
found = torrent_lookup( ( uint8_t * )hash->val.s.s );
|
||||
if( !TORRENT_ID_VALID( found ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
inf = torrent_info( found );
|
||||
assert( NULL != inf );
|
||||
if( 0 > ipc_addinfo( pkinf, found, inf, IPC_INF_HASH ) )
|
||||
{
|
||||
errnomsg( "failed to build message" );
|
||||
tr_bencFree( &pk );
|
||||
byebye( client->ev, EVBUFFER_EOF, NULL );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
buf = ipc_mkval( &pk, &buflen );
|
||||
tr_bencFree( &pk );
|
||||
queuemsg( client, buf, buflen );
|
||||
free( buf );
|
||||
}
|
||||
|
||||
void
|
||||
prefmsg( enum ipc_msg id, benc_val_t * val UNUSED, int64_t tag, void * arg )
|
||||
{
|
||||
struct client * client = arg;
|
||||
uint8_t * buf;
|
||||
size_t buflen;
|
||||
|
||||
switch( id )
|
||||
{
|
||||
case IPC_MSG_GETAUTOMAP:
|
||||
buf = ipc_mkint( &client->ipc, &buflen, IPC_MSG_AUTOMAP, tag,
|
||||
torrent_get_port_mapping() );
|
||||
break;
|
||||
case IPC_MSG_GETAUTOSTART:
|
||||
buf = ipc_mkint( &client->ipc, &buflen, IPC_MSG_AUTOSTART, tag,
|
||||
torrent_get_autostart() );
|
||||
break;
|
||||
case IPC_MSG_GETDIR:
|
||||
buf = ipc_mkstr( &client->ipc, &buflen, IPC_MSG_DIR, tag,
|
||||
torrent_get_directory() );
|
||||
break;
|
||||
case IPC_MSG_GETDOWNLIMIT:
|
||||
buf = ipc_mkint( &client->ipc, &buflen, IPC_MSG_DOWNLIMIT, tag,
|
||||
torrent_get_downlimit() );
|
||||
break;
|
||||
case IPC_MSG_GETPEX:
|
||||
buf = ipc_mkint( &client->ipc, &buflen, IPC_MSG_PEX, tag,
|
||||
torrent_get_pex() );
|
||||
break;
|
||||
case IPC_MSG_GETPORT:
|
||||
buf = ipc_mkint( &client->ipc, &buflen, IPC_MSG_PORT, tag,
|
||||
torrent_get_port() );
|
||||
break;
|
||||
case IPC_MSG_GETUPLIMIT:
|
||||
buf = ipc_mkint( &client->ipc, &buflen, IPC_MSG_UPLIMIT, tag,
|
||||
torrent_get_uplimit() );
|
||||
break;
|
||||
default:
|
||||
assert( 0 );
|
||||
return;
|
||||
}
|
||||
|
||||
queuemsg( client, buf, buflen );
|
||||
free( buf );
|
||||
}
|
||||
|
||||
void
|
||||
supmsg( enum ipc_msg id UNUSED, benc_val_t * val, int64_t tag, void * arg )
|
||||
{
|
||||
struct client * client = arg;
|
||||
uint8_t * buf;
|
||||
size_t buflen;
|
||||
int ii;
|
||||
benc_val_t pk, *pkval, * name;
|
||||
enum ipc_msg found;
|
||||
|
||||
if( NULL == val || TYPE_LIST != val->type )
|
||||
{
|
||||
msgresp( client, tag, IPC_MSG_NOTSUP );
|
||||
return;
|
||||
}
|
||||
|
||||
pkval = ipc_initval( &client->ipc, IPC_MSG_SUP, tag, &pk, TYPE_LIST );
|
||||
if( NULL == pkval )
|
||||
{
|
||||
errnomsg( "failed to build message" );
|
||||
byebye( client->ev, EVBUFFER_EOF, NULL );
|
||||
return;
|
||||
}
|
||||
/* XXX look at other initval to make sure we free pk */
|
||||
if( tr_bencListReserve( pkval, val->val.l.count ) )
|
||||
{
|
||||
errnomsg( "failed to build message" );
|
||||
tr_bencFree( &pk );
|
||||
byebye( client->ev, EVBUFFER_EOF, NULL );
|
||||
return;
|
||||
}
|
||||
|
||||
for( ii = 0; val->val.l.count > ii; ii++ )
|
||||
{
|
||||
name = &val->val.l.vals[ii];
|
||||
if( NULL == name || TYPE_STR != name->type )
|
||||
{
|
||||
tr_bencFree( &pk );
|
||||
msgresp( client, tag, IPC_MSG_NOTSUP );
|
||||
return;
|
||||
}
|
||||
found = ipc_msgid( &client->ipc, name->val.s.s );
|
||||
if( IPC__MSG_COUNT == found )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
tr_bencInitStr( tr_bencListAdd( pkval ),
|
||||
name->val.s.s, name->val.s.i, 1 );
|
||||
}
|
||||
|
||||
buf = ipc_mkval( &pk, &buflen );
|
||||
tr_bencFree( &pk );
|
||||
queuemsg( client, buf, buflen );
|
||||
free( buf );
|
||||
}
|
||||
33
daemon/server.h
Normal file
33
daemon/server.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/******************************************************************************
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2007 Joshua Elsasser
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TR_DAEMON_SERVER_H
|
||||
#define TR_DAEMON_SERVER_H
|
||||
|
||||
struct event_base;
|
||||
|
||||
int server_init( struct event_base * );
|
||||
int server_listen( int );
|
||||
|
||||
#endif /* TR_DAEMON_SERVER_H */
|
||||
968
daemon/torrents.c
Normal file
968
daemon/torrents.c
Normal file
@@ -0,0 +1,968 @@
|
||||
/******************************************************************************
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2007 Joshua Elsasser
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/uio.h>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <event.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "bsdtree.h"
|
||||
#include "bencode.h"
|
||||
#include "errors.h"
|
||||
#include "misc.h"
|
||||
#include "torrents.h"
|
||||
#include "transmission.h"
|
||||
#include "trcompat.h"
|
||||
|
||||
#define EXIT_TIMEOUT 10 /* how many seconds to wait on exit */
|
||||
#define TIMER_SECS 1 /* timer interval seconds */
|
||||
#define TIMER_USECS 0 /* timer interval microseconds */
|
||||
|
||||
struct tor
|
||||
{
|
||||
int id;
|
||||
int deleting;
|
||||
uint8_t hash[SHA_DIGEST_LENGTH];
|
||||
tr_torrent_t * tor;
|
||||
int pexset;
|
||||
int pex;
|
||||
RB_ENTRY( tor ) idlinks;
|
||||
RB_ENTRY( tor ) hashlinks;
|
||||
};
|
||||
|
||||
RB_HEAD( tortree, tor );
|
||||
RB_HEAD( hashtree, tor );
|
||||
|
||||
static struct tor * opentor ( const char *, const char *, uint8_t *, size_t,
|
||||
const char * );
|
||||
static void closetor ( struct tor *, int );
|
||||
static void starttimer ( int );
|
||||
static void timerfunc ( int, short, void * );
|
||||
static int loadstate ( void );
|
||||
static int savestate ( void );
|
||||
static int toridcmp ( struct tor *, struct tor * );
|
||||
static int torhashcmp ( struct tor *, struct tor * );
|
||||
static struct tor * idlookup ( int, int );
|
||||
static struct tor * hashlookup ( const uint8_t *, int );
|
||||
static struct tor * iterate ( struct tor * );
|
||||
|
||||
static struct event_base * gl_base = NULL;
|
||||
static tr_handle_t * gl_handle = NULL;
|
||||
static struct tortree gl_tree = RB_INITIALIZER( &gl_tree );
|
||||
static struct hashtree gl_hashes = RB_INITIALIZER( &gl_hashes );
|
||||
static int gl_lastid = 0;
|
||||
static struct event gl_event;
|
||||
static time_t gl_exiting = 0;
|
||||
static int gl_exitval = 0;
|
||||
static char gl_state[MAXPATHLEN];
|
||||
static char gl_newstate[MAXPATHLEN];
|
||||
|
||||
static int gl_autostart = 1;
|
||||
static int gl_pex = 1;
|
||||
static int gl_port = TR_DEFAULT_PORT;
|
||||
static int gl_mapping = 0;
|
||||
static int gl_uplimit = -1;
|
||||
static int gl_downlimit = -1;
|
||||
static char gl_dir[MAXPATHLEN];
|
||||
|
||||
RB_GENERATE_STATIC( tortree, tor, idlinks, toridcmp )
|
||||
RB_GENERATE_STATIC( hashtree, tor, hashlinks, torhashcmp )
|
||||
INTCMP_FUNC( toridcmp, tor, id )
|
||||
|
||||
void
|
||||
torrent_init( struct event_base * base )
|
||||
{
|
||||
assert( NULL == gl_handle && NULL == gl_base );
|
||||
|
||||
gl_base = base;
|
||||
gl_handle = tr_init( "daemon" );
|
||||
|
||||
confpath( gl_state, sizeof gl_state, CONF_FILE_STATE, 0 );
|
||||
strlcpy( gl_newstate, gl_state, sizeof gl_state );
|
||||
strlcat( gl_newstate, ".new", sizeof gl_state );
|
||||
absolutify( gl_dir, sizeof gl_dir, "." );
|
||||
|
||||
loadstate();
|
||||
}
|
||||
|
||||
int
|
||||
torrent_add_file( const char * path, const char * dir, int autostart )
|
||||
{
|
||||
struct tor * tor;
|
||||
|
||||
assert( NULL != gl_handle );
|
||||
assert( !gl_exiting );
|
||||
|
||||
tor = opentor( path, NULL, NULL, 0, dir );
|
||||
if( NULL == tor )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if( 0 > autostart )
|
||||
{
|
||||
autostart = gl_autostart;
|
||||
}
|
||||
if( autostart )
|
||||
{
|
||||
tr_torrentStart( tor->tor );
|
||||
}
|
||||
|
||||
savestate();
|
||||
|
||||
return tor->id;
|
||||
}
|
||||
|
||||
int
|
||||
torrent_add_data( uint8_t * data, size_t size, const char * dir, int autostart )
|
||||
{
|
||||
struct tor * tor;
|
||||
|
||||
assert( NULL != gl_handle );
|
||||
assert( !gl_exiting );
|
||||
|
||||
tor = opentor( NULL, NULL, data, size, dir );
|
||||
if( NULL == tor )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if( 0 > autostart )
|
||||
{
|
||||
autostart = gl_autostart;
|
||||
}
|
||||
if( autostart )
|
||||
{
|
||||
tr_torrentStart( tor->tor );
|
||||
}
|
||||
|
||||
savestate();
|
||||
|
||||
return tor->id;
|
||||
}
|
||||
|
||||
void
|
||||
torrent_start( int id )
|
||||
{
|
||||
struct tor * tor;
|
||||
tr_stat_t * st;
|
||||
|
||||
assert( NULL != gl_handle );
|
||||
assert( !gl_exiting );
|
||||
|
||||
tor = idlookup( id, 0 );
|
||||
if( NULL == tor )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
st = tr_torrentStat( tor->tor );
|
||||
if( TR_STATUS_INACTIVE & st->status )
|
||||
{
|
||||
tr_torrentStart( tor->tor );
|
||||
savestate();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
torrent_stop( int id )
|
||||
{
|
||||
struct tor * tor;
|
||||
tr_stat_t * st;
|
||||
|
||||
assert( NULL != gl_handle );
|
||||
assert( !gl_exiting );
|
||||
|
||||
tor = idlookup( id, 0 );
|
||||
if( NULL == tor )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
st = tr_torrentStat( tor->tor );
|
||||
if( TR_STATUS_ACTIVE & st->status )
|
||||
{
|
||||
tr_torrentStop( tor->tor );
|
||||
savestate();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
torrent_remove( int id )
|
||||
{
|
||||
struct tor * tor;
|
||||
|
||||
assert( NULL != gl_handle );
|
||||
assert( !gl_exiting );
|
||||
|
||||
tor = idlookup( id, 0 );
|
||||
if( NULL == tor )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
closetor( tor, 1 );
|
||||
savestate();
|
||||
}
|
||||
|
||||
tr_info_t *
|
||||
torrent_info( int id )
|
||||
{
|
||||
struct tor * tor;
|
||||
|
||||
assert( NULL != gl_handle );
|
||||
assert( !gl_exiting );
|
||||
|
||||
tor = idlookup( id, 0 );
|
||||
if( NULL == tor )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return tr_torrentInfo( tor->tor );
|
||||
}
|
||||
|
||||
tr_stat_t *
|
||||
torrent_stat( int id )
|
||||
{
|
||||
struct tor * tor;
|
||||
|
||||
assert( NULL != gl_handle );
|
||||
assert( !gl_exiting );
|
||||
|
||||
tor = idlookup( id, 0 );
|
||||
if( NULL == tor )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return tr_torrentStat( tor->tor );
|
||||
}
|
||||
|
||||
int
|
||||
torrent_lookup( const uint8_t * hashstr )
|
||||
{
|
||||
uint8_t hash[SHA_DIGEST_LENGTH];
|
||||
size_t ii;
|
||||
struct tor * tor;
|
||||
char buf[3];
|
||||
|
||||
assert( NULL != gl_handle );
|
||||
assert( !gl_exiting );
|
||||
|
||||
bzero( buf, sizeof buf );
|
||||
for( ii = 0; sizeof( hash ) > ii; ii++ )
|
||||
{
|
||||
if( !isxdigit( hashstr[2*ii] ) || !isxdigit( hashstr[1+2*ii] ) )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
memcpy( buf, &hashstr[2*ii], 2 );
|
||||
hash[ii] = strtol( buf, NULL, 16 );
|
||||
}
|
||||
|
||||
tor = hashlookup( hash, 0 );
|
||||
if( NULL == tor )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return tor->id;
|
||||
}
|
||||
|
||||
void *
|
||||
torrent_iter( void * iter, int * id )
|
||||
{
|
||||
struct tor * tor = iter;
|
||||
|
||||
assert( NULL != gl_handle );
|
||||
assert( !gl_exiting );
|
||||
|
||||
tor = iterate( tor );
|
||||
|
||||
if( NULL != tor )
|
||||
{
|
||||
*id = tor->id;
|
||||
}
|
||||
|
||||
return tor;
|
||||
}
|
||||
|
||||
void
|
||||
torrent_exit( int exitval )
|
||||
{
|
||||
struct tor * tor;
|
||||
|
||||
assert( NULL != gl_handle );
|
||||
assert( !gl_exiting );
|
||||
gl_exiting = time( NULL );
|
||||
gl_exitval = exitval;
|
||||
|
||||
RB_FOREACH( tor, tortree, &gl_tree )
|
||||
{
|
||||
closetor( tor, 0 );
|
||||
}
|
||||
|
||||
tr_natTraversalEnable( gl_handle, 0 );
|
||||
starttimer( 1 );
|
||||
}
|
||||
|
||||
void
|
||||
torrent_set_autostart( int autostart )
|
||||
{
|
||||
assert( NULL != gl_handle );
|
||||
assert( !gl_exiting );
|
||||
gl_autostart = autostart;
|
||||
savestate();
|
||||
}
|
||||
|
||||
int
|
||||
torrent_get_autostart( void )
|
||||
{
|
||||
return gl_autostart;
|
||||
}
|
||||
|
||||
void
|
||||
torrent_set_port( int port )
|
||||
{
|
||||
assert( NULL != gl_handle );
|
||||
assert( !gl_exiting );
|
||||
if( 0 < port && 0xffff > port )
|
||||
{
|
||||
gl_port = port;
|
||||
tr_setBindPort( gl_handle, port );
|
||||
savestate();
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
torrent_get_port( void )
|
||||
{
|
||||
return gl_port;
|
||||
}
|
||||
|
||||
void
|
||||
torrent_set_pex( int pex )
|
||||
{
|
||||
struct tor * tor;
|
||||
|
||||
assert( NULL != gl_handle );
|
||||
assert( !gl_exiting );
|
||||
|
||||
if( pex == gl_pex )
|
||||
{
|
||||
return;
|
||||
}
|
||||
gl_pex = pex;
|
||||
|
||||
for( tor = iterate( NULL ); NULL != tor; tor = iterate( tor ) )
|
||||
{
|
||||
if( tor->pexset )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
tr_torrentDisablePex( tor->tor, !gl_pex );
|
||||
}
|
||||
|
||||
savestate();
|
||||
}
|
||||
|
||||
int
|
||||
torrent_get_pex( void )
|
||||
{
|
||||
return gl_pex;
|
||||
}
|
||||
|
||||
void
|
||||
torrent_enable_port_mapping( int automap )
|
||||
{
|
||||
assert( NULL != gl_handle );
|
||||
assert( !gl_exiting );
|
||||
gl_mapping = ( automap ? 1 : 0 );
|
||||
tr_natTraversalEnable( gl_handle, gl_mapping );
|
||||
savestate();
|
||||
}
|
||||
|
||||
int
|
||||
torrent_get_port_mapping( void )
|
||||
{
|
||||
return gl_mapping;
|
||||
}
|
||||
|
||||
void
|
||||
torrent_set_uplimit( int uplimit )
|
||||
{
|
||||
assert( NULL != gl_handle );
|
||||
assert( !gl_exiting );
|
||||
gl_uplimit = uplimit;
|
||||
tr_setGlobalUploadLimit( gl_handle, uplimit );
|
||||
savestate();
|
||||
}
|
||||
|
||||
int
|
||||
torrent_get_uplimit( void )
|
||||
{
|
||||
return gl_uplimit;
|
||||
}
|
||||
|
||||
void
|
||||
torrent_set_downlimit( int downlimit )
|
||||
{
|
||||
assert( NULL != gl_handle );
|
||||
assert( !gl_exiting );
|
||||
gl_downlimit = downlimit;
|
||||
tr_setGlobalDownloadLimit( gl_handle, downlimit );
|
||||
savestate();
|
||||
}
|
||||
|
||||
int
|
||||
torrent_get_downlimit( void )
|
||||
{
|
||||
return gl_downlimit;
|
||||
}
|
||||
|
||||
void
|
||||
torrent_set_directory( const char * path )
|
||||
{
|
||||
assert( NULL != gl_handle );
|
||||
assert( !gl_exiting );
|
||||
|
||||
absolutify( gl_dir, sizeof gl_dir, path );
|
||||
savestate();
|
||||
}
|
||||
|
||||
const char *
|
||||
torrent_get_directory( void )
|
||||
{
|
||||
return gl_dir;
|
||||
}
|
||||
|
||||
struct tor *
|
||||
opentor( const char * path, const char * hash, uint8_t * data, size_t size,
|
||||
const char * dir )
|
||||
{
|
||||
struct tor * tor, * found;
|
||||
int errcode;
|
||||
tr_info_t * inf;
|
||||
|
||||
assert( ( NULL != path && NULL == hash && NULL == data ) ||
|
||||
( NULL == path && NULL != hash && NULL == data ) ||
|
||||
( NULL == path && NULL == hash && NULL != data ) );
|
||||
|
||||
/* XXX should probably wrap around back to 1 and avoid duplicates */
|
||||
if( INT_MAX == gl_lastid )
|
||||
{
|
||||
errmsg( "Congratulations, you're the %ith torrent! Your prize the "
|
||||
"inability to load any more torrents, enjoy!", INT_MAX );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tor = calloc( 1, sizeof *tor );
|
||||
if( NULL == tor )
|
||||
{
|
||||
mallocmsg( sizeof *tor );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if( NULL != path )
|
||||
{
|
||||
tor->tor = tr_torrentInit( gl_handle, path, tor->hash,
|
||||
TR_FLAG_SAVE, &errcode );
|
||||
}
|
||||
else if( NULL != hash )
|
||||
{
|
||||
tor->tor = tr_torrentInitSaved( gl_handle, hash, 0, &errcode );
|
||||
}
|
||||
else
|
||||
{
|
||||
tor->tor = tr_torrentInitData( gl_handle, data, size, tor->hash,
|
||||
TR_FLAG_SAVE, &errcode );
|
||||
}
|
||||
|
||||
if( NULL == tor->tor )
|
||||
{
|
||||
found = NULL;
|
||||
switch( errcode )
|
||||
{
|
||||
case TR_EINVALID:
|
||||
if( NULL == path )
|
||||
{
|
||||
errmsg( "invalid torrent file" );
|
||||
}
|
||||
else
|
||||
{
|
||||
errmsg( "invalid torrent file: %s", path );
|
||||
}
|
||||
break;
|
||||
case TR_EUNSUPPORTED:
|
||||
if( NULL == path )
|
||||
{
|
||||
errmsg( "unsupported torrent file" );
|
||||
}
|
||||
else
|
||||
{
|
||||
errmsg( "unsupported torrent file: %s", path );
|
||||
}
|
||||
break;
|
||||
case TR_EDUPLICATE:
|
||||
/* XXX not yet
|
||||
found = hashlookup( tor->hash, 1 );
|
||||
assert( NULL != found );
|
||||
found->deleting = 0;
|
||||
*/
|
||||
errmsg( "XXX loaded duplicate torrent" );
|
||||
break;
|
||||
default:
|
||||
if( NULL == path )
|
||||
{
|
||||
errmsg( "torrent file failed to load" );
|
||||
}
|
||||
else
|
||||
{
|
||||
errmsg( "torrent file failed to load: %s", path );
|
||||
}
|
||||
break;
|
||||
}
|
||||
free( tor );
|
||||
return found;
|
||||
}
|
||||
gl_lastid++;
|
||||
tor->id = gl_lastid;
|
||||
tor->deleting = 0;
|
||||
|
||||
assert( sizeof( inf->hash ) == sizeof( tor->hash ) );
|
||||
inf = tr_torrentInfo( tor->tor );
|
||||
memcpy( tor->hash, inf->hash, sizeof tor->hash );
|
||||
|
||||
tr_torrentSetFolder( tor->tor, ( NULL == dir ? gl_dir : dir ) );
|
||||
|
||||
if( TR_FLAG_PRIVATE & inf->flags )
|
||||
{
|
||||
tor->pexset = 1;
|
||||
tor->pex = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
tr_torrentDisablePex( tor->tor, !gl_pex );
|
||||
}
|
||||
|
||||
found = RB_INSERT( tortree, &gl_tree, tor );
|
||||
assert( NULL == found );
|
||||
found = RB_INSERT( hashtree, &gl_hashes, tor );
|
||||
assert( NULL == found );
|
||||
|
||||
return tor;
|
||||
}
|
||||
|
||||
void
|
||||
closetor( struct tor * tor, int calltimer )
|
||||
{
|
||||
tr_stat_t * st;
|
||||
|
||||
if( NULL == tor || tor->deleting )
|
||||
{
|
||||
return;
|
||||
}
|
||||
tor->deleting = 1;
|
||||
|
||||
st = tr_torrentStat( tor->tor );
|
||||
if( TR_STATUS_ACTIVE & st->status )
|
||||
{
|
||||
tr_torrentStop( tor->tor );
|
||||
}
|
||||
|
||||
starttimer( calltimer );
|
||||
}
|
||||
|
||||
void
|
||||
starttimer( int callnow )
|
||||
{
|
||||
if( !evtimer_initialized( &gl_event ) )
|
||||
{
|
||||
evtimer_set( &gl_event, timerfunc, NULL );
|
||||
/* XXX event_base_set( gl_base, &gl_event ); */
|
||||
}
|
||||
|
||||
if( callnow )
|
||||
{
|
||||
timerfunc( -1, EV_TIMEOUT, NULL );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
timerfunc( int fd UNUSED, short event UNUSED, void * arg UNUSED )
|
||||
{
|
||||
struct tor * tor, * next;
|
||||
tr_handle_status_t * hs;
|
||||
tr_stat_t * st;
|
||||
int stillmore;
|
||||
struct timeval tv;
|
||||
|
||||
stillmore = 0;
|
||||
for( tor = RB_MIN( tortree, &gl_tree ); NULL != tor; tor = next )
|
||||
{
|
||||
next = RB_NEXT( tortree, &gl_tree, tor );
|
||||
if( !tor->deleting )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
st = tr_torrentStat( tor->tor );
|
||||
if( TR_STATUS_PAUSE & st->status )
|
||||
{
|
||||
tr_torrentClose( gl_handle, tor->tor );
|
||||
RB_REMOVE( tortree, &gl_tree, tor );
|
||||
RB_REMOVE( hashtree, &gl_hashes, tor );
|
||||
free( tor );
|
||||
}
|
||||
else
|
||||
{
|
||||
stillmore = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if( gl_exiting )
|
||||
{
|
||||
if( !stillmore )
|
||||
{
|
||||
hs = tr_handleStatus( gl_handle );
|
||||
if( TR_NAT_TRAVERSAL_DISABLED != hs->natTraversalStatus )
|
||||
{
|
||||
stillmore = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if( !stillmore || EXIT_TIMEOUT <= time( NULL ) - gl_exiting )
|
||||
{
|
||||
if( stillmore )
|
||||
{
|
||||
errmsg( "timing out trackers and/or port mapping on exit" );
|
||||
}
|
||||
for( tor = RB_MIN( tortree, &gl_tree ); NULL != tor; tor = next )
|
||||
{
|
||||
next = RB_NEXT( tortree, &gl_tree, tor );
|
||||
tr_torrentClose( gl_handle, tor->tor );
|
||||
RB_REMOVE( tortree, &gl_tree, tor );
|
||||
RB_REMOVE( hashtree, &gl_hashes, tor );
|
||||
free( tor );
|
||||
}
|
||||
tr_close( gl_handle );
|
||||
exit( gl_exitval );
|
||||
}
|
||||
}
|
||||
|
||||
if( stillmore )
|
||||
{
|
||||
bzero( &tv, sizeof tv );
|
||||
tv.tv_sec = TIMER_SECS;
|
||||
tv.tv_usec = TIMER_USECS;
|
||||
evtimer_add( &gl_event, &tv );
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
loadstate( void )
|
||||
{
|
||||
uint8_t * buf;
|
||||
size_t len;
|
||||
benc_val_t top, * num, * str, * list, * dict;
|
||||
int ii;
|
||||
struct tor * tor;
|
||||
const char * dir;
|
||||
|
||||
buf = readfile( gl_state, &len );
|
||||
if( NULL == buf )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if( tr_bencLoad( buf, len, &top, NULL ) )
|
||||
{
|
||||
free( buf );
|
||||
errmsg( "failed to load bencoded data from %s", gl_state );
|
||||
return -1;
|
||||
}
|
||||
free( buf );
|
||||
|
||||
num = tr_bencDictFind( &top, "autostart" );
|
||||
if( NULL != num && TYPE_INT == num->type )
|
||||
{
|
||||
gl_autostart = ( num->val.i ? 1 : 0 );
|
||||
}
|
||||
|
||||
num = tr_bencDictFind( &top, "port" );
|
||||
if( NULL != num && TYPE_INT == num->type &&
|
||||
0 < num->val.i && 0xffff > num->val.i )
|
||||
{
|
||||
gl_port = num->val.i;
|
||||
}
|
||||
tr_setBindPort( gl_handle, gl_port );
|
||||
|
||||
num = tr_bencDictFind( &top, "default-pex" );
|
||||
if( NULL != num && TYPE_INT == num->type )
|
||||
{
|
||||
gl_pex = ( num->val.i ? 1 : 0 );
|
||||
}
|
||||
|
||||
num = tr_bencDictFind( &top, "port-mapping" );
|
||||
if( NULL != num && TYPE_INT == num->type )
|
||||
{
|
||||
gl_mapping = ( num->val.i ? 1 : 0 );
|
||||
}
|
||||
tr_natTraversalEnable( gl_handle, gl_mapping );
|
||||
|
||||
num = tr_bencDictFind( &top, "upload-limit" );
|
||||
if( NULL != num && TYPE_INT == num->type )
|
||||
{
|
||||
gl_uplimit = num->val.i;
|
||||
}
|
||||
tr_setGlobalUploadLimit( gl_handle, gl_uplimit );
|
||||
|
||||
num = tr_bencDictFind( &top, "download-limit" );
|
||||
if( NULL != num && TYPE_INT == num->type )
|
||||
{
|
||||
gl_downlimit = num->val.i;
|
||||
}
|
||||
tr_setGlobalDownloadLimit( gl_handle, gl_downlimit );
|
||||
|
||||
str = tr_bencDictFind( &top, "default-directory" );
|
||||
if( NULL != str && TYPE_STR == str->type )
|
||||
{
|
||||
strlcpy( gl_dir, str->val.s.s, sizeof gl_dir );
|
||||
}
|
||||
|
||||
list = tr_bencDictFind( &top, "torrents" );
|
||||
if( NULL == list || TYPE_LIST != list->type )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
for( ii = 0; ii < list->val.l.count; ii++ )
|
||||
{
|
||||
dict = &list->val.l.vals[ii];
|
||||
if( TYPE_DICT != dict->type )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
str = tr_bencDictFind( dict, "directory" );
|
||||
dir = ( NULL != str && TYPE_STR == str->type ? str->val.s.s : NULL );
|
||||
|
||||
str = tr_bencDictFind( dict, "hash" );
|
||||
if( NULL == str || TYPE_STR != str->type ||
|
||||
2 * SHA_DIGEST_LENGTH != str->val.s.i )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
tor = opentor( NULL, str->val.s.s, NULL, 0, dir );
|
||||
if( NULL == tor )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
num = tr_bencDictFind( dict, "pex" );
|
||||
if( NULL != num && TYPE_INT == num->type )
|
||||
{
|
||||
tor->pexset = 1;
|
||||
tor->pex = ( num->val.i ? 1 : 0 );
|
||||
}
|
||||
tr_torrentDisablePex( tor->tor, !( tor->pexset ? tor->pex : gl_pex ) );
|
||||
|
||||
num = tr_bencDictFind( dict, "paused" );
|
||||
if( NULL != num && TYPE_INT == num->type && !num->val.i )
|
||||
{
|
||||
tr_torrentStart( tor->tor );
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
savestate( void )
|
||||
{
|
||||
benc_val_t top, * list, * tor;
|
||||
struct tor * ii;
|
||||
tr_info_t * inf;
|
||||
tr_stat_t * st;
|
||||
uint8_t * buf;
|
||||
int len, pexset;
|
||||
|
||||
tr_bencInit( &top, TYPE_DICT );
|
||||
if( tr_bencDictReserve( &top, 8 ) )
|
||||
{
|
||||
nomem:
|
||||
tr_bencFree( &top );
|
||||
errmsg( "failed to save state: failed to allocate memory" );
|
||||
return -1;
|
||||
}
|
||||
tr_bencInitInt( tr_bencDictAdd( &top, "autostart" ), gl_autostart );
|
||||
tr_bencInitInt( tr_bencDictAdd( &top, "port" ), gl_port );
|
||||
tr_bencInitInt( tr_bencDictAdd( &top, "default-pex" ), gl_pex );
|
||||
tr_bencInitInt( tr_bencDictAdd( &top, "port-mapping" ), gl_mapping );
|
||||
tr_bencInitInt( tr_bencDictAdd( &top, "upload-limit" ), gl_uplimit );
|
||||
tr_bencInitInt( tr_bencDictAdd( &top, "download-limit" ), gl_downlimit );
|
||||
tr_bencInitStr( tr_bencDictAdd( &top, "default-directory" ),
|
||||
gl_dir, -1, 1 );
|
||||
list = tr_bencDictAdd( &top, "torrents" );
|
||||
tr_bencInit( list, TYPE_LIST );
|
||||
|
||||
len = 0;
|
||||
RB_FOREACH( ii, tortree, &gl_tree )
|
||||
{
|
||||
if( !ii->deleting )
|
||||
{
|
||||
len++;
|
||||
}
|
||||
}
|
||||
if( tr_bencListReserve( list, len ) )
|
||||
{
|
||||
goto nomem;
|
||||
}
|
||||
|
||||
RB_FOREACH( ii, tortree, &gl_tree )
|
||||
{
|
||||
if( ii->deleting )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
tor = tr_bencListAdd( list );
|
||||
assert( NULL != tor );
|
||||
tr_bencInit( tor, TYPE_DICT );
|
||||
inf = tr_torrentInfo( ii->tor );
|
||||
st = tr_torrentStat( ii->tor );
|
||||
pexset = ( ii->pexset && !( TR_FLAG_PRIVATE & inf->flags ) );
|
||||
if( tr_bencDictReserve( tor, ( pexset ? 4 : 3 ) ) )
|
||||
{
|
||||
goto nomem;
|
||||
}
|
||||
tr_bencInitStr( tr_bencDictAdd( tor, "hash" ),
|
||||
inf->hashString, 2 * SHA_DIGEST_LENGTH, 1 );
|
||||
tr_bencInitInt( tr_bencDictAdd( tor, "paused" ),
|
||||
( TR_STATUS_INACTIVE & st->status ? 1 : 0 ) );
|
||||
tr_bencInitStr( tr_bencDictAdd( tor, "directory" ),
|
||||
tr_torrentGetFolder( ii->tor ), -1, 1 );
|
||||
if( pexset )
|
||||
{
|
||||
tr_bencInitInt( tr_bencDictAdd( tor, "pex" ), ii->pex );
|
||||
}
|
||||
}
|
||||
|
||||
buf = ( uint8_t * )tr_bencSaveMalloc( &top, &len );
|
||||
SAFEBENCFREE( &top );
|
||||
if( NULL == buf )
|
||||
{
|
||||
errnomsg( "failed to save state: bencoding failed" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
if( 0 > writefile( gl_newstate, buf, len ) )
|
||||
{
|
||||
free( buf );
|
||||
return -1;
|
||||
}
|
||||
free( buf );
|
||||
|
||||
if( 0 > rename( gl_newstate, gl_state ) )
|
||||
{
|
||||
errnomsg( "failed to save state: failed to rename %s to %s",
|
||||
gl_newstate, CONF_FILE_STATE );
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
torhashcmp( struct tor * left, struct tor * right )
|
||||
{
|
||||
return memcmp( left->hash, right->hash, sizeof left->hash );
|
||||
}
|
||||
|
||||
struct tor *
|
||||
idlookup( int id, int wantdel )
|
||||
{
|
||||
struct tor key, * found;
|
||||
|
||||
bzero( &key, sizeof key );
|
||||
key.id = id;
|
||||
found = RB_FIND( tortree, &gl_tree, &key );
|
||||
if( NULL != found && !wantdel && found->deleting )
|
||||
{
|
||||
found = NULL;
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
struct tor *
|
||||
hashlookup( const uint8_t * hash, int wantdel )
|
||||
{
|
||||
struct tor key, * found;
|
||||
|
||||
bzero( &key, sizeof key );
|
||||
memcpy( key.hash, hash, sizeof key.hash );
|
||||
found = RB_FIND( hashtree, &gl_hashes, &key );
|
||||
if( NULL != found && !wantdel && found->deleting )
|
||||
{
|
||||
found = NULL;
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
struct tor *
|
||||
iterate( struct tor * tor )
|
||||
{
|
||||
assert( NULL != gl_handle );
|
||||
assert( !gl_exiting );
|
||||
|
||||
if( NULL == tor )
|
||||
{
|
||||
tor = RB_MIN( tortree, &gl_tree );
|
||||
}
|
||||
else
|
||||
{
|
||||
tor = RB_NEXT( tortree, &gl_tree, tor );
|
||||
}
|
||||
|
||||
while( NULL != tor && tor->deleting )
|
||||
{
|
||||
tor = RB_NEXT( tortree, &gl_tree, tor );
|
||||
}
|
||||
|
||||
return tor;
|
||||
}
|
||||
62
daemon/torrents.h
Normal file
62
daemon/torrents.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/******************************************************************************
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2007 Joshua Elsasser
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef TR_DAEMON_TORRENTS_H
|
||||
#define TR_DAEMON_TORRENTS_H
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
/* typedefs suck */
|
||||
#include "transmission.h"
|
||||
|
||||
struct event_base;
|
||||
|
||||
void torrent_init ( struct event_base * );
|
||||
int torrent_add_file ( const char *, const char *, int );
|
||||
int torrent_add_data ( uint8_t *, size_t, const char *, int );
|
||||
void torrent_start ( int );
|
||||
void torrent_stop ( int );
|
||||
void torrent_remove ( int );
|
||||
tr_info_t * torrent_info ( int );
|
||||
tr_stat_t * torrent_stat ( int );
|
||||
int torrent_lookup ( const uint8_t * );
|
||||
void * torrent_iter ( void *, int * );
|
||||
|
||||
void torrent_exit ( int );
|
||||
void torrent_set_autostart ( int );
|
||||
int torrent_get_autostart ( void );
|
||||
void torrent_set_port ( int );
|
||||
int torrent_get_port ( void );
|
||||
void torrent_set_pex ( int );
|
||||
int torrent_get_pex ( void );
|
||||
void torrent_enable_port_mapping ( int );
|
||||
int torrent_get_port_mapping ( void );
|
||||
void torrent_set_uplimit ( int );
|
||||
int torrent_get_uplimit ( void );
|
||||
void torrent_set_downlimit ( int );
|
||||
int torrent_get_downlimit ( void );
|
||||
void torrent_set_directory ( const char * );
|
||||
const char * torrent_get_directory ( void );
|
||||
|
||||
#endif /* TR_DAEMON_TORRENTS_H */
|
||||
64
daemon/transmission-daemon.1
Normal file
64
daemon/transmission-daemon.1
Normal file
@@ -0,0 +1,64 @@
|
||||
.\" $Id$
|
||||
.\"
|
||||
.\" Copyright (c) 2007 Joshua Elsasser
|
||||
.\"
|
||||
.\" Permission is hereby granted, free of charge, to any person obtaining a
|
||||
.\" copy of this software and associated documentation files (the "Software"),
|
||||
.\" to deal in the Software without restriction, including without limitation
|
||||
.\" the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
.\" and/or sell copies of the Software, and to permit persons to whom the
|
||||
.\" Software is furnished to do so, subject to the following conditions:
|
||||
.\"
|
||||
.\" The above copyright notice and this permission notice shall be included in
|
||||
.\" all copies or substantial portions of the Software.
|
||||
.\"
|
||||
.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
.\" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
.\" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
.\" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
.\" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
.\" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
.\" DEALINGS IN THE SOFTWARE.
|
||||
|
||||
.Dd March 30, 2007
|
||||
.Dt TRANSMISSION-DAEMON 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm transmission-daemon
|
||||
.Nd a bittorrent client
|
||||
.Sh SYNOPSIS
|
||||
.Nm transmission-daemon
|
||||
.Fl h
|
||||
.Nm
|
||||
.Op Fl f
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
program cures baldness, obesity and gout.
|
||||
.Pp
|
||||
The options are as follows:
|
||||
.Bl -tag -width Ds
|
||||
.It Fl f Fl -foreground
|
||||
Run in the foreground and print errors to stderr instead of forking
|
||||
and logging errors with syslog.
|
||||
.It Fl h Fl -help
|
||||
Print command-line option descriptions.
|
||||
.El
|
||||
.Sh FILES
|
||||
.Pa ~/.transmission
|
||||
.Sh AUTHORS
|
||||
.An -nosplit
|
||||
The
|
||||
.Nm
|
||||
program was written by
|
||||
.An Josh Elsasser Aq josh@elsasser.org ,
|
||||
.An Eric Petit Aq titer@m0k.org ,
|
||||
and
|
||||
.An Mitchell Livingston Aq livings124@gmail.com .
|
||||
.Sh SEE ALSO
|
||||
.Xr transmissioncli 1 ,
|
||||
.Xr transmission-gtk 1 ,
|
||||
.Xr transmission-proxy 1 ,
|
||||
.Xr transmission-remote 1
|
||||
.Pp
|
||||
http://transmission.m0k.org
|
||||
65
daemon/transmission-proxy.1
Normal file
65
daemon/transmission-proxy.1
Normal file
@@ -0,0 +1,65 @@
|
||||
.\" $Id$
|
||||
.\"
|
||||
.\" Copyright (c) 2007 Joshua Elsasser
|
||||
.\"
|
||||
.\" Permission is hereby granted, free of charge, to any person obtaining a
|
||||
.\" copy of this software and associated documentation files (the "Software"),
|
||||
.\" to deal in the Software without restriction, including without limitation
|
||||
.\" the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
.\" and/or sell copies of the Software, and to permit persons to whom the
|
||||
.\" Software is furnished to do so, subject to the following conditions:
|
||||
.\"
|
||||
.\" The above copyright notice and this permission notice shall be included in
|
||||
.\" all copies or substantial portions of the Software.
|
||||
.\"
|
||||
.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
.\" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
.\" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
.\" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
.\" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
.\" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
.\" DEALINGS IN THE SOFTWARE.
|
||||
|
||||
.Dd March 30, 2007
|
||||
.Dt TRANSMISSION-PROXY 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm transmission-proxy
|
||||
.Nd a bittorrent client
|
||||
.Sh SYNOPSIS
|
||||
.Nm transmission-proxy
|
||||
.Fl h
|
||||
.Nm
|
||||
.Op Fl t
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
program comprises 37% of the diet of the average swede.
|
||||
.Pp
|
||||
The options are as follows:
|
||||
.Bl -tag -width Ds
|
||||
.It Fl t Fl -type Ar daemon | gtk
|
||||
Connect to either
|
||||
.Xr transmission-daemon 1
|
||||
or
|
||||
.Xr transmission-gtk 1 .
|
||||
.It Fl h Fl -help
|
||||
Print command-line option descriptions.
|
||||
.El
|
||||
.Sh FILES
|
||||
.Pa ~/.transmission
|
||||
.Sh AUTHORS
|
||||
.An -nosplit
|
||||
The
|
||||
.Nm
|
||||
program was written by
|
||||
.An Josh Elsasser Aq josh@elsasser.org ,
|
||||
.An Eric Petit Aq titer@m0k.org ,
|
||||
and
|
||||
.An Mitchell Livingston Aq livings124@gmail.com .
|
||||
.Sh SEE ALSO
|
||||
.Xr transmissioncli 1 ,
|
||||
.Xr transmission-gtk 1 ,
|
||||
.Xr transmission-remote 1
|
||||
.Pp
|
||||
http://transmission.m0k.org
|
||||
206
daemon/transmission-remote.1
Normal file
206
daemon/transmission-remote.1
Normal file
@@ -0,0 +1,206 @@
|
||||
.\" $Id$
|
||||
.\"
|
||||
.\" Copyright (c) 2007 Joshua Elsasser
|
||||
.\"
|
||||
.\" Permission is hereby granted, free of charge, to any person obtaining a
|
||||
.\" copy of this software and associated documentation files (the "Software"),
|
||||
.\" to deal in the Software without restriction, including without limitation
|
||||
.\" the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
.\" and/or sell copies of the Software, and to permit persons to whom the
|
||||
.\" Software is furnished to do so, subject to the following conditions:
|
||||
.\"
|
||||
.\" The above copyright notice and this permission notice shall be included in
|
||||
.\" all copies or substantial portions of the Software.
|
||||
.\"
|
||||
.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
.\" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
.\" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
.\" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
.\" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
.\" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
.\" DEALINGS IN THE SOFTWARE.
|
||||
|
||||
.Dd March 30, 2007
|
||||
.Dt TRANSMISSION-REMOTE 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm transmission-remote
|
||||
.Nd a remote control utility for
|
||||
.Xr transmission-daemon 1
|
||||
and
|
||||
.Xr transmission-gtk 1
|
||||
.Sh SYNOPSIS
|
||||
.Bk -words
|
||||
.Nm transmission-remote
|
||||
.Fl h
|
||||
.Nm
|
||||
.Oo
|
||||
.Fl t Ar daemon | Ar gtk
|
||||
.Oc
|
||||
.Op Fl a Ar torrent-file
|
||||
.Op Fl d Ar download-rate
|
||||
.Op Fl D
|
||||
.Op Fl e
|
||||
.Op Fl E
|
||||
.Op Fl f Ar directory
|
||||
.Op Fl i
|
||||
.Op Fl l
|
||||
.Op Fl m
|
||||
.Op Fl M
|
||||
.Op Fl p Ar port
|
||||
.Op Fl q
|
||||
.Oo
|
||||
.Fl r Ar all | Ar info-hash
|
||||
.Oc
|
||||
.Oo
|
||||
.Fl s Ar all | Ar info-hash
|
||||
.Oc
|
||||
.Oo
|
||||
.Fl S Ar all | Ar info-hash
|
||||
.Oc
|
||||
.Op Fl u Ar upload-rate
|
||||
.Op Fl U
|
||||
.Op Ar torrent-file ...
|
||||
.Nm
|
||||
.Fl x
|
||||
.Op Fl a Ar torrent-file
|
||||
.Op Fl d Ar download-rate
|
||||
.Op Fl D
|
||||
.Op Fl e
|
||||
.Op Fl E
|
||||
.Op Fl f Ar directory
|
||||
.Op Fl i
|
||||
.Op Fl l
|
||||
.Op Fl m
|
||||
.Op Fl M
|
||||
.Op Fl p Ar port
|
||||
.Op Fl q
|
||||
.Oo
|
||||
.Fl r Ar all | Ar info-hash
|
||||
.Oc
|
||||
.Oo
|
||||
.Fl s Ar all | Ar info-hash
|
||||
.Oc
|
||||
.Oo
|
||||
.Fl S Ar all | Ar info-hash
|
||||
.Oc
|
||||
.Op Fl u Ar upload-rate
|
||||
.Op Fl U
|
||||
.Ar command
|
||||
.Op Ar argument ...
|
||||
.Ek
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
program is a vile scoundral lacking the most fundamental principles of
|
||||
morality and decency.
|
||||
.Pp
|
||||
The options are as follows:
|
||||
.Bl -tag -width Ds
|
||||
.It Fl a Fl -add Ar torrent-file
|
||||
Add the torrent metainfo file
|
||||
.Ar torrent-file .
|
||||
.It Fl d Fl -download-limit Ar download-rate
|
||||
Set maximum download rate to
|
||||
.Ar download-rate
|
||||
in kilobytes per second.
|
||||
.It Fl D Fl -download-unlimited
|
||||
Remove the download limit.
|
||||
.It Fl e
|
||||
Enable peer exchange.
|
||||
.It Fl E
|
||||
Disable peer exchange.
|
||||
.It Fl f Fl -folder Ar directory
|
||||
Use
|
||||
.Ar directory
|
||||
as the default location for newly added torrents to download files to.
|
||||
.It Fl h Fl -help
|
||||
Print command-line option descriptions.
|
||||
.It Fl i Fl -info
|
||||
Print a listing of all torrent info hashes and names.
|
||||
.It Fl l Fl -list
|
||||
Print a listing of all torrent names with size and current status.
|
||||
.It Fl m Fl -port-mapping
|
||||
Enable automatic port mapping via NAT-PMP or UPnP IGD.
|
||||
.It Fl M Fl -no-port-mapping
|
||||
Disable automatic port mapping.
|
||||
.It Fl p Fl -port Ar port
|
||||
Attempt to bind to
|
||||
.Ar port
|
||||
for use as a listening port to accept incoming peer connections.
|
||||
.It Fl q Fl -quit
|
||||
Quit the running instance.
|
||||
.It Fl r Fl -remove Ar all | info-hash
|
||||
Remove all torrents, or the torrent with an info hash of
|
||||
.Ar info-hash .
|
||||
Neither the downloaded data nor the original torrent metainfo file
|
||||
will be deleted.
|
||||
.It Fl s Fl -start Ar all | info-hash
|
||||
Stop all torrents from downloading or seeding, or the torrent with an
|
||||
info hash of
|
||||
.Ar info-hash .
|
||||
.It Fl S Fl -stop Ar all | info-hash
|
||||
Start all torrents downloading or seeding, or the torrent with an info
|
||||
hash of
|
||||
.Ar info-hash .
|
||||
.It Fl t Fl -type Ar daemon | gtk
|
||||
Connect to either
|
||||
.Xr transmission-daemon 1
|
||||
or
|
||||
.Xr transmission-gtk 1 .
|
||||
.It Fl u Fl -upload-limit Ar upload-rate
|
||||
Set maximum upload rate to
|
||||
.Ar upload-rate
|
||||
in kilobytes per second.
|
||||
.It Fl U Fl -upload-unlimited
|
||||
Remove the upload limit.
|
||||
.It Fl x Fl -proxy
|
||||
Proxy the connection to
|
||||
.Xr transmission-daemon 1
|
||||
or
|
||||
.Xr transmission-gtk 1
|
||||
through
|
||||
.Ar command .
|
||||
.El
|
||||
.Sh FILES
|
||||
.Pa ~/.transmission
|
||||
.Sh EXAMPLES
|
||||
Show status for all torrents:
|
||||
.Pp
|
||||
.Dl transmission-remote -l
|
||||
.Pp
|
||||
Pause a torrent:
|
||||
.Pp
|
||||
.Dl transmission-remote -S ca6ac4bbd971d3902935dbcfc2d3ea25b428a547
|
||||
.Pp
|
||||
Set download and upload limits to 100 KiB/sec and 20 KiB/sec:
|
||||
.Pp
|
||||
.Dl transmission-remote -d 100 -u 20
|
||||
.Pp
|
||||
Quit the GTK+ frontend:
|
||||
.Pp
|
||||
.Dl transmission-remote -t gtk -q
|
||||
.Pp
|
||||
Add two torrents:
|
||||
.Pp
|
||||
.Dl transmission-remote foo.torrent bar.torrent
|
||||
.Pp
|
||||
Show the info hashes of all the torrents on jade:
|
||||
.Pp
|
||||
.Dl transmission-remote -x -i ssh jade transmission-proxy
|
||||
.Sh AUTHORS
|
||||
.An -nosplit
|
||||
The
|
||||
.Nm
|
||||
program was written by
|
||||
.An Josh Elsasser Aq josh@elsasser.org ,
|
||||
.An Eric Petit Aq titer@m0k.org ,
|
||||
and
|
||||
.An Mitchell Livingston Aq livings124@gmail.com .
|
||||
.Sh SEE ALSO
|
||||
.Xr transmissioncli 1 ,
|
||||
.Xr transmission-daemon 1 ,
|
||||
.Xr transmission-gtk 1 ,
|
||||
.Xr transmission-proxy 1
|
||||
.Pp
|
||||
http://transmission.m0k.org
|
||||
@@ -80,6 +80,9 @@ program was written by
|
||||
and
|
||||
.An Mitchell Livingston Aq livings124@gmail.com .
|
||||
.Sh SEE ALSO
|
||||
.Xr transmissioncli 1
|
||||
.Xr transmissioncli 1 ,
|
||||
.Xr transmission-daemon 1 ,
|
||||
.Xr transmission-proxy 1 ,
|
||||
.Xr transmission-remote 1
|
||||
.Pp
|
||||
http://transmission.m0k.org
|
||||
|
||||
@@ -131,6 +131,7 @@ typedef struct tr_bitfield_s tr_bitfield_t;
|
||||
|
||||
typedef enum { TR_NET_OK, TR_NET_ERROR, TR_NET_WAIT } tr_tristate_t;
|
||||
|
||||
#include "trcompat.h"
|
||||
#include "bsdqueue.h"
|
||||
#include "bsdtree.h"
|
||||
#include "platform.h"
|
||||
|
||||
@@ -110,7 +110,8 @@ char * tr_getPrefsDirectory()
|
||||
strcat( prefsDirectory, "/Transmission" );
|
||||
#elif defined( SYS_DARWIN )
|
||||
snprintf( prefsDirectory, MAX_PATH_LENGTH,
|
||||
"%s/Library/Caches/Transmission", tr_getHomeDirectory() );
|
||||
"%s/Library/Application Support/Transmission",
|
||||
tr_getHomeDirectory() );
|
||||
#elif defined(__AMIGAOS4__)
|
||||
snprintf( prefsDirectory, MAX_PATH_LENGTH, "PROGDIR:.transmission" );
|
||||
#else
|
||||
@@ -147,8 +148,8 @@ char * tr_getCacheDirectory()
|
||||
snprintf( cacheDirectory, MAX_PATH_LENGTH, "%s/Cache",
|
||||
tr_getPrefsDirectory() );
|
||||
#elif defined( SYS_DARWIN )
|
||||
snprintf( cacheDirectory, MAX_PATH_LENGTH, "%s",
|
||||
tr_getPrefsDirectory() );
|
||||
snprintf( cacheDirectory, MAX_PATH_LENGTH, "%s/Caches/Transmission",
|
||||
tr_getHomeDirectory() );
|
||||
#else
|
||||
snprintf( cacheDirectory, MAX_PATH_LENGTH, "%s/cache",
|
||||
tr_getPrefsDirectory() );
|
||||
@@ -180,8 +181,7 @@ char * tr_getTorrentsDirectory()
|
||||
snprintf( torrentsDirectory, MAX_PATH_LENGTH, "%s/Torrents",
|
||||
tr_getPrefsDirectory() );
|
||||
#elif defined( SYS_DARWIN )
|
||||
snprintf( torrentsDirectory, MAX_PATH_LENGTH,
|
||||
"%s/Library/Application Support/Transmission/Torrents",
|
||||
snprintf( torrentsDirectory, MAX_PATH_LENGTH, "%s/Torrents",
|
||||
tr_getHomeDirectory() );
|
||||
#else
|
||||
snprintf( torrentsDirectory, MAX_PATH_LENGTH, "%s/torrents",
|
||||
|
||||
60
libtransmission/strlcat.c
Normal file
60
libtransmission/strlcat.c
Normal file
@@ -0,0 +1,60 @@
|
||||
/* $Id$ */
|
||||
/* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef HAVE_STRLCAT
|
||||
|
||||
/*
|
||||
* Appends src to string dst of size siz (unlike strncat, siz is the
|
||||
* full size of dst, not space left). At most siz-1 characters
|
||||
* will be copied. Always NUL terminates (unless siz <= strlen(dst)).
|
||||
* Returns strlen(src) + MIN(siz, strlen(initial dst)).
|
||||
* If retval >= siz, truncation occurred.
|
||||
*/
|
||||
size_t
|
||||
strlcat(char *dst, const char *src, size_t siz)
|
||||
{
|
||||
char *d = dst;
|
||||
const char *s = src;
|
||||
size_t n = siz;
|
||||
size_t dlen;
|
||||
|
||||
/* Find the end of dst and adjust bytes left but don't go past end */
|
||||
while (n-- != 0 && *d != '\0')
|
||||
d++;
|
||||
dlen = d - dst;
|
||||
n = siz - dlen;
|
||||
|
||||
if (n == 0)
|
||||
return(dlen + strlen(s));
|
||||
while (*s != '\0') {
|
||||
if (n != 1) {
|
||||
*d++ = *s;
|
||||
n--;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
*d = '\0';
|
||||
|
||||
return(dlen + (s - src)); /* count does not include NUL */
|
||||
}
|
||||
|
||||
#endif /* HAVE_STRLCAT */
|
||||
56
libtransmission/strlcpy.c
Normal file
56
libtransmission/strlcpy.c
Normal file
@@ -0,0 +1,56 @@
|
||||
/* $Id$ */
|
||||
/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef HAVE_STRLCPY
|
||||
|
||||
/*
|
||||
* Copy src to string dst of size siz. At most siz-1 characters
|
||||
* will be copied. Always NUL terminates (unless siz == 0).
|
||||
* Returns strlen(src); if retval >= siz, truncation occurred.
|
||||
*/
|
||||
size_t
|
||||
strlcpy(char *dst, const char *src, size_t siz)
|
||||
{
|
||||
char *d = dst;
|
||||
const char *s = src;
|
||||
size_t n = siz;
|
||||
|
||||
/* Copy as many bytes as will fit */
|
||||
if (n != 0) {
|
||||
while (--n != 0) {
|
||||
if ((*d++ = *s++) == '\0')
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Not enough room in dst, add NUL and traverse rest of src */
|
||||
if (n == 0) {
|
||||
if (siz != 0)
|
||||
*d = '\0'; /* NUL-terminate dst */
|
||||
while (*s++)
|
||||
;
|
||||
}
|
||||
|
||||
return(s - src - 1); /* count does not include NUL */
|
||||
}
|
||||
|
||||
#endif /* HAVE_STRLCPY */
|
||||
37
libtransmission/trcompat.h
Normal file
37
libtransmission/trcompat.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/******************************************************************************
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2005-2007 Transmission authors and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*****************************************************************************/
|
||||
#ifndef TRCOMPAT_H
|
||||
#define TRCOMPAT_H
|
||||
|
||||
#ifndef HAVE_STRLCPY
|
||||
size_t
|
||||
strlcpy(char *dst, const char *src, size_t siz);
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_STRLCAT
|
||||
size_t
|
||||
strlcat(char *dst, const char *src, size_t siz);
|
||||
#endif
|
||||
|
||||
#endif /* TRCOMPAT_H */
|
||||
70
mk/daemon.mk
Normal file
70
mk/daemon.mk
Normal file
@@ -0,0 +1,70 @@
|
||||
# $Id$
|
||||
|
||||
include ../mk/config.mk
|
||||
include ../mk/common.mk
|
||||
|
||||
COMSRCS = errors.c ipc.c misc.c
|
||||
SRVSRCS = daemon.c server.c torrents.c
|
||||
CLISRCS = client.c remote.c
|
||||
PXYSRCS = proxy.c
|
||||
|
||||
COMOBJS = $(COMSRCS:%.c=%.o)
|
||||
SRVOBJS = $(SRVSRCS:%.c=%.o)
|
||||
CLIOBJS = $(CLISRCS:%.c=%.o)
|
||||
PXYOBJS = $(PXYSRCS:%.c=%.o)
|
||||
SRCS = $(COMSRCS) $(SRVSRCS) $(CLISRCS) $(PXYSRCS)
|
||||
|
||||
CFLAGS += $(CFLAGS_EVENT) -I../libtransmission
|
||||
LDLIBS += ../libtransmission/libtransmission.a
|
||||
LDFLAGS += $(LDFLAGS_EVENT)
|
||||
|
||||
all: transmission-daemon transmission-remote transmission-proxy
|
||||
|
||||
transmission-daemon: OBJS = $(SRVOBJS) $(COMOBJS)
|
||||
transmission-daemon: $(LDLIBS) $(SRVOBJS) $(COMOBJS)
|
||||
$(LINK_RULE)
|
||||
|
||||
transmission-remote: OBJS = $(CLIOBJS) $(COMOBJS)
|
||||
transmission-remote: $(LDLIBS) $(CLIOBJS) $(COMOBJS)
|
||||
$(LINK_RULE)
|
||||
|
||||
transmission-proxy: OBJS = $(PXYOBJS) $(COMOBJS)
|
||||
transmission-proxy: $(LDLIBS) $(PXYOBJS) $(COMOBJS)
|
||||
$(LINK_RULE)
|
||||
|
||||
%.o: %.c ../mk/config.mk ../mk/common.mk ../mk/daemon.mk
|
||||
$(CC_RULE)
|
||||
|
||||
clean:
|
||||
@echo "Clean transmission-daemon"
|
||||
@echo "Clean transmission-remote"
|
||||
@echo "Clean transmission-proxy"
|
||||
@echo "Clean $(COMOBJS) $(SRVOBJS) $(CLIOBJS) $(PXYOBJS)"
|
||||
@$(RM) transmission-daemon transmission-remote
|
||||
@$(RM) $(COMOBJS) $(SRVOBJS) $(CLIOBJS) $(PXYOBJS)
|
||||
|
||||
.depend: $(SRCS) ../mk/config.mk ../mk/common.mk ../mk/daemon.mk
|
||||
$(DEP_RULE)
|
||||
|
||||
install: install.srv install.srv.man install.cli install.cli.man \
|
||||
install.pxy install.pxy.man
|
||||
|
||||
install.srv: transmission-daemon
|
||||
$(INSTALL_BIN_RULE)
|
||||
|
||||
install.srv.man: transmission-daemon.1
|
||||
$(INSTALL_MAN_RULE)
|
||||
|
||||
install.cli: transmission-remote
|
||||
$(INSTALL_BIN_RULE)
|
||||
|
||||
install.cli.man: transmission-remote.1
|
||||
$(INSTALL_MAN_RULE)
|
||||
|
||||
install.pxy: transmission-proxy
|
||||
$(INSTALL_BIN_RULE)
|
||||
|
||||
install.pxy.man: transmission-proxy.1
|
||||
$(INSTALL_MAN_RULE)
|
||||
|
||||
-include .depend
|
||||
@@ -7,6 +7,9 @@ TARGETS = .cli
|
||||
ifeq ($(GTK),yes)
|
||||
TARGETS += .gtk
|
||||
endif
|
||||
ifeq ($(DAEMON),yes)
|
||||
TARGETS += .daemon
|
||||
endif
|
||||
ifeq ($(SYSTEM),BeOS)
|
||||
TARGETS += .beos
|
||||
endif
|
||||
@@ -25,6 +28,10 @@ all: $(TARGETS)
|
||||
@echo "* Building Transmission GTK+ client"
|
||||
@$(MAKE) $(MAKEARGS) -C gtk -f ../mk/gtk.mk
|
||||
|
||||
.daemon: .lib
|
||||
@echo "* Building Transmission daemon client"
|
||||
@$(MAKE) $(MAKEARGS) -C daemon -f ../mk/daemon.mk
|
||||
|
||||
.beos: .lib
|
||||
@echo "* Building Transmission BeOS client"
|
||||
@$(MAKE) $(MAKEARGS) -C beos -f ../mk/beos.mk
|
||||
@@ -39,6 +46,10 @@ install: all $(foreach SUB,$(TARGETS),.install$(SUB)) .install.misc
|
||||
@echo "* Installing Transmission GTK+ client"
|
||||
@$(MAKE) $(MAKEARGS) -C gtk -f ../mk/gtk.mk install
|
||||
|
||||
.install.daemon: .daemon
|
||||
@echo "* Installing Transmission daemon client"
|
||||
@$(MAKE) $(MAKEARGS) -C daemon -f ../mk/daemon.mk install
|
||||
|
||||
.install.beos:
|
||||
|
||||
.install.misc:
|
||||
@@ -52,6 +63,9 @@ clean:
|
||||
ifeq ($(GTK),yes)
|
||||
@$(MAKE) $(MAKEARGS) -C gtk -f ../mk/gtk.mk clean
|
||||
endif
|
||||
ifeq ($(DAEMON),yes)
|
||||
@$(MAKE) $(MAKEARGS) -C daemon -f ../mk/daemon.mk clean
|
||||
endif
|
||||
ifeq ($(SYSTEM),BeOS)
|
||||
@$(MAKE) $(MAKEARGS) -C beos -f ../mk/beos.mk clean
|
||||
endif
|
||||
|
||||
@@ -6,7 +6,7 @@ include ../mk/common.mk
|
||||
SRCS = transmission.c bencode.c net.c tracker.c peer.c inout.c \
|
||||
metainfo.c sha1.c utils.c fdlimit.c clients.c completion.c \
|
||||
platform.c ratecontrol.c choking.c natpmp.c upnp.c http.c xml.c \
|
||||
shared.c torrent.c
|
||||
shared.c torrent.c strlcpy.c strlcat.c
|
||||
|
||||
OBJS = $(SRCS:%.c=%.o)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user