source: branches/1.4x/daemon/daemon.c @ 7346

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

(1.4x daemon) #1583: daemon ignores IP address whitelist

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