source: trunk/daemon/daemon.c @ 8903

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

(trunk) all this commit does is remove trailing whitespace from some c, c++, and javascript source

  • Property svn:keywords set to Date Rev Author Id
File size: 12.1 KB
Line 
1/*
2 * This file Copyright (C) 2008-2009 Charles Kerr <charles@transmissionbt.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 8903 2009-08-12 14:40:32Z 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 <sys/types.h> /* umask*/
20#include <sys/stat.h> /* umask*/
21
22#include <fcntl.h> /* open */
23#include <signal.h>
24#include <unistd.h> /* daemon */
25
26#include <event.h>
27
28#include <libtransmission/transmission.h>
29#include <libtransmission/bencode.h>
30#include <libtransmission/tr-getopt.h>
31#include <libtransmission/utils.h>
32#include <libtransmission/version.h>
33
34#include "watch.h"
35
36#define MY_NAME "transmission-daemon"
37
38#define PREF_KEY_DIR_WATCH          "watch-dir"
39#define PREF_KEY_DIR_WATCH_ENABLED  "watch-dir-enabled"
40
41static tr_bool paused = FALSE;
42static tr_bool closing = FALSE;
43static tr_session * mySession = NULL;
44
45/***
46****  Config File
47***/
48
49static const char *
50getUsage( void )
51{
52    return "Transmission " LONG_VERSION_STRING
53           "  http://www.transmissionbt.com/\n"
54           "A fast and easy BitTorrent client\n"
55           "\n"
56           MY_NAME " is a headless Transmission session\n"
57           "that can be controlled via transmission-remote or Clutch.\n"
58           "\n"
59           "Usage: " MY_NAME " [options]";
60}
61
62static const struct tr_option options[] =
63{
64    { 'a', "allowed", "Allowed IP addresses.  (Default: " TR_DEFAULT_RPC_WHITELIST ")", "a", 1, "<list>" },
65    { 'b', "blocklist", "Enable peer blocklists", "b", 0, NULL },
66    { 'B', "no-blocklist", "Disable peer blocklists", "B", 0, NULL },
67    { 'c', "watch-dir", "Directory to watch for new .torrent files", "c", 1, "<directory>" },
68    { 'C', "no-watch-dir", "Disable the watch-dir", "C", 0, NULL },
69    { 'd', "dump-settings", "Dump the settings and exit", "d", 0, NULL },
70    { 'f', "foreground", "Run in the foreground instead of daemonizing", "f", 0, NULL },
71    { 'g', "config-dir", "Where to look for configuration files", "g", 1, "<path>" },
72    { 'p', "port", "RPC port (Default: " TR_DEFAULT_RPC_PORT_STR ")", "p", 1, "<port>" },
73    { 't', "auth", "Require authentication", "t", 0, NULL },
74    { 'T', "no-auth", "Don't require authentication", "T", 0, NULL },
75    { 'u', "username", "Set username for authentication", "u", 1, "<username>" },
76    { 'v', "password", "Set password for authentication", "v", 1, "<password>" },
77    { 'V', "version", "Show version number and exit", "V", 0, NULL },
78    { 'w', "download-dir", "Where to save downloaded data", "w", 1, "<path>" },
79    { 800, "paused", "Pause all torrents on startup", NULL, 0, NULL },
80    { 'P', "peerport", "Port for incoming peers (Default: " TR_DEFAULT_PEER_PORT_STR ")", "P", 1, "<port>" },
81    { 'm', "portmap", "Enable portmapping via NAT-PMP or UPnP", "m", 0, NULL },
82    { 'M', "no-portmap", "Disable portmapping", "M", 0, NULL },
83    { 'L', "peerlimit-global", "Maximum overall number of peers (Default: " TR_DEFAULT_PEER_LIMIT_GLOBAL_STR ")", "L", 1, "<limit>" },
84    { 'l', "peerlimit-torrent", "Maximum number of peers per torrent (Default: " TR_DEFAULT_PEER_LIMIT_TORRENT_STR ")", "l", 1, "<limit>" },
85    { 910, "encryption-required",  "Encrypt all peer connections", "er", 0, NULL },
86    { 911, "encryption-preferred", "Prefer encrypted peer connections", "ep", 0, NULL },
87    { 912, "encryption-tolerated", "Prefer unencrypted peer connections", "et", 0, NULL },
88    { 'i', "bind-address-ipv4", "Where to listen for peer connections", "i", 1, "<ipv4 address>" },
89    { 'I', "bind-address-ipv6", "Where to listen for peer connections", "I", 1, "<ipv6 address>" },
90    { 'r', "rpc-bind-address", "Where to listen for RPC connections", "r", 1, "<ipv4 address>" },
91    { 0, NULL, NULL, NULL, 0, NULL }
92};
93
94static void
95showUsage( void )
96{
97    tr_getopt_usage( MY_NAME, getUsage( ), options );
98    exit( 0 );
99}
100
101static void
102gotsig( int sig UNUSED )
103{
104    closing = TRUE;
105}
106
107#if defined(WIN32)
108 #define USE_NO_DAEMON
109#elif !defined(HAVE_DAEMON) || defined(__UCLIBC__)
110 #define USE_TR_DAEMON
111#else
112 #define USE_OS_DAEMON
113#endif
114
115static int
116tr_daemon( int nochdir, int noclose )
117{
118#if defined(USE_OS_DAEMON)
119    return daemon( nochdir, noclose );
120#elif defined(USE_TR_DAEMON)
121    pid_t pid = fork( );
122    if( pid < 0 )
123        return -1;
124    else if( pid > 0 )
125        _exit( 0 );
126    else {
127        pid = setsid( );
128        if( pid < 0 )
129            return -1;
130
131        pid = fork( );
132        if( pid < 0 )
133            return -1;
134        else if( pid > 0 )
135            _exit( 0 );
136        else {
137
138            if( !nochdir )
139                if( chdir( "/" ) < 0 )
140                    return -1;
141
142            umask( (mode_t)0 );
143
144            if( !noclose ) {
145                /* send stdin, stdout, and stderr to /dev/null */
146                int i;
147                int fd = open( "/dev/null", O_RDWR, 0 );
148                for( i=0; i<3; ++i ) {
149                    if( close( i ) )
150                        return -1;
151                    dup2( fd, i );
152                }
153                close( fd );
154            }
155
156            return 0;
157        }
158    }
159#else /* USE_NO_DAEMON */
160    return 0;
161#endif
162}
163
164static const char*
165getConfigDir( int argc, const char ** argv )
166{
167    int c;
168    const char * configDir = NULL;
169    const char * optarg;
170    const int ind = tr_optind;
171
172    while(( c = tr_getopt( getUsage( ), argc, argv, options, &optarg ))) {
173        if( c == 'g' ) {
174            configDir = optarg;
175            break;
176        }
177    }
178
179    tr_optind = ind;
180
181    if( configDir == NULL )
182        configDir = tr_getDefaultConfigDir( MY_NAME );
183
184    return configDir;
185}
186
187static void
188onFileAdded( tr_session * session, const char * dir, const char * file )
189{
190    if( strstr( file, ".torrent" ) != NULL )
191    {
192        char * filename = tr_buildPath( dir, file, NULL );
193        tr_ctor * ctor = tr_ctorNew( session );
194
195        int err = tr_ctorSetMetainfoFromFile( ctor, filename );
196        if( !err )
197            tr_torrentNew( ctor, &err );
198
199        tr_ctorFree( ctor );
200        tr_free( filename );
201    }
202}
203
204int
205main( int argc, char ** argv )
206{
207    int c;
208    const char * optarg;
209    tr_benc settings;
210    tr_bool boolVal;
211    tr_bool foreground = FALSE;
212    tr_bool dumpSettings = FALSE;
213    const char * configDir = NULL;
214    dtr_watchdir * watchdir = NULL;
215
216    signal( SIGINT, gotsig );
217    signal( SIGTERM, gotsig );
218#ifndef WIN32
219    signal( SIGQUIT, gotsig );
220    signal( SIGPIPE, SIG_IGN );
221    signal( SIGHUP, SIG_IGN );
222#endif
223
224    /* load settings from defaults + config file */
225    tr_bencInitDict( &settings, 0 );
226    configDir = getConfigDir( argc, (const char**)argv );
227    tr_sessionLoadSettings( &settings, configDir, MY_NAME );
228    tr_bencDictAddBool( &settings, TR_PREFS_KEY_RPC_ENABLED, TRUE );
229
230    /* overwrite settings from the comamndline */
231    tr_optind = 1;
232    while(( c = tr_getopt( getUsage(), argc, (const char**)argv, options, &optarg ))) {
233        switch( c ) {
234            case 'a': tr_bencDictAddStr( &settings, TR_PREFS_KEY_RPC_WHITELIST, optarg );
235                      tr_bencDictAddBool( &settings, TR_PREFS_KEY_RPC_WHITELIST_ENABLED, TRUE );
236                      break;
237            case 'b': tr_bencDictAddBool( &settings, TR_PREFS_KEY_BLOCKLIST_ENABLED, TRUE );
238                      break;
239            case 'B': tr_bencDictAddBool( &settings, TR_PREFS_KEY_BLOCKLIST_ENABLED, FALSE );
240                      break;
241            case 'c': tr_bencDictAddStr( &settings, PREF_KEY_DIR_WATCH, optarg );
242                      tr_bencDictAddBool( &settings, PREF_KEY_DIR_WATCH_ENABLED, TRUE );
243                      break;
244            case 'C': tr_bencDictAddBool( &settings, PREF_KEY_DIR_WATCH_ENABLED, FALSE );
245                      break;
246            case 'd': dumpSettings = TRUE;
247                      break;
248            case 'f': foreground = TRUE;
249                      break;
250            case 'g': /* handled above */
251                      break;
252            case 'V': /* version */
253                      fprintf(stderr, "Transmission %s\n", LONG_VERSION_STRING);
254                      exit( 0 );
255            case 'p': tr_bencDictAddInt( &settings, TR_PREFS_KEY_RPC_PORT, atoi( optarg ) );
256                      break;
257            case 't': tr_bencDictAddBool( &settings, TR_PREFS_KEY_RPC_AUTH_REQUIRED, TRUE );
258                      break;
259            case 'T': tr_bencDictAddBool( &settings, TR_PREFS_KEY_RPC_AUTH_REQUIRED, FALSE );
260                      break;
261            case 'u': tr_bencDictAddStr( &settings, TR_PREFS_KEY_RPC_USERNAME, optarg );
262                      break;
263            case 'v': tr_bencDictAddStr( &settings, TR_PREFS_KEY_RPC_PASSWORD, optarg );
264                      break;
265            case 'w': tr_bencDictAddStr( &settings, TR_PREFS_KEY_DOWNLOAD_DIR, optarg );
266                      break;
267            case 'P': tr_bencDictAddInt( &settings, TR_PREFS_KEY_PEER_PORT, atoi( optarg ) );
268                      break;
269            case 'm': tr_bencDictAddBool( &settings, TR_PREFS_KEY_PORT_FORWARDING, TRUE );
270                      break;
271            case 'M': tr_bencDictAddBool( &settings, TR_PREFS_KEY_PORT_FORWARDING, FALSE );
272                      break;
273            case 'L': tr_bencDictAddInt( &settings, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, atoi( optarg ) );
274                      break;
275            case 'l': tr_bencDictAddInt( &settings, TR_PREFS_KEY_PEER_LIMIT_TORRENT, atoi( optarg ) );
276                      break;
277            case 800: paused = TRUE;
278                      break;
279            case 910: tr_bencDictAddInt( &settings, TR_PREFS_KEY_ENCRYPTION, TR_ENCRYPTION_REQUIRED );
280                      break;
281            case 911: tr_bencDictAddInt( &settings, TR_PREFS_KEY_ENCRYPTION, TR_ENCRYPTION_PREFERRED );
282                      break;
283            case 912: tr_bencDictAddInt( &settings, TR_PREFS_KEY_ENCRYPTION, TR_CLEAR_PREFERRED );
284                      break;
285            case 'i':
286                      tr_bencDictAddStr( &settings, TR_PREFS_KEY_BIND_ADDRESS_IPV4, optarg );
287                      break;
288            case 'I':
289                      tr_bencDictAddStr( &settings, TR_PREFS_KEY_BIND_ADDRESS_IPV6, optarg );
290                      break;
291            case 'r':
292                      tr_bencDictAddStr( &settings, TR_PREFS_KEY_RPC_BIND_ADDRESS, optarg );
293                      break;
294            default:  showUsage( );
295                      break;
296        }
297    }
298
299    if( dumpSettings )
300    {
301        char * str = tr_bencToStr( &settings, TR_FMT_JSON, NULL );
302        fprintf( stderr, "%s", str );
303        tr_free( str );
304        return 0;
305    }
306
307    if( !foreground && tr_daemon( TRUE, FALSE ) < 0 )
308    {
309        fprintf( stderr, "failed to daemonize: %s\n", strerror( errno ) );
310        exit( 1 );
311    }
312
313    /* start the session */
314    mySession = tr_sessionInit( "daemon", configDir, FALSE, &settings );
315
316    if( tr_bencDictFindBool( &settings, TR_PREFS_KEY_RPC_AUTH_REQUIRED, &boolVal ) && boolVal )
317        tr_ninf( MY_NAME, "requiring authentication" );
318
319    /* maybe add a watchdir */
320    {
321        const char * dir;
322
323        if( tr_bencDictFindBool( &settings, PREF_KEY_DIR_WATCH_ENABLED, &boolVal )
324            && boolVal
325            && tr_bencDictFindStr( &settings, PREF_KEY_DIR_WATCH, &dir )
326            && dir
327            && *dir )
328        {
329            tr_inf( "Watching \"%s\" for new .torrent files", dir );
330            watchdir = dtr_watchdir_new( mySession, dir, onFileAdded );
331        }
332    }
333
334    /* load the torrents */
335    {
336        tr_torrent ** torrents;
337        tr_ctor * ctor = tr_ctorNew( mySession );
338        if( paused )
339            tr_ctorSetPaused( ctor, TR_FORCE, TRUE );
340        torrents = tr_sessionLoadTorrents( mySession, ctor, NULL );
341        tr_free( torrents );
342        tr_ctorFree( ctor );
343    }
344
345    while( !closing )
346    {
347        tr_wait( 1000 ); /* sleep one second */
348        dtr_watchdir_update( watchdir );
349    }
350
351    /* shutdown */
352    printf( "Closing transmission session..." );
353    tr_sessionSaveSettings( mySession, configDir, &settings );
354    dtr_watchdir_free( watchdir );
355    tr_sessionClose( mySession );
356    printf( " done.\n" );
357
358    /* cleanup */
359    tr_bencFree( &settings );
360    return 0;
361}
Note: See TracBrowser for help on using the repository browser.