source: trunk/daemon/daemon.c @ 7151

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

(libT) add #ifdefs to ensure that client apps don't #include private libtransmission headers.

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