source: trunk/daemon/daemon.c @ 6978

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

janitorial: tr_handle -> tr_session

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