/* * This file Copyright (C) 2008 Charles Kerr * * This file is licensed by the GPL version 2. Works owned by the * Transmission project are granted a special exemption to clause 2(b) * so that the bulk of its code can remain under the MIT license. * This exemption does not extend to derived works not owned by * the Transmission project. * * $Id$ */ #include #include #include /* printf */ #include /* exit, atoi */ #include /* strcmp */ #include /* open */ #include #include /* daemon, getcwd */ #include #include #include #include #include #include #define MY_NAME "transmission-daemon" static int closing = FALSE; static tr_handle * mySession; static char myConfigFilename[MAX_PATH_LENGTH]; #define KEY_BLOCKLIST "blocklist-enabled" #define KEY_DOWNLOAD_DIR "download-dir" #define KEY_ENCRYPTION "encryption" #define KEY_PEER_LIMIT "max-peers-global" #define KEY_PEER_PORT "peer-port" #define KEY_PORT_FORWARDING "port-forwarding-enabled" #define KEY_PEX_ENABLED "pex-enabled" #define KEY_AUTH_REQUIRED "rpc-authentication-required" #define KEY_USERNAME "rpc-username" #define KEY_PASSWORD "rpc-password" #define KEY_ACL "rpc-access-control-list" #define KEY_RPC_PORT "rpc-port" #define KEY_DSPEED "download-limit" #define KEY_DSPEED_ENABLED "download-limit-enabled" #define KEY_USPEED "upload-limit" #define KEY_USPEED_ENABLED "upload-limit-enabled" #define CONFIG_FILE "settings.json" /*** **** Config File ***/ static void replaceInt( tr_benc * dict, const char * key, int64_t value ) { tr_bencDictRemove( dict, key ); tr_bencDictAddInt( dict, key, value ); } static void replaceStr( tr_benc * dict, const char * key, const char* value ) { tr_bencDictRemove( dict, key ); tr_bencDictAddStr( dict, key, value ); } static void saveState( tr_session * s ) { int i, n = 0; char * strs[4]; tr_benc d; if( tr_bencLoadJSONFile( myConfigFilename, &d ) ) tr_bencInitDict( &d, 16 ); replaceInt( &d, KEY_BLOCKLIST, tr_blocklistIsEnabled( s ) ); replaceStr( &d, KEY_DOWNLOAD_DIR, tr_sessionGetDownloadDir( s ) ); replaceInt( &d, KEY_PEER_LIMIT, tr_sessionGetPeerLimit( s ) ); replaceInt( &d, KEY_PEER_PORT, tr_sessionGetPeerPort( s ) ); replaceInt( &d, KEY_PORT_FORWARDING, tr_sessionIsPortForwardingEnabled( s ) ); replaceInt( &d, KEY_PEX_ENABLED, tr_sessionIsPexEnabled( s ) ); replaceStr( &d, KEY_USERNAME, strs[n++] = tr_sessionGetRPCUsername( s ) ); replaceStr( &d, KEY_PASSWORD, strs[n++] = tr_sessionGetRPCPassword( s ) ); replaceStr( &d, KEY_ACL, strs[n++] = tr_sessionGetRPCACL( s ) ); replaceInt( &d, KEY_RPC_PORT, tr_sessionGetRPCPort( s ) ); replaceInt( &d, KEY_AUTH_REQUIRED, tr_sessionIsRPCPasswordEnabled( s ) ); replaceInt( &d, KEY_DSPEED, tr_sessionGetSpeedLimit( s, TR_DOWN ) ); replaceInt( &d, KEY_DSPEED_ENABLED, tr_sessionIsSpeedLimitEnabled( s, TR_DOWN ) ); replaceInt( &d, KEY_USPEED, tr_sessionGetSpeedLimit( s, TR_UP ) ); replaceInt( &d, KEY_USPEED_ENABLED, tr_sessionIsSpeedLimitEnabled( s, TR_UP ) ); replaceInt( &d, KEY_ENCRYPTION, tr_sessionGetEncryption( s ) ); tr_bencSaveJSONFile( myConfigFilename, &d ); tr_bencFree( &d ); tr_ninf( MY_NAME, "saved \"%s\"", myConfigFilename ); for( i=0; i" }, { 'b', "blocklist", "Enable peer blocklists", "b", 0, NULL }, { 'B', "no-blocklist", "Disable peer blocklists", "B", 0, NULL }, { 'f', "foreground", "Run in the foreground instead of daemonizing", "f", 0, NULL }, { 'g', "config-dir", "Where to look for configuration files", "g", 1, "" }, { 'p', "port", "Port for incoming peers (Default: "TR_DEFAULT_PORT_STR")", "p", 1, "" }, { 't', "auth", "Requre authentication", "t", 0, NULL }, { 'T', "no-auth", "Don't require authentication", "T", 0, NULL }, { 'u', "username", "Set username for authentication", "u", 1, "" }, { 'v', "password", "Set password for authentication", "v", 1, "" }, { 'w', "download-dir", "Where to save downloaded data", "w", 1, "" }, { 0, NULL, NULL, NULL, 0, NULL } }; static void showUsage( void ) { tr_getopt_usage( MY_NAME, getUsage(), options ); exit( 0 ); } static void readargs( int argc, const char ** argv, int * nofork, const char ** configDir, const char ** downloadDir, int * rpcPort, const char ** acl, int * authRequired, const char ** username, const char ** password, int * blocklistEnabled ) { int c; const char * optarg; while(( c = tr_getopt( getUsage(), argc, argv, options, &optarg ))) { switch( c ) { case 'a': *acl = optarg; break; case 'b': *blocklistEnabled = 1; break; case 'B': *blocklistEnabled = 0; break; case 'f': *nofork = 1; break; case 'g': *configDir = optarg; break; case 'p': *rpcPort = atoi( optarg ); break; case 't': *authRequired = TRUE; break; case 'T': *authRequired = FALSE; break; case 'u': *username = optarg; break; case 'v': *password = optarg; break; case 'w': *downloadDir = optarg; break; default: showUsage( ); break; } } } static void gotsig( int sig UNUSED ) { closing = TRUE; } #if !defined(HAVE_DAEMON) static int daemon( int nochdir, int noclose ) { switch( fork( ) ) { case 0: break; case -1: tr_nerr( MY_NAME, "Error daemonizing (fork)! %d - %s", errno, strerror(errno) ); return -1; default: _exit(0); } if( setsid() < 0 ) { tr_nerr( MY_NAME, "Error daemonizing (setsid)! %d - %s", errno, strerror(errno) ); return -1; } switch( fork( ) ) { case 0: break; case -1: tr_nerr( MY_NAME, "Error daemonizing (fork2)! %d - %s", errno, strerror(errno) ); return -1; default: _exit(0); } if( !nochdir && 0 > chdir( "/" ) ) { tr_nerr( MY_NAME, "Error daemonizing (chdir)! %d - %s", errno, strerror(errno) ); return -1; } if( !noclose ) { int fd; if((( fd = open("/dev/null", O_RDONLY))) != 0 ) { dup2( fd, 0 ); close( fd ); } if((( fd = open("/dev/null", O_WRONLY))) != 1 ) { dup2( fd, 1 ); close( fd ); } if((( fd = open("/dev/null", O_WRONLY))) != 2 ) { dup2( fd, 2 ); close( fd ); } } return 0; } #endif int main( int argc, char ** argv ) { int nofork = 0; int rpcPort = -1; int authRequired = -1; int blocklistEnabled = -1; char * freeme = NULL; const char * configDir = NULL; const char * downloadDir = NULL; const char * acl = NULL; const char * username = NULL; const char * password = NULL; signal( SIGINT, gotsig ); signal( SIGQUIT, gotsig ); signal( SIGTERM, gotsig ); signal( SIGPIPE, SIG_IGN ); signal( SIGHUP, SIG_IGN ); readargs( argc, (const char**)argv, &nofork, &configDir, &downloadDir, &rpcPort, &acl, &authRequired, &username, &password, &blocklistEnabled ); if( configDir == NULL ) configDir = freeme = tr_strdup_printf( "%s-daemon", tr_getDefaultConfigDir() ); tr_buildPath( myConfigFilename, sizeof( myConfigFilename ), configDir, CONFIG_FILE, NULL ); if( !nofork ) { if( 0 > daemon( 1, 0 ) ) { fprintf( stderr, "failed to daemonize: %s\n", strerror( errno ) ); exit( 1 ); } } session_init( configDir, downloadDir, rpcPort, acl, authRequired, username, password, blocklistEnabled ); while( !closing ) sleep( 1 ); saveState( mySession ); printf( "Closing transmission session..." ); tr_sessionClose( mySession ); printf( " done.\n" ); tr_free( freeme ); return 0; }