Revert "transmission: update from 2.13 to 2.22"
[tomato.git] / release / src / router / transmission / daemon / daemon.c
blob09a6d255f628f82b14ad9ac07f77a7b66a1d626b
1 /*
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 $
13 #include <errno.h>
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 */
22 #include <signal.h>
23 #ifdef HAVE_SYSLOG
24 #include <syslog.h>
25 #endif
26 #include <unistd.h> /* daemon */
28 #include <event.h>
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>
36 #include "watch.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"
44 #define MEM_K 1024
45 #define MEM_K_STR "KiB"
46 #define MEM_M_STR "MiB"
47 #define MEM_G_STR "GiB"
48 #define MEM_T_STR "TiB"
50 #define DISK_K 1024
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"
57 #define SPEED_K 1024
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;
68 /***
69 **** Config File
70 ***/
72 static const char *
73 getUsage( void )
75 return "Transmission " LONG_VERSION_STRING
76 " http://www.transmissionbt.com/\n"
77 "A fast and easy BitTorrent client\n"
78 "\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"
82 "\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 }
132 static void
133 showUsage( void )
135 tr_getopt_usage( MY_NAME, getUsage( ), options );
136 exit( 0 );
139 static void
140 gotsig( int sig )
142 switch( sig )
144 case SIGHUP:
146 tr_benc settings;
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 );
155 break;
158 default:
159 closing = TRUE;
160 break;
164 #if defined(WIN32)
165 #define USE_NO_DAEMON
166 #elif !defined(HAVE_DAEMON) || defined(__UCLIBC__)
167 #define USE_TR_DAEMON
168 #else
169 #define USE_OS_DAEMON
170 #endif
172 static int
173 tr_daemon( int nochdir, int noclose )
175 #if defined(USE_OS_DAEMON)
176 return daemon( nochdir, noclose );
177 #elif defined(USE_TR_DAEMON)
178 pid_t pid = fork( );
179 if( pid < 0 )
180 return -1;
181 else if( pid > 0 )
182 _exit( 0 );
183 else {
184 pid = setsid( );
185 if( pid < 0 )
186 return -1;
188 pid = fork( );
189 if( pid < 0 )
190 return -1;
191 else if( pid > 0 )
192 _exit( 0 );
193 else {
195 if( !nochdir )
196 if( chdir( "/" ) < 0 )
197 return -1;
199 umask( (mode_t)0 );
201 if( !noclose ) {
202 /* send stdin, stdout, and stderr to /dev/null */
203 int i;
204 int fd = open( "/dev/null", O_RDWR, 0 );
205 if( fd < 0 )
206 fprintf( stderr, "unable to open /dev/null: %s\n", tr_strerror(errno) );
207 for( i=0; i<3; ++i ) {
208 if( close( i ) )
209 return -1;
210 dup2( fd, i );
212 close( fd );
215 return 0;
218 #else /* USE_NO_DAEMON */
219 return 0;
220 #endif
223 static const char*
224 getConfigDir( int argc, const char ** argv )
226 int c;
227 const char * configDir = NULL;
228 const char * optarg;
229 const int ind = tr_optind;
231 while(( c = tr_getopt( getUsage( ), argc, argv, options, &optarg ))) {
232 if( c == 'g' ) {
233 configDir = optarg;
234 break;
238 tr_optind = ind;
240 if( configDir == NULL )
241 configDir = tr_getDefaultConfigDir( MY_NAME );
243 return configDir;
246 static void
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 );
253 if( !err )
255 tr_torrentNew( ctor, &err );
257 if( err == TR_PARSE_ERR )
258 tr_err( "Error parsing .torrent file \"%s\"", file );
259 else
261 tr_bool trash = FALSE;
262 int test = tr_ctorGetDeleteSource( ctor, &trash );
264 tr_inf( "Parsing .torrent file successful \"%s\"", file );
266 if( !test && trash )
268 tr_inf( "Deleting input .torrent file \"%s\"", file );
269 if( remove( filename ) )
270 tr_err( "Error deleting .torrent file: %s", tr_strerror( errno ) );
272 else
274 char * new_filename = tr_strdup_printf( "%s.added", filename );
275 rename( filename, new_filename );
276 tr_free( new_filename );
281 tr_ctorFree( ctor );
282 tr_free( filename );
285 static void
286 printMessage( FILE * logfile, int level, const char * name, const char * message, const char * file, int line )
288 if( logfile != NULL )
290 char timestr[64];
291 tr_getLogTimeStr( timestr, sizeof( timestr ) );
292 if( name )
293 fprintf( logfile, "[%s] %s %s (%s:%d)\n", timestr, name, message, file, line );
294 else
295 fprintf( logfile, "[%s] %s (%s:%d)\n", timestr, message, file, line );
297 #ifdef HAVE_SYSLOG
298 else /* daemon... write to syslog */
300 int priority;
302 /* figure out the syslog priority */
303 switch( level ) {
304 case TR_MSG_ERR: priority = LOG_ERR; break;
305 case TR_MSG_DBG: priority = LOG_DEBUG; break;
306 default: priority = LOG_INFO; break;
309 if( name )
310 syslog( priority, "%s %s (%s:%d)", name, message, file, line );
311 else
312 syslog( priority, "%s (%s:%d)", message, file, line );
314 #endif
317 static void
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 )
332 int c;
333 const char * optarg;
334 tr_benc settings;
335 tr_bool boolVal;
336 tr_bool loaded;
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 );
347 #ifndef WIN32
348 signal( SIGHUP, gotsig );
349 #endif
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 */
358 tr_optind = 1;
359 while(( c = tr_getopt( getUsage(), argc, (const char**)argv, options, &optarg ))) {
360 switch( c ) {
361 case 'a': tr_bencDictAddStr( &settings, TR_PREFS_KEY_RPC_WHITELIST, optarg );
362 tr_bencDictAddBool( &settings, TR_PREFS_KEY_RPC_WHITELIST_ENABLED, TRUE );
363 break;
364 case 'b': tr_bencDictAddBool( &settings, TR_PREFS_KEY_BLOCKLIST_ENABLED, TRUE );
365 break;
366 case 'B': tr_bencDictAddBool( &settings, TR_PREFS_KEY_BLOCKLIST_ENABLED, FALSE );
367 break;
368 case 'c': tr_bencDictAddStr( &settings, PREF_KEY_DIR_WATCH, optarg );
369 tr_bencDictAddBool( &settings, PREF_KEY_DIR_WATCH_ENABLED, TRUE );
370 break;
371 case 'C': tr_bencDictAddBool( &settings, PREF_KEY_DIR_WATCH_ENABLED, FALSE );
372 break;
373 case 941: tr_bencDictAddStr( &settings, TR_PREFS_KEY_INCOMPLETE_DIR, optarg );
374 tr_bencDictAddBool( &settings, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, TRUE );
375 break;
376 case 942: tr_bencDictAddBool( &settings, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, FALSE );
377 break;
378 case 'd': dumpSettings = TRUE;
379 break;
380 case 'e': logfile = fopen( optarg, "a+" );
381 if( logfile == NULL )
382 fprintf( stderr, "Couldn't open \"%s\": %s\n", optarg, tr_strerror( errno ) );
383 break;
384 case 'f': foreground = TRUE;
385 break;
386 case 'g': /* handled above */
387 break;
388 case 'V': /* version */
389 fprintf(stderr, "%s %s\n", MY_NAME, LONG_VERSION_STRING);
390 exit( 0 );
391 case 'o': tr_bencDictAddBool( &settings, TR_PREFS_KEY_DHT_ENABLED, TRUE );
392 break;
393 case 'O': tr_bencDictAddBool( &settings, TR_PREFS_KEY_DHT_ENABLED, FALSE );
394 break;
395 case 'p': tr_bencDictAddInt( &settings, TR_PREFS_KEY_RPC_PORT, atoi( optarg ) );
396 break;
397 case 't': tr_bencDictAddBool( &settings, TR_PREFS_KEY_RPC_AUTH_REQUIRED, TRUE );
398 break;
399 case 'T': tr_bencDictAddBool( &settings, TR_PREFS_KEY_RPC_AUTH_REQUIRED, FALSE );
400 break;
401 case 'u': tr_bencDictAddStr( &settings, TR_PREFS_KEY_RPC_USERNAME, optarg );
402 break;
403 case 'v': tr_bencDictAddStr( &settings, TR_PREFS_KEY_RPC_PASSWORD, optarg );
404 break;
405 case 'w': tr_bencDictAddStr( &settings, TR_PREFS_KEY_DOWNLOAD_DIR, optarg );
406 break;
407 case 'P': tr_bencDictAddInt( &settings, TR_PREFS_KEY_PEER_PORT, atoi( optarg ) );
408 break;
409 case 'm': tr_bencDictAddBool( &settings, TR_PREFS_KEY_PORT_FORWARDING, TRUE );
410 break;
411 case 'M': tr_bencDictAddBool( &settings, TR_PREFS_KEY_PORT_FORWARDING, FALSE );
412 break;
413 case 'L': tr_bencDictAddInt( &settings, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, atoi( optarg ) );
414 break;
415 case 'l': tr_bencDictAddInt( &settings, TR_PREFS_KEY_PEER_LIMIT_TORRENT, atoi( optarg ) );
416 break;
417 case 800: paused = TRUE;
418 break;
419 case 910: tr_bencDictAddInt( &settings, TR_PREFS_KEY_ENCRYPTION, TR_ENCRYPTION_REQUIRED );
420 break;
421 case 911: tr_bencDictAddInt( &settings, TR_PREFS_KEY_ENCRYPTION, TR_ENCRYPTION_PREFERRED );
422 break;
423 case 912: tr_bencDictAddInt( &settings, TR_PREFS_KEY_ENCRYPTION, TR_CLEAR_PREFERRED );
424 break;
425 case 'i': tr_bencDictAddStr( &settings, TR_PREFS_KEY_BIND_ADDRESS_IPV4, optarg );
426 break;
427 case 'I': tr_bencDictAddStr( &settings, TR_PREFS_KEY_BIND_ADDRESS_IPV6, optarg );
428 break;
429 case 'r': tr_bencDictAddStr( &settings, TR_PREFS_KEY_RPC_BIND_ADDRESS, optarg );
430 break;
431 case 953: tr_bencDictAddReal( &settings, TR_PREFS_KEY_RATIO, atof(optarg) );
432 tr_bencDictAddBool( &settings, TR_PREFS_KEY_RATIO_ENABLED, TRUE );
433 break;
434 case 954: tr_bencDictAddBool( &settings, TR_PREFS_KEY_RATIO_ENABLED, FALSE );
435 break;
436 case 'x': tr_bencDictAddStr( &settings, PREF_KEY_PIDFILE, optarg );
437 break;
438 case 'y': tr_bencDictAddBool( &settings, TR_PREFS_KEY_LPD_ENABLED, TRUE );
439 break;
440 case 'Y': tr_bencDictAddBool( &settings, TR_PREFS_KEY_LPD_ENABLED, FALSE );
441 break;
442 case 810: tr_bencDictAddInt( &settings, TR_PREFS_KEY_MSGLEVEL, TR_MSG_ERR );
443 break;
444 case 811: tr_bencDictAddInt( &settings, TR_PREFS_KEY_MSGLEVEL, TR_MSG_INF );
445 break;
446 case 812: tr_bencDictAddInt( &settings, TR_PREFS_KEY_MSGLEVEL, TR_MSG_DBG );
447 break;
448 default: showUsage( );
449 break;
453 if( foreground && !logfile )
454 logfile = stderr;
456 if( !loaded )
458 printMessage( logfile, TR_MSG_ERR, MY_NAME, "Error loading config file -- exiting.", __FILE__, __LINE__ );
459 return -1;
462 if( dumpSettings )
464 char * str = tr_bencToStr( &settings, TR_FMT_JSON, NULL );
465 fprintf( stderr, "%s", str );
466 tr_free( str );
467 return 0;
470 if( !foreground && tr_daemon( TRUE, FALSE ) < 0 )
472 char buf[256];
473 tr_snprintf( buf, sizeof( buf ), "Failed to daemonize: %s", tr_strerror( errno ) );
474 printMessage( logfile, TR_MSG_ERR, MY_NAME, buf, __FILE__, __LINE__ );
475 exit( 1 );
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 );
486 pid_filename = NULL;
487 tr_bencDictFindStr( &settings, PREF_KEY_PIDFILE, &pid_filename );
488 if( pid_filename && *pid_filename )
490 FILE * fp = fopen( pid_filename, "w+" );
491 if( fp != NULL )
493 fprintf( fp, "%d", (int)getpid() );
494 fclose( fp );
495 tr_inf( "Saved pidfile \"%s\"", pid_filename );
496 pidfile_created = TRUE;
498 else
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 */
507 const char * dir;
509 if( tr_bencDictFindBool( &settings, PREF_KEY_DIR_WATCH_ENABLED, &boolVal )
510 && boolVal
511 && tr_bencDictFindStr( &settings, PREF_KEY_DIR_WATCH, &dir )
512 && dir
513 && *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 );
524 if( paused )
525 tr_ctorSetPaused( ctor, TR_FORCE, TRUE );
526 torrents = tr_sessionLoadTorrents( mySession, ctor, NULL );
527 tr_free( torrents );
528 tr_ctorFree( ctor );
531 #ifdef HAVE_SYSLOG
532 if( !foreground )
533 openlog( MY_NAME, LOG_CONS|LOG_PID, LOG_DAEMON );
534 #endif
536 while( !closing ) {
537 tr_wait_msec( 1000 ); /* sleep one second */
538 dtr_watchdir_update( watchdir );
539 pumpLogMessages( logfile );
542 /* shutdown */
543 #if HAVE_SYSLOG
544 if( !foreground )
546 syslog( LOG_INFO, "%s", "Closing session" );
547 closelog( );
549 #endif
551 printf( "Closing transmission session..." );
552 tr_sessionSaveSettings( mySession, configDir, &settings );
553 dtr_watchdir_free( watchdir );
554 tr_sessionClose( mySession );
555 printf( " done.\n" );
557 /* cleanup */
558 if( pidfile_created )
559 remove( pid_filename );
560 tr_bencFree( &settings );
561 return 0;