2 * This file Copyright (C) 2008-2010 Mnemosyne LLC
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2
6 * as published by the Free Software Foundation.
8 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
10 * $Id: daemon.c 11254 2010-09-22 22:19:21Z charles $
14 #include <stdio.h> /* printf */
15 #include <stdlib.h> /* exit, atoi */
16 #include <string.h> /* strcmp */
18 #include <sys/types.h> /* umask*/
19 #include <sys/stat.h> /* umask*/
21 #include <fcntl.h> /* open */
26 #include <unistd.h> /* daemon */
30 #include <libtransmission/transmission.h>
31 #include <libtransmission/bencode.h>
32 #include <libtransmission/tr-getopt.h>
33 #include <libtransmission/utils.h>
34 #include <libtransmission/version.h>
38 #define MY_NAME "transmission-daemon"
40 #define PREF_KEY_DIR_WATCH "watch-dir"
41 #define PREF_KEY_DIR_WATCH_ENABLED "watch-dir-enabled"
42 #define PREF_KEY_PIDFILE "pidfile"
45 #define MEM_K_STR "KiB"
46 #define MEM_M_STR "MiB"
47 #define MEM_G_STR "GiB"
48 #define MEM_T_STR "TiB"
51 #define DISK_B_STR "B"
52 #define DISK_K_STR "KiB"
53 #define DISK_M_STR "MiB"
54 #define DISK_G_STR "GiB"
55 #define DISK_T_STR "TiB"
58 #define SPEED_B_STR "B/s"
59 #define SPEED_K_STR "KiB/s"
60 #define SPEED_M_STR "MiB/s"
61 #define SPEED_G_STR "GiB/s"
62 #define SPEED_T_STR "TiB/s"
64 static tr_bool paused
= FALSE
;
65 static tr_bool closing
= FALSE
;
66 static tr_session
* mySession
= NULL
;
75 return "Transmission " LONG_VERSION_STRING
76 " http://www.transmissionbt.com/\n"
77 "A fast and easy BitTorrent client\n"
79 MY_NAME
" is a headless Transmission session\n"
80 "that can be controlled via transmission-remote\n"
81 "or the web interface.\n"
83 "Usage: " MY_NAME
" [options]";
86 static const struct tr_option options
[] =
89 { 'a', "allowed", "Allowed IP addresses. (Default: " TR_DEFAULT_RPC_WHITELIST
")", "a", 1, "<list>" },
90 { 'b', "blocklist", "Enable peer blocklists", "b", 0, NULL
},
91 { 'B', "no-blocklist", "Disable peer blocklists", "B", 0, NULL
},
92 { 'c', "watch-dir", "Where to watch for new .torrent files", "c", 1, "<directory>" },
93 { 'C', "no-watch-dir", "Disable the watch-dir", "C", 0, NULL
},
94 { 941, "incomplete-dir", "Where to store new torrents until they're complete", NULL
, 1, "<directory>" },
95 { 942, "no-incomplete-dir", "Don't store incomplete torrents in a different location", NULL
, 0, NULL
},
96 { 'd', "dump-settings", "Dump the settings and exit", "d", 0, NULL
},
97 { 'e', "logfile", "Dump the log messages to this filename", "e", 1, "<filename>" },
98 { 'f', "foreground", "Run in the foreground instead of daemonizing", "f", 0, NULL
},
99 { 'g', "config-dir", "Where to look for configuration files", "g", 1, "<path>" },
100 { 'p', "port", "RPC port (Default: " TR_DEFAULT_RPC_PORT_STR
")", "p", 1, "<port>" },
101 { 't', "auth", "Require authentication", "t", 0, NULL
},
102 { 'T', "no-auth", "Don't require authentication", "T", 0, NULL
},
103 { 'u', "username", "Set username for authentication", "u", 1, "<username>" },
104 { 'v', "password", "Set password for authentication", "v", 1, "<password>" },
105 { 'V', "version", "Show version number and exit", "V", 0, NULL
},
106 { 810, "log-error", "Show error messages", NULL
, 0, NULL
},
107 { 811, "log-info", "Show error and info messages", NULL
, 0, NULL
},
108 { 812, "log-debug", "Show error, info, and debug messages", NULL
, 0, NULL
},
109 { 'w', "download-dir", "Where to save downloaded data", "w", 1, "<path>" },
110 { 800, "paused", "Pause all torrents on startup", NULL
, 0, NULL
},
111 { 'o', "dht", "Enable distributed hash tables (DHT)", "o", 0, NULL
},
112 { 'O', "no-dht", "Disable distributed hash tables (DHT)", "O", 0, NULL
},
113 { 'y', "lpd", "Enable local peer discovery (LPD)", "y", 0, NULL
},
114 { 'Y', "no-lpd", "Disable local peer discovery (LPD)", "Y", 0, NULL
},
115 { 'P', "peerport", "Port for incoming peers (Default: " TR_DEFAULT_PEER_PORT_STR
")", "P", 1, "<port>" },
116 { 'm', "portmap", "Enable portmapping via NAT-PMP or UPnP", "m", 0, NULL
},
117 { 'M', "no-portmap", "Disable portmapping", "M", 0, NULL
},
118 { 'L', "peerlimit-global", "Maximum overall number of peers (Default: " TR_DEFAULT_PEER_LIMIT_GLOBAL_STR
")", "L", 1, "<limit>" },
119 { 'l', "peerlimit-torrent", "Maximum number of peers per torrent (Default: " TR_DEFAULT_PEER_LIMIT_TORRENT_STR
")", "l", 1, "<limit>" },
120 { 910, "encryption-required", "Encrypt all peer connections", "er", 0, NULL
},
121 { 911, "encryption-preferred", "Prefer encrypted peer connections", "ep", 0, NULL
},
122 { 912, "encryption-tolerated", "Prefer unencrypted peer connections", "et", 0, NULL
},
123 { 'i', "bind-address-ipv4", "Where to listen for peer connections", "i", 1, "<ipv4 addr>" },
124 { 'I', "bind-address-ipv6", "Where to listen for peer connections", "I", 1, "<ipv6 addr>" },
125 { 'r', "rpc-bind-address", "Where to listen for RPC connections", "r", 1, "<ipv4 addr>" },
126 { 953, "global-seedratio", "All torrents, unless overridden by a per-torrent setting, should seed until a specific ratio", "gsr", 1, "ratio" },
127 { 954, "no-global-seedratio", "All torrents, unless overridden by a per-torrent setting, should seed regardless of ratio", "GSR", 0, NULL
},
128 { 'x', "pid-file", "Enable PID file", "x", 1, "<pid-file>" },
129 { 0, NULL
, NULL
, NULL
, 0, NULL
}
135 tr_getopt_usage( MY_NAME
, getUsage( ), options
);
147 const char * configDir
= tr_sessionGetConfigDir( mySession
);
148 tr_inf( "Reloading settings from \"%s\"", configDir
);
149 tr_bencInitDict( &settings
, 0 );
150 tr_bencDictAddBool( &settings
, TR_PREFS_KEY_RPC_ENABLED
, TRUE
);
151 tr_sessionLoadSettings( &settings
, configDir
, MY_NAME
);
152 tr_sessionSet( mySession
, &settings
);
153 tr_bencFree( &settings
);
154 tr_sessionReloadBlocklists( mySession
);
165 #define USE_NO_DAEMON
166 #elif !defined(HAVE_DAEMON) || defined(__UCLIBC__)
167 #define USE_TR_DAEMON
169 #define USE_OS_DAEMON
173 tr_daemon( int nochdir
, int noclose
)
175 #if defined(USE_OS_DAEMON)
176 return daemon( nochdir
, noclose
);
177 #elif defined(USE_TR_DAEMON)
196 if( chdir( "/" ) < 0 )
202 /* send stdin, stdout, and stderr to /dev/null */
204 int fd
= open( "/dev/null", O_RDWR
, 0 );
206 fprintf( stderr
, "unable to open /dev/null: %s\n", tr_strerror(errno
) );
207 for( i
=0; i
<3; ++i
) {
218 #else /* USE_NO_DAEMON */
224 getConfigDir( int argc
, const char ** argv
)
227 const char * configDir
= NULL
;
229 const int ind
= tr_optind
;
231 while(( c
= tr_getopt( getUsage( ), argc
, argv
, options
, &optarg
))) {
240 if( configDir
== NULL
)
241 configDir
= tr_getDefaultConfigDir( MY_NAME
);
247 onFileAdded( tr_session
* session
, const char * dir
, const char * file
)
249 char * filename
= tr_buildPath( dir
, file
, NULL
);
250 tr_ctor
* ctor
= tr_ctorNew( session
);
251 int err
= tr_ctorSetMetainfoFromFile( ctor
, filename
);
255 tr_torrentNew( ctor
, &err
);
257 if( err
== TR_PARSE_ERR
)
258 tr_err( "Error parsing .torrent file \"%s\"", file
);
261 tr_bool trash
= FALSE
;
262 int test
= tr_ctorGetDeleteSource( ctor
, &trash
);
264 tr_inf( "Parsing .torrent file successful \"%s\"", file
);
268 tr_inf( "Deleting input .torrent file \"%s\"", file
);
269 if( remove( filename
) )
270 tr_err( "Error deleting .torrent file: %s", tr_strerror( errno
) );
274 char * new_filename
= tr_strdup_printf( "%s.added", filename
);
275 rename( filename
, new_filename
);
276 tr_free( new_filename
);
286 printMessage( FILE * logfile
, int level
, const char * name
, const char * message
, const char * file
, int line
)
288 if( logfile
!= NULL
)
291 tr_getLogTimeStr( timestr
, sizeof( timestr
) );
293 fprintf( logfile
, "[%s] %s %s (%s:%d)\n", timestr
, name
, message
, file
, line
);
295 fprintf( logfile
, "[%s] %s (%s:%d)\n", timestr
, message
, file
, line
);
298 else /* daemon... write to syslog */
302 /* figure out the syslog priority */
304 case TR_MSG_ERR
: priority
= LOG_ERR
; break;
305 case TR_MSG_DBG
: priority
= LOG_DEBUG
; break;
306 default: priority
= LOG_INFO
; break;
310 syslog( priority
, "%s %s (%s:%d)", name
, message
, file
, line
);
312 syslog( priority
, "%s (%s:%d)", message
, file
, line
);
318 pumpLogMessages( FILE * logfile
)
320 const tr_msg_list
* l
;
321 tr_msg_list
* list
= tr_getQueuedMessages( );
323 for( l
=list
; l
!=NULL
; l
=l
->next
)
324 printMessage( logfile
, l
->level
, l
->name
, l
->message
, l
->file
, l
->line
);
326 tr_freeMessageList( list
);
330 main( int argc
, char ** argv
)
337 tr_bool foreground
= FALSE
;
338 tr_bool dumpSettings
= FALSE
;
339 const char * configDir
= NULL
;
340 const char * pid_filename
;
341 dtr_watchdir
* watchdir
= NULL
;
342 FILE * logfile
= NULL
;
343 tr_bool pidfile_created
= FALSE
;
345 signal( SIGINT
, gotsig
);
346 signal( SIGTERM
, gotsig
);
348 signal( SIGHUP
, gotsig
);
351 /* load settings from defaults + config file */
352 tr_bencInitDict( &settings
, 0 );
353 tr_bencDictAddBool( &settings
, TR_PREFS_KEY_RPC_ENABLED
, TRUE
);
354 configDir
= getConfigDir( argc
, (const char**)argv
);
355 loaded
= tr_sessionLoadSettings( &settings
, configDir
, MY_NAME
);
357 /* overwrite settings from the comamndline */
359 while(( c
= tr_getopt( getUsage(), argc
, (const char**)argv
, options
, &optarg
))) {
361 case 'a': tr_bencDictAddStr( &settings
, TR_PREFS_KEY_RPC_WHITELIST
, optarg
);
362 tr_bencDictAddBool( &settings
, TR_PREFS_KEY_RPC_WHITELIST_ENABLED
, TRUE
);
364 case 'b': tr_bencDictAddBool( &settings
, TR_PREFS_KEY_BLOCKLIST_ENABLED
, TRUE
);
366 case 'B': tr_bencDictAddBool( &settings
, TR_PREFS_KEY_BLOCKLIST_ENABLED
, FALSE
);
368 case 'c': tr_bencDictAddStr( &settings
, PREF_KEY_DIR_WATCH
, optarg
);
369 tr_bencDictAddBool( &settings
, PREF_KEY_DIR_WATCH_ENABLED
, TRUE
);
371 case 'C': tr_bencDictAddBool( &settings
, PREF_KEY_DIR_WATCH_ENABLED
, FALSE
);
373 case 941: tr_bencDictAddStr( &settings
, TR_PREFS_KEY_INCOMPLETE_DIR
, optarg
);
374 tr_bencDictAddBool( &settings
, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED
, TRUE
);
376 case 942: tr_bencDictAddBool( &settings
, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED
, FALSE
);
378 case 'd': dumpSettings
= TRUE
;
380 case 'e': logfile
= fopen( optarg
, "a+" );
381 if( logfile
== NULL
)
382 fprintf( stderr
, "Couldn't open \"%s\": %s\n", optarg
, tr_strerror( errno
) );
384 case 'f': foreground
= TRUE
;
386 case 'g': /* handled above */
388 case 'V': /* version */
389 fprintf(stderr
, "%s %s\n", MY_NAME
, LONG_VERSION_STRING
);
391 case 'o': tr_bencDictAddBool( &settings
, TR_PREFS_KEY_DHT_ENABLED
, TRUE
);
393 case 'O': tr_bencDictAddBool( &settings
, TR_PREFS_KEY_DHT_ENABLED
, FALSE
);
395 case 'p': tr_bencDictAddInt( &settings
, TR_PREFS_KEY_RPC_PORT
, atoi( optarg
) );
397 case 't': tr_bencDictAddBool( &settings
, TR_PREFS_KEY_RPC_AUTH_REQUIRED
, TRUE
);
399 case 'T': tr_bencDictAddBool( &settings
, TR_PREFS_KEY_RPC_AUTH_REQUIRED
, FALSE
);
401 case 'u': tr_bencDictAddStr( &settings
, TR_PREFS_KEY_RPC_USERNAME
, optarg
);
403 case 'v': tr_bencDictAddStr( &settings
, TR_PREFS_KEY_RPC_PASSWORD
, optarg
);
405 case 'w': tr_bencDictAddStr( &settings
, TR_PREFS_KEY_DOWNLOAD_DIR
, optarg
);
407 case 'P': tr_bencDictAddInt( &settings
, TR_PREFS_KEY_PEER_PORT
, atoi( optarg
) );
409 case 'm': tr_bencDictAddBool( &settings
, TR_PREFS_KEY_PORT_FORWARDING
, TRUE
);
411 case 'M': tr_bencDictAddBool( &settings
, TR_PREFS_KEY_PORT_FORWARDING
, FALSE
);
413 case 'L': tr_bencDictAddInt( &settings
, TR_PREFS_KEY_PEER_LIMIT_GLOBAL
, atoi( optarg
) );
415 case 'l': tr_bencDictAddInt( &settings
, TR_PREFS_KEY_PEER_LIMIT_TORRENT
, atoi( optarg
) );
417 case 800: paused
= TRUE
;
419 case 910: tr_bencDictAddInt( &settings
, TR_PREFS_KEY_ENCRYPTION
, TR_ENCRYPTION_REQUIRED
);
421 case 911: tr_bencDictAddInt( &settings
, TR_PREFS_KEY_ENCRYPTION
, TR_ENCRYPTION_PREFERRED
);
423 case 912: tr_bencDictAddInt( &settings
, TR_PREFS_KEY_ENCRYPTION
, TR_CLEAR_PREFERRED
);
425 case 'i': tr_bencDictAddStr( &settings
, TR_PREFS_KEY_BIND_ADDRESS_IPV4
, optarg
);
427 case 'I': tr_bencDictAddStr( &settings
, TR_PREFS_KEY_BIND_ADDRESS_IPV6
, optarg
);
429 case 'r': tr_bencDictAddStr( &settings
, TR_PREFS_KEY_RPC_BIND_ADDRESS
, optarg
);
431 case 953: tr_bencDictAddReal( &settings
, TR_PREFS_KEY_RATIO
, atof(optarg
) );
432 tr_bencDictAddBool( &settings
, TR_PREFS_KEY_RATIO_ENABLED
, TRUE
);
434 case 954: tr_bencDictAddBool( &settings
, TR_PREFS_KEY_RATIO_ENABLED
, FALSE
);
436 case 'x': tr_bencDictAddStr( &settings
, PREF_KEY_PIDFILE
, optarg
);
438 case 'y': tr_bencDictAddBool( &settings
, TR_PREFS_KEY_LPD_ENABLED
, TRUE
);
440 case 'Y': tr_bencDictAddBool( &settings
, TR_PREFS_KEY_LPD_ENABLED
, FALSE
);
442 case 810: tr_bencDictAddInt( &settings
, TR_PREFS_KEY_MSGLEVEL
, TR_MSG_ERR
);
444 case 811: tr_bencDictAddInt( &settings
, TR_PREFS_KEY_MSGLEVEL
, TR_MSG_INF
);
446 case 812: tr_bencDictAddInt( &settings
, TR_PREFS_KEY_MSGLEVEL
, TR_MSG_DBG
);
448 default: showUsage( );
453 if( foreground
&& !logfile
)
458 printMessage( logfile
, TR_MSG_ERR
, MY_NAME
, "Error loading config file -- exiting.", __FILE__
, __LINE__
);
464 char * str
= tr_bencToStr( &settings
, TR_FMT_JSON
, NULL
);
465 fprintf( stderr
, "%s", str
);
470 if( !foreground
&& tr_daemon( TRUE
, FALSE
) < 0 )
473 tr_snprintf( buf
, sizeof( buf
), "Failed to daemonize: %s", tr_strerror( errno
) );
474 printMessage( logfile
, TR_MSG_ERR
, MY_NAME
, buf
, __FILE__
, __LINE__
);
478 /* start the session */
479 tr_formatter_mem_init( MEM_K
, MEM_K_STR
, MEM_M_STR
, MEM_G_STR
, MEM_T_STR
);
480 tr_formatter_size_init( DISK_K
, DISK_K_STR
, DISK_M_STR
, DISK_G_STR
, DISK_T_STR
);
481 tr_formatter_speed_init( SPEED_K
, SPEED_K_STR
, SPEED_M_STR
, SPEED_G_STR
, SPEED_T_STR
);
482 mySession
= tr_sessionInit( "daemon", configDir
, TRUE
, &settings
);
483 tr_ninf( NULL
, "Using settings from \"%s\"", configDir
);
484 tr_sessionSaveSettings( mySession
, configDir
, &settings
);
487 tr_bencDictFindStr( &settings
, PREF_KEY_PIDFILE
, &pid_filename
);
488 if( pid_filename
&& *pid_filename
)
490 FILE * fp
= fopen( pid_filename
, "w+" );
493 fprintf( fp
, "%d", (int)getpid() );
495 tr_inf( "Saved pidfile \"%s\"", pid_filename
);
496 pidfile_created
= TRUE
;
499 tr_err( "Unable to save pidfile \"%s\": %s", pid_filename
, strerror( errno
) );
502 if( tr_bencDictFindBool( &settings
, TR_PREFS_KEY_RPC_AUTH_REQUIRED
, &boolVal
) && boolVal
)
503 tr_ninf( MY_NAME
, "requiring authentication" );
505 /* maybe add a watchdir */
509 if( tr_bencDictFindBool( &settings
, PREF_KEY_DIR_WATCH_ENABLED
, &boolVal
)
511 && tr_bencDictFindStr( &settings
, PREF_KEY_DIR_WATCH
, &dir
)
515 tr_inf( "Watching \"%s\" for new .torrent files", dir
);
516 watchdir
= dtr_watchdir_new( mySession
, dir
, onFileAdded
);
520 /* load the torrents */
522 tr_torrent
** torrents
;
523 tr_ctor
* ctor
= tr_ctorNew( mySession
);
525 tr_ctorSetPaused( ctor
, TR_FORCE
, TRUE
);
526 torrents
= tr_sessionLoadTorrents( mySession
, ctor
, NULL
);
533 openlog( MY_NAME
, LOG_CONS
|LOG_PID
, LOG_DAEMON
);
537 tr_wait_msec( 1000 ); /* sleep one second */
538 dtr_watchdir_update( watchdir
);
539 pumpLogMessages( logfile
);
546 syslog( LOG_INFO
, "%s", "Closing session" );
551 printf( "Closing transmission session..." );
552 tr_sessionSaveSettings( mySession
, configDir
, &settings
);
553 dtr_watchdir_free( watchdir
);
554 tr_sessionClose( mySession
);
555 printf( " done.\n" );
558 if( pidfile_created
)
559 remove( pid_filename
);
560 tr_bencFree( &settings
);