source: trunk/daemon/daemon.c @ 7345

Last change on this file since 7345 was 7345, checked in by charles, 12 years ago

(trunk daemon) #1583: daemon ignores IP address whitelist

  • Property svn:keywords set to Date Rev Author Id
File size: 15.2 KB
Line 
1/*
2 * This file Copyright (C) 2008 Charles Kerr <charles@rebelbase.com>
3 *
4 * This file is licensed by the GPL version 2.  Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: daemon.c 7345 2008-12-10 19:00:19Z charles $
11 */
12
13#include <assert.h>
14#include <errno.h>
15#include <stdio.h> /* printf */
16#include <stdlib.h> /* exit, atoi */
17#include <string.h> /* strcmp */
18
19#include <fcntl.h> /* open */
20#include <signal.h>
21#include <unistd.h> /* daemon */
22
23#include <libtransmission/transmission.h>
24#include <libtransmission/bencode.h>
25#include <libtransmission/tr-getopt.h>
26#include <libtransmission/utils.h>
27#include <libtransmission/version.h>
28
29#define MY_NAME "transmission-daemon"
30
31static int           closing = FALSE;
32static tr_session  * mySession = NULL;
33static char        * myConfigFilename = NULL;
34
35#define KEY_BLOCKLIST         "blocklist-enabled"
36#define KEY_DOWNLOAD_DIR      "download-dir"
37#define KEY_ENCRYPTION        "encryption"
38#define KEY_LAZY_BITFIELD     "lazy-bitfield-enabled"
39#define KEY_PEER_LIMIT        "max-peers-global"
40#define KEY_PEER_PORT         "peer-port"
41#define KEY_PORT_FORWARDING   "port-forwarding-enabled"
42#define KEY_PEX_ENABLED       "pex-enabled"
43#define KEY_AUTH_REQUIRED     "rpc-authentication-required"
44#define KEY_USERNAME          "rpc-username"
45#define KEY_PASSWORD          "rpc-password"
46#define KEY_WHITELIST         "rpc-whitelist"
47#define KEY_WHITELIST_ENABLED "rpc-whitelist-enabled"
48#define KEY_RPC_PORT          "rpc-port"
49#define KEY_DSPEED            "download-limit"
50#define KEY_DSPEED_ENABLED    "download-limit-enabled"
51#define KEY_USPEED            "upload-limit"
52#define KEY_USPEED_ENABLED    "upload-limit-enabled"
53
54#define CONFIG_FILE           "settings.json"
55
56/***
57****  Config File
58***/
59
60static void
61replaceInt( tr_benc *    dict,
62            const char * key,
63            int64_t      value )
64{
65    tr_bencDictRemove( dict, key );
66    tr_bencDictAddInt( dict, key, value );
67}
68
69static void
70replaceStr( tr_benc *    dict,
71            const char * key,
72            const char*  value )
73{
74    tr_bencDictRemove( dict, key );
75    tr_bencDictAddStr( dict, key, value );
76}
77
78static void
79saveState( tr_session * s )
80{
81    int     i, n = 0;
82    char *  strs[4];
83
84    tr_benc d;
85
86    if( tr_bencLoadJSONFile( myConfigFilename, &d ) )
87        tr_bencInitDict( &d, 16 );
88
89    replaceInt( &d, KEY_BLOCKLIST,       tr_blocklistIsEnabled( s ) );
90    replaceStr( &d, KEY_DOWNLOAD_DIR,    tr_sessionGetDownloadDir( s ) );
91    replaceInt( &d, KEY_PEER_LIMIT,      tr_sessionGetPeerLimit( s ) );
92    replaceInt( &d, KEY_PEER_PORT,       tr_sessionGetPeerPort( s ) );
93    replaceInt( &d, KEY_PORT_FORWARDING, tr_sessionIsPortForwardingEnabled( s ) );
94    replaceInt( &d, KEY_PEX_ENABLED,     tr_sessionIsPexEnabled( s ) );
95    replaceStr( &d, KEY_USERNAME,        strs[n++] = tr_sessionGetRPCUsername( s ) );
96    replaceStr( &d, KEY_PASSWORD,        strs[n++] = tr_sessionGetRPCPassword( s ) );
97    replaceStr( &d, KEY_WHITELIST,       strs[n++] = tr_sessionGetRPCWhitelist( s ) );
98    replaceInt( &d, KEY_RPC_PORT,        tr_sessionGetRPCPort( s ) );
99    replaceInt( &d, KEY_AUTH_REQUIRED,   tr_sessionIsRPCPasswordEnabled( s ) );
100    replaceInt( &d, KEY_DSPEED,          tr_sessionGetSpeedLimit( s, TR_DOWN ) );
101    replaceInt( &d, KEY_DSPEED_ENABLED,  tr_sessionIsSpeedLimitEnabled( s, TR_DOWN ) );
102    replaceInt( &d, KEY_USPEED,          tr_sessionGetSpeedLimit( s, TR_UP ) );
103    replaceInt( &d, KEY_USPEED_ENABLED,  tr_sessionIsSpeedLimitEnabled( s, TR_UP ) );
104    replaceInt( &d, KEY_ENCRYPTION,      tr_sessionGetEncryption( s ) );
105
106    tr_bencSaveJSONFile( myConfigFilename, &d );
107    tr_bencFree( &d );
108    tr_ninf( MY_NAME, "saved \"%s\"", myConfigFilename );
109
110    for( i = 0; i < n; ++i )
111        tr_free( strs[i] );
112}
113
114static void
115getConfigInt( tr_benc *    dict,
116              const char * key,
117              int *        setme,
118              int          defaultVal )
119{
120    if( *setme < 0 )
121    {
122        int64_t i;
123        if( tr_bencDictFindInt( dict, key, &i ) )
124            *setme = i;
125        else
126            *setme = defaultVal;
127    }
128}
129
130static void
131getConfigStr( tr_benc *     dict,
132              const char *  key,
133              const char ** setme,
134              const char *  defaultVal )
135{
136    if( !*setme )
137    {
138        const char * s;
139        if( tr_bencDictFindStr( dict, key, &s ) )
140            *setme = s;
141        else
142            *setme = defaultVal;
143    }
144}
145
146/**
147 * @param port      port number, or -1 if not set in the command line
148 * @param auth      TRUE, FALSE, or -1 if not set on the command line
149 * @param blocklist TRUE, FALSE, or -1 if not set on the command line
150 */
151static void
152session_init( const char * configDir,
153              const char * downloadDir,
154              int          rpcPort,
155              const char * whitelist,
156              int          authRequired,
157              const char * username,
158              const char * password,
159              int          blocklistEnabled )
160{
161    tr_benc       state, *dict = NULL;
162    int           peerPort = -1, peers = -1;
163    int           whitelistEnabled = -1;
164    int           pexEnabled = -1;
165    int           fwdEnabled = -1;
166    int           upLimit = -1, upLimited = -1, downLimit = -1,
167                  downLimited = -1;
168    int           encryption = -1;
169    int           useLazyBitfield = -1;
170    tr_ctor *     ctor;
171    tr_torrent ** torrents;
172
173    if( !tr_bencLoadJSONFile( myConfigFilename, &state ) )
174        dict = &state;
175   
176
177    /***
178    ****  Decide on which values to pass into tr_sessionInitFull().
179    ****  The command-line arguments are given precedence and
180    ****  the state file from the previous session is used as a fallback.
181    ****  If neither of those can be found, the TR_DEFAULT fields are used .
182    ***/
183
184    getConfigStr( dict, KEY_DOWNLOAD_DIR,      &downloadDir,       TR_DEFAULT_DOWNLOAD_DIR );
185    getConfigInt( dict, KEY_PEX_ENABLED,       &pexEnabled,        TR_DEFAULT_PEX_ENABLED );
186    getConfigInt( dict, KEY_PORT_FORWARDING,   &fwdEnabled,        TR_DEFAULT_PORT_FORWARDING_ENABLED );
187    getConfigInt( dict, KEY_PEER_PORT,         &peerPort,          TR_DEFAULT_PORT );
188    getConfigInt( dict, KEY_DSPEED,            &downLimit,         100 );
189    getConfigInt( dict, KEY_DSPEED_ENABLED,    &downLimited,       FALSE );
190    getConfigInt( dict, KEY_USPEED,            &upLimit,           100 );
191    getConfigInt( dict, KEY_USPEED_ENABLED,    &upLimited,         FALSE );
192    getConfigInt( dict, KEY_LAZY_BITFIELD,     &useLazyBitfield,   TR_DEFAULT_LAZY_BITFIELD_ENABLED );
193    getConfigInt( dict, KEY_PEER_LIMIT,        &peers,             TR_DEFAULT_GLOBAL_PEER_LIMIT );
194    getConfigInt( dict, KEY_BLOCKLIST,         &blocklistEnabled,  TR_DEFAULT_BLOCKLIST_ENABLED );
195    getConfigInt( dict, KEY_RPC_PORT,          &rpcPort,           TR_DEFAULT_RPC_PORT );
196    getConfigStr( dict, KEY_WHITELIST,         &whitelist,         TR_DEFAULT_RPC_WHITELIST );
197    getConfigInt( dict, KEY_AUTH_REQUIRED,     &authRequired,      FALSE );
198    getConfigStr( dict, KEY_USERNAME,          &username,          NULL );
199    getConfigStr( dict, KEY_PASSWORD,          &password,          NULL );
200    getConfigInt( dict, KEY_ENCRYPTION,        &encryption,        TR_DEFAULT_ENCRYPTION );
201
202    whitelistEnabled = whitelist && *whitelist;
203
204    /***
205    ****
206    ***/
207
208    /* start the session */
209    mySession = tr_sessionInitFull( configDir, "daemon", downloadDir,
210                                    pexEnabled, fwdEnabled, peerPort,
211                                    encryption,
212                                    useLazyBitfield,
213                                    upLimited, upLimit,
214                                    downLimited, downLimit,
215                                    peers,
216                                    TR_MSG_INF, 0,
217                                    blocklistEnabled,
218                                    TR_DEFAULT_PEER_SOCKET_TOS,
219                                    TRUE, rpcPort,
220                                    whitelistEnabled, whitelist,
221                                    authRequired, username, password,
222                                    TR_DEFAULT_PROXY_ENABLED,
223                                    TR_DEFAULT_PROXY,
224                                    TR_DEFAULT_PROXY_PORT,
225                                    TR_DEFAULT_PROXY_TYPE,
226                                    TR_DEFAULT_PROXY_AUTH_ENABLED,
227                                    TR_DEFAULT_PROXY_USERNAME,
228                                    TR_DEFAULT_PROXY_PASSWORD );
229
230
231    if( authRequired )
232        tr_ninf( MY_NAME, "requiring authentication" );
233
234    /* load the torrents */
235    ctor = tr_ctorNew( mySession );
236    torrents = tr_sessionLoadTorrents( mySession, ctor, NULL );
237    tr_free( torrents );
238    tr_ctorFree( ctor );
239
240    if( dict )
241        tr_bencFree( &state );
242}
243
244static const char *
245getUsage( void )
246{
247    return "Transmission " LONG_VERSION_STRING
248           "  http://www.transmissionbt.com/\n"
249           "A fast and easy BitTorrent client\n"
250           "\n"
251           MY_NAME " is a headless Transmission session\n"
252                   "that can be controlled via transmission-remote or Clutch.\n"
253                   "\n"
254                   "Usage: " MY_NAME " [options]";
255}
256
257static const struct tr_option options[] =
258{
259    { 'a', "allowed",
260      "Allowed IP addresses.  (Default: " TR_DEFAULT_RPC_WHITELIST ")",       "a",
261      1, "<list>"     },
262    { 'b', "blocklist",    "Enable peer blocklists",
263      "b",             0, NULL         },
264    { 'B', "no-blocklist", "Disable peer blocklists",
265      "B",             0, NULL         },
266    { 'f', "foreground",   "Run in the foreground instead of daemonizing",
267      "f",             0, NULL         },
268    { 'g', "config-dir",   "Where to look for configuration files",
269      "g",             1, "<path>"     },
270    { 'p', "port",
271      "RPC port (Default: " TR_DEFAULT_RPC_PORT_STR ")",               "p",
272      1, "<port>"     },
273    { 't', "auth",         "Require authentication",
274      "t",             0, NULL         },
275    { 'T', "no-auth",      "Don't require authentication",
276      "T",             0, NULL         },
277    { 'u', "username",     "Set username for authentication",
278      "u",             1, "<username>" },
279    { 'v', "password",     "Set password for authentication",
280      "v",             1, "<password>" },
281    { 'w', "download-dir", "Where to save downloaded data",
282      "w",             1, "<path>"     },
283    {   0, NULL,           NULL,
284        NULL,            0, NULL         }
285};
286
287static void
288showUsage( void )
289{
290    tr_getopt_usage( MY_NAME, getUsage( ), options );
291    exit( 0 );
292}
293
294static void
295readargs( int           argc,
296          const char ** argv,
297          int *         nofork,
298          const char ** configDir,
299          const char ** downloadDir,
300          int *         rpcPort,
301          const char ** whitelist,
302          int *         authRequired,
303          const char ** username,
304          const char ** password,
305          int *         blocklistEnabled )
306{
307    int          c;
308    const char * optarg;
309
310    while( ( c = tr_getopt( getUsage( ), argc, argv, options, &optarg ) ) )
311    {
312        switch( c )
313        {
314            case 'a':
315                *whitelist = optarg; break;
316
317            case 'b':
318                *blocklistEnabled = 1; break;
319
320            case 'B':
321                *blocklistEnabled = 0; break;
322
323            case 'f':
324                *nofork = 1; break;
325
326            case 'g':
327                *configDir = optarg; break;
328
329            case 'p':
330                *rpcPort = atoi( optarg ); break;
331
332            case 't':
333                *authRequired = TRUE; break;
334
335            case 'T':
336                *authRequired = FALSE; break;
337
338            case 'u':
339                *username = optarg; break;
340
341            case 'v':
342                *password = optarg; break;
343
344            case 'w':
345                *downloadDir = optarg; break;
346
347            default:
348                showUsage( ); break;
349        }
350    }
351}
352
353static void
354gotsig( int sig UNUSED )
355{
356    closing = TRUE;
357}
358
359#if !defined( WIN32 )
360#if !defined( HAVE_DAEMON )
361static int
362daemon( int nochdir,
363        int noclose )
364{
365    switch( fork( ) )
366    {
367        case 0:
368            break;
369
370        case - 1:
371            tr_nerr( MY_NAME, "Error daemonizing (fork)! %d - %s", errno,
372                    strerror(
373                        errno ) );
374            return -1;
375
376        default:
377            _exit( 0 );
378    }
379
380    if( setsid( ) < 0 )
381    {
382        tr_nerr( MY_NAME, "Error daemonizing (setsid)! %d - %s", errno,
383                strerror(
384                    errno ) );
385        return -1;
386    }
387
388    switch( fork( ) )
389    {
390        case 0:
391            break;
392
393        case - 1:
394            tr_nerr( MY_NAME, "Error daemonizing (fork2)! %d - %s", errno,
395                    strerror(
396                        errno ) );
397            return -1;
398
399        default:
400            _exit( 0 );
401    }
402
403    if( !nochdir && 0 > chdir( "/" ) )
404    {
405        tr_nerr( MY_NAME, "Error daemonizing (chdir)! %d - %s", errno,
406                strerror(
407                    errno ) );
408        return -1;
409    }
410
411    if( !noclose )
412    {
413        int fd;
414        if( ( ( fd = open( "/dev/null", O_RDONLY ) ) ) != 0 )
415        {
416            dup2( fd,  0 );
417            close( fd );
418        }
419        if( ( ( fd = open( "/dev/null", O_WRONLY ) ) ) != 1 )
420        {
421            dup2( fd, 1 );
422            close( fd );
423        }
424        if( ( ( fd = open( "/dev/null", O_WRONLY ) ) ) != 2 )
425        {
426            dup2( fd, 2 );
427            close( fd );
428        }
429    }
430
431    return 0;
432}
433#endif
434#endif
435
436int
437main( int     argc,
438      char ** argv )
439{
440    int          nofork = 0;
441    int          rpcPort = -1;
442    int          authRequired = -1;
443    int          blocklistEnabled = -1;
444    char *       freeme = NULL;
445    const char * configDir = NULL;
446    const char * downloadDir = NULL;
447    const char * whitelist = NULL;
448    const char * username = NULL;
449    const char * password = NULL;
450
451    signal( SIGINT, gotsig );
452    signal( SIGTERM, gotsig );
453#ifndef WIN32
454    signal( SIGQUIT, gotsig );
455    signal( SIGPIPE, SIG_IGN );
456    signal( SIGHUP, SIG_IGN );
457#endif
458
459    readargs( argc, (const char**)argv, &nofork, &configDir, &downloadDir,
460              &rpcPort, &whitelist, &authRequired, &username, &password,
461              &blocklistEnabled );
462    if( configDir == NULL )
463        configDir = getenv( "TRANSMISSION_HOME" );
464    if( configDir == NULL )
465        configDir = freeme = tr_strdup_printf( "%s-daemon",
466                                               tr_getDefaultConfigDir( ) );
467    myConfigFilename = tr_buildPath( configDir, CONFIG_FILE, NULL );
468
469#ifndef WIN32
470    if( !nofork )
471    {
472        if( 0 > daemon( 1, 0 ) )
473        {
474            fprintf( stderr, "failed to daemonize: %s\n", strerror( errno ) );
475            exit( 1 );
476        }
477    }
478#endif
479
480    session_init( configDir, downloadDir,
481                  rpcPort, whitelist, authRequired, username, password,
482                  blocklistEnabled );
483
484    while( !closing )
485        tr_wait( 1000 ); /* sleep one second */
486
487    saveState( mySession );
488    printf( "Closing transmission session..." );
489    tr_sessionClose( mySession );
490    printf( " done.\n" );
491
492    tr_free( freeme );
493    tr_free( myConfigFilename );
494    return 0;
495}
496
Note: See TracBrowser for help on using the repository browser.