source: trunk/daemon/daemon.c @ 7979

Last change on this file since 7979 was 7979, checked in by charles, 13 years ago

(trunk daemon) #1882: take KyleK's suggestion of using inotify when possible. As he writes, "the opendir() approach will basically prevent my NAS drives to go to standby."

  • Property svn:keywords set to Date Rev Author Id
File size: 11.2 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 7979 2009-03-01 15:38:58Z 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
41
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    { 'P', "peerport", "Port for incoming peers (Default: " TR_DEFAULT_PEER_PORT_STR ")", "P", 1, "<port>" },
80    { 'm', "portmap", "Enable portmapping via NAT-PMP or UPnP", "m", 0, NULL },
81    { 'M', "no-portmap", "Disable portmapping", "M", 0, NULL },
82    { 'L', "peerlimit-global", "Maximum overall number of peers (Default: " TR_DEFAULT_PEER_LIMIT_GLOBAL_STR ")", "L", 1, "<limit>" },
83    { 'l', "peerlimit-torrent", "Maximum number of peers per torrent (Default: " TR_DEFAULT_PEER_LIMIT_TORRENT_STR ")", "l", 1, "<limit>" },
84    { 910, "encryption-required",  "Encrypt all peer connections", "er", 0, NULL },
85    { 911, "encryption-preferred", "Prefer encrypted peer connections", "ep", 0, NULL },
86    { 912, "encryption-tolerated", "Prefer unencrypted peer connections", "et", 0, NULL },
87    { 0, NULL, NULL, NULL, 0, NULL }
88};
89
90static void
91showUsage( void )
92{
93    tr_getopt_usage( MY_NAME, getUsage( ), options );
94    exit( 0 );
95}
96
97static void
98gotsig( int sig UNUSED )
99{
100    closing = TRUE;
101}
102
103#if defined(WIN32)
104 #define USE_NO_DAEMON
105#elif !defined(HAVE_DAEMON) || defined(__UCLIBC__)
106 #define USE_TR_DAEMON
107#else
108 #define USE_OS_DAEMON
109#endif
110
111static int
112tr_daemon( int nochdir, int noclose )
113{
114#if defined(USE_OS_DAEMON)
115    return daemon( nochdir, noclose );
116#elif defined(USE_TR_DAEMON)
117    pid_t pid = fork( );
118    if( pid < 0 )
119        return -1;
120    else if( pid > 0 )
121        _exit( 0 );
122    else {
123        pid = setsid( );
124        if( pid < 0 )
125            return -1;
126
127        pid = fork( );
128        if( pid < 0 )
129            return -1;
130        else if( pid > 0 )
131            _exit( 0 );
132        else {
133
134            if( !nochdir )
135                if( chdir( "/" ) < 0 )
136                    return -1;
137
138            umask( (mode_t)0 );
139
140            if( !noclose ) {
141                /* send stdin, stdout, and stderr to /dev/null */
142                int i;
143                int fd = open( "/dev/null", O_RDWR, 0 );
144                for( i=0; i<3; ++i ) {
145                    if( close( i ) )
146                        return -1;
147                    dup2( fd, i );
148                }
149                close( fd );
150            }
151
152            return 0;
153        }
154    }
155#else /* USE_NO_DAEMON */
156    return 0;
157#endif
158}
159
160static const char*
161getConfigDir( int argc, const char ** argv )
162{
163    int c;
164    const char * configDir = NULL;
165    const char * optarg;
166    const int ind = tr_optind;
167
168    while(( c = tr_getopt( getUsage( ), argc, argv, options, &optarg ))) {
169        if( c == 'g' ) {
170            configDir = optarg;
171            break;
172        }
173    }
174
175    tr_optind = ind;
176
177    if( configDir == NULL )
178        configDir = tr_getDefaultConfigDir( MY_NAME );
179
180    return configDir;
181}
182
183static void
184onFileAdded( tr_session * session, const char * dir, const char * file )
185{
186    if( strstr( file, ".torrent" ) != NULL )
187    {
188        char * filename = tr_buildPath( dir, file, NULL );
189        tr_ctor * ctor = tr_ctorNew( session );
190
191        int err = tr_ctorSetMetainfoFromFile( ctor, filename );
192        if( !err )
193            tr_torrentNew( session, ctor, &err );
194
195        tr_ctorFree( ctor );
196        tr_free( filename );
197    }
198}
199
200int
201main( int argc, char ** argv )
202{
203    int c;
204    int64_t i;
205    const char * optarg;
206    tr_benc settings;
207    tr_bool foreground = FALSE;
208    tr_bool dumpSettings = FALSE;
209    const char * configDir = NULL;
210    dtr_watchdir * watchdir = NULL;
211
212    signal( SIGINT, gotsig );
213    signal( SIGTERM, gotsig );
214#ifndef WIN32
215    signal( SIGQUIT, gotsig );
216    signal( SIGPIPE, SIG_IGN );
217    signal( SIGHUP, SIG_IGN );
218#endif
219
220    /* load settings from defaults + config file */
221    tr_bencInitDict( &settings, 0 );
222    configDir = getConfigDir( argc, (const char**)argv );
223    tr_sessionLoadSettings( &settings, configDir, MY_NAME );
224    tr_bencDictAddInt( &settings, TR_PREFS_KEY_RPC_ENABLED, 1 );
225
226    /* overwrite settings from the comamndline */
227    tr_optind = 1;
228    while(( c = tr_getopt( getUsage(), argc, (const char**)argv, options, &optarg ))) {
229        switch( c ) {
230            case 'a': tr_bencDictAddStr( &settings, TR_PREFS_KEY_RPC_WHITELIST, optarg );
231                      tr_bencDictAddInt( &settings, TR_PREFS_KEY_RPC_WHITELIST_ENABLED, 1 );
232                      break;
233            case 'b': tr_bencDictAddInt( &settings, TR_PREFS_KEY_BLOCKLIST_ENABLED, 1 );
234                      break;
235            case 'B': tr_bencDictAddInt( &settings, TR_PREFS_KEY_BLOCKLIST_ENABLED, 0 );
236                      break;
237            case 'c': tr_bencDictAddStr( &settings, PREF_KEY_DIR_WATCH, optarg );
238                      tr_bencDictAddInt( &settings, PREF_KEY_DIR_WATCH_ENABLED, 1 );
239                      break;
240            case 'C': tr_bencDictAddInt( &settings, PREF_KEY_DIR_WATCH_ENABLED, 0 );
241                      break;
242            case 'd': dumpSettings = TRUE;
243                      break;
244            case 'f': foreground = TRUE;
245                      break;
246            case 'g': /* handled above */
247                      break;
248            case 'V': /* version */
249                      fprintf(stderr, "Transmission %s\n", LONG_VERSION_STRING);
250                      exit( 0 );
251            case 'p': tr_bencDictAddInt( &settings, TR_PREFS_KEY_RPC_PORT, atoi( optarg ) );
252                      break;
253            case 't': tr_bencDictAddInt( &settings, TR_PREFS_KEY_RPC_AUTH_REQUIRED, 1 );
254                      break;
255            case 'T': tr_bencDictAddInt( &settings, TR_PREFS_KEY_RPC_AUTH_REQUIRED, 0 );
256                      break;
257            case 'u': tr_bencDictAddStr( &settings, TR_PREFS_KEY_RPC_USERNAME, optarg );
258                      break;
259            case 'v': tr_bencDictAddStr( &settings, TR_PREFS_KEY_RPC_PASSWORD, optarg );
260                      break;
261            case 'w': tr_bencDictAddStr( &settings, TR_PREFS_KEY_DOWNLOAD_DIR, optarg );
262                      break;
263            case 'P': tr_bencDictAddInt( &settings, TR_PREFS_KEY_PEER_PORT, atoi( optarg ) );
264                      break;
265            case 'm': tr_bencDictAddInt( &settings, TR_PREFS_KEY_PORT_FORWARDING, 1 );
266                      break;
267            case 'M': tr_bencDictAddInt( &settings, TR_PREFS_KEY_PORT_FORWARDING, 0 );
268                      break;
269            case 'L': tr_bencDictAddInt( &settings, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, atoi( optarg ) );
270                      break;
271            case 'l': tr_bencDictAddInt( &settings, TR_PREFS_KEY_PEER_LIMIT_TORRENT, atoi( optarg ) );
272                      break;
273            case 910: tr_bencDictAddInt( &settings, TR_PREFS_KEY_ENCRYPTION, TR_ENCRYPTION_REQUIRED );
274                      break;
275            case 911: tr_bencDictAddInt( &settings, TR_PREFS_KEY_ENCRYPTION, TR_ENCRYPTION_PREFERRED );
276                      break;
277            case 912: tr_bencDictAddInt( &settings, TR_PREFS_KEY_ENCRYPTION, TR_CLEAR_PREFERRED );
278                      break;
279            default:  showUsage( );
280                      break;
281        }
282    }
283
284    if( dumpSettings )
285    {
286        struct evbuffer * buf = tr_getBuffer( );
287
288        tr_bencSaveAsJSON( &settings, buf );
289        fprintf( stderr, "%s", (char*)EVBUFFER_DATA(buf) );
290
291        tr_releaseBuffer( buf );
292        return 0;
293    }
294
295    if( !foreground && tr_daemon( TRUE, FALSE ) < 0 )
296    {
297        fprintf( stderr, "failed to daemonize: %s\n", strerror( errno ) );
298        exit( 1 );
299    }
300
301    /* start the session */
302    mySession = tr_sessionInit( "daemon", configDir, FALSE, &settings );
303
304    if( tr_bencDictFindInt( &settings, TR_PREFS_KEY_RPC_AUTH_REQUIRED, &i ) && i!=0 )
305        tr_ninf( MY_NAME, "requiring authentication" );
306
307    /* maybe add a watchdir */
308    {
309        int64_t doWatch;
310        const char * dir;
311
312        if( tr_bencDictFindInt( &settings, PREF_KEY_DIR_WATCH_ENABLED, &doWatch )
313            && doWatch
314            && tr_bencDictFindStr( &settings, PREF_KEY_DIR_WATCH, &dir )
315            && dir
316            && *dir )
317        {
318            tr_inf( "Watching \"%s\" for new .torrent files", dir );
319            watchdir = dtr_watchdir_new( mySession, dir, onFileAdded );
320        }
321    }
322
323    /* load the torrents */
324    {
325        tr_ctor * ctor = tr_ctorNew( mySession );
326        tr_torrent ** torrents = tr_sessionLoadTorrents( mySession, ctor, NULL );
327        tr_free( torrents );
328        tr_ctorFree( ctor );
329    }
330
331    while( !closing )
332    {
333        tr_wait( 1000 ); /* sleep one second */
334        dtr_watchdir_update( watchdir );
335    }
336
337    /* shutdown */
338    printf( "Closing transmission session..." );
339    tr_sessionSaveSettings( mySession, configDir, &settings );
340    dtr_watchdir_free( watchdir );
341    tr_sessionClose( mySession );
342    printf( " done.\n" );
343
344    /* cleanup */
345    tr_bencFree( &settings );
346    return 0;
347}
Note: See TracBrowser for help on using the repository browser.