1/* 2 * This file Copyright (C) Mnemosyne LLC 3 * 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. 7 * 8 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 9 * 10 * $Id: daemon.c 13330 2012-05-30 17:59:52Z jordan $ 11 */ 12 13#include <errno.h> 14#include <stdio.h> /* printf */ 15#include <stdlib.h> /* exit, atoi */ 16 17#include <fcntl.h> /* open */ 18#include <signal.h> 19#ifdef HAVE_SYSLOG 20#include <syslog.h> 21#endif 22#include <unistd.h> /* daemon */ 23 24#include <event2/buffer.h> 25 26#include <libtransmission/transmission.h> 27#include <libtransmission/bencode.h> 28#include <libtransmission/tr-getopt.h> 29#include <libtransmission/utils.h> 30#include <libtransmission/version.h> 31 32#include "watch.h" 33 34#define MY_NAME "transmission-daemon" 35 36#define PREF_KEY_DIR_WATCH "watch-dir" 37#define PREF_KEY_DIR_WATCH_ENABLED "watch-dir-enabled" 38#define PREF_KEY_PIDFILE "pidfile" 39 40#define MEM_K 1024 41#define MEM_K_STR "KiB" 42#define MEM_M_STR "MiB" 43#define MEM_G_STR "GiB" 44#define MEM_T_STR "TiB" 45 46#define DISK_K 1000 47#define DISK_B_STR "B" 48#define DISK_K_STR "kB" 49#define DISK_M_STR "MB" 50#define DISK_G_STR "GB" 51#define DISK_T_STR "TB" 52 53#define SPEED_K 1000 54#define SPEED_B_STR "B/s" 55#define SPEED_K_STR "kB/s" 56#define SPEED_M_STR "MB/s" 57#define SPEED_G_STR "GB/s" 58#define SPEED_T_STR "TB/s" 59 60#define LOGFILE_MODE_STR "a+" 61 62static bool paused = false; 63static bool closing = false; 64static bool seenHUP = false; 65static const char *logfileName = NULL; 66static FILE *logfile = NULL; 67static tr_session * mySession = NULL; 68 69/*** 70**** Config File 71***/ 72 73static const char * 74getUsage( void ) 75{ 76 return "Transmission " LONG_VERSION_STRING 77 " http://www.transmissionbt.com/\n" 78 "A fast and easy BitTorrent client\n" 79 "\n" 80 MY_NAME " is a headless Transmission session\n" 81 "that can be controlled via transmission-remote\n" 82 "or the web interface.\n" 83 "\n" 84 "Usage: " MY_NAME " [options]"; 85} 86 87static const struct tr_option options[] = 88{ 89 90 { 'a', "allowed", "Allowed IP addresses. (Default: " TR_DEFAULT_RPC_WHITELIST ")", "a", 1, "<list>" }, 91 { 'b', "blocklist", "Enable peer blocklists", "b", 0, NULL }, 92 { 'B', "no-blocklist", "Disable peer blocklists", "B", 0, NULL }, 93 { 'c', "watch-dir", "Where to watch for new .torrent files", "c", 1, "<directory>" }, 94 { 'C', "no-watch-dir", "Disable the watch-dir", "C", 0, NULL }, 95 { 941, "incomplete-dir", "Where to store new torrents until they're complete", NULL, 1, "<directory>" }, 96 { 942, "no-incomplete-dir", "Don't store incomplete torrents in a different location", NULL, 0, NULL }, 97 { 'd', "dump-settings", "Dump the settings and exit", "d", 0, NULL }, 98 { 'e', "logfile", "Dump the log messages to this filename", "e", 1, "<filename>" }, 99 { 'f', "foreground", "Run in the foreground instead of daemonizing", "f", 0, NULL }, 100 { 'g', "config-dir", "Where to look for configuration files", "g", 1, "<path>" }, 101 { 'p', "port", "RPC port (Default: " TR_DEFAULT_RPC_PORT_STR ")", "p", 1, "<port>" }, 102 { 't', "auth", "Require authentication", "t", 0, NULL }, 103 { 'T', "no-auth", "Don't require authentication", "T", 0, NULL }, 104 { 'u', "username", "Set username for authentication", "u", 1, "<username>" }, 105 { 'v', "password", "Set password for authentication", "v", 1, "<password>" }, 106 { 'V', "version", "Show version number and exit", "V", 0, NULL }, 107 { 810, "log-error", "Show error messages", NULL, 0, NULL }, 108 { 811, "log-info", "Show error and info messages", NULL, 0, NULL }, 109 { 812, "log-debug", "Show error, info, and debug messages", NULL, 0, NULL }, 110 { 'w', "download-dir", "Where to save downloaded data", "w", 1, "<path>" }, 111 { 800, "paused", "Pause all torrents on startup", NULL, 0, NULL }, 112 { 'o', "dht", "Enable distributed hash tables (DHT)", "o", 0, NULL }, 113 { 'O', "no-dht", "Disable distributed hash tables (DHT)", "O", 0, NULL }, 114 { 'y', "lpd", "Enable local peer discovery (LPD)", "y", 0, NULL }, 115 { 'Y', "no-lpd", "Disable local peer discovery (LPD)", "Y", 0, NULL }, 116 { 830, "utp", "Enable uTP for peer connections", NULL, 0, NULL }, 117 { 831, "no-utp", "Disable uTP for peer connections", NULL, 0, NULL }, 118 { 'P', "peerport", "Port for incoming peers (Default: " TR_DEFAULT_PEER_PORT_STR ")", "P", 1, "<port>" }, 119 { 'm', "portmap", "Enable portmapping via NAT-PMP or UPnP", "m", 0, NULL }, 120 { 'M', "no-portmap", "Disable portmapping", "M", 0, NULL }, 121 { 'L', "peerlimit-global", "Maximum overall number of peers (Default: " TR_DEFAULT_PEER_LIMIT_GLOBAL_STR ")", "L", 1, "<limit>" }, 122 { 'l', "peerlimit-torrent", "Maximum number of peers per torrent (Default: " TR_DEFAULT_PEER_LIMIT_TORRENT_STR ")", "l", 1, "<limit>" }, 123 { 910, "encryption-required", "Encrypt all peer connections", "er", 0, NULL }, 124 { 911, "encryption-preferred", "Prefer encrypted peer connections", "ep", 0, NULL }, 125 { 912, "encryption-tolerated", "Prefer unencrypted peer connections", "et", 0, NULL }, 126 { 'i', "bind-address-ipv4", "Where to listen for peer connections", "i", 1, "<ipv4 addr>" }, 127 { 'I', "bind-address-ipv6", "Where to listen for peer connections", "I", 1, "<ipv6 addr>" }, 128 { 'r', "rpc-bind-address", "Where to listen for RPC connections", "r", 1, "<ipv4 addr>" }, 129 { 953, "global-seedratio", "All torrents, unless overridden by a per-torrent setting, should seed until a specific ratio", "gsr", 1, "ratio" }, 130 { 954, "no-global-seedratio", "All torrents, unless overridden by a per-torrent setting, should seed regardless of ratio", "GSR", 0, NULL }, 131 { 'x', "pid-file", "Enable PID file", "x", 1, "<pid-file>" }, 132 { 0, NULL, NULL, NULL, 0, NULL } 133}; 134 135static void 136showUsage( void ) 137{ 138 tr_getopt_usage( MY_NAME, getUsage( ), options ); 139 exit( 0 ); 140} 141 142static void 143gotsig( int sig ) 144{ 145 switch( sig ) 146 { 147 case SIGHUP: 148 { 149 if( !mySession ) 150 { 151 tr_inf( "Deferring reload until session is fully started." ); 152 seenHUP = true; 153 } 154 else 155 { 156 tr_benc settings; 157 const char * configDir; 158 159 /* reopen the logfile to allow for log rotation */ 160 if( logfileName ) { 161 logfile = freopen( logfileName, LOGFILE_MODE_STR, logfile ); 162 if( !logfile ) 163 fprintf( stderr, "Couldn't reopen \"%s\": %s\n", logfileName, tr_strerror( errno ) ); 164 } 165 166 configDir = tr_sessionGetConfigDir( mySession ); 167 tr_inf( "Reloading settings from \"%s\"", configDir ); 168 tr_bencInitDict( &settings, 0 ); 169 tr_bencDictAddBool( &settings, TR_PREFS_KEY_RPC_ENABLED, true ); 170 tr_sessionLoadSettings( &settings, configDir, MY_NAME ); 171 tr_sessionSet( mySession, &settings ); 172 tr_bencFree( &settings ); 173 tr_sessionReloadBlocklists( mySession ); 174 } 175 break; 176 } 177 178 default: 179 tr_err( "Unexpected signal(%d) in daemon, closing.", sig); 180 /* no break */ 181 182 case SIGINT: 183 case SIGTERM: 184 closing = true; 185 break; 186 } 187} 188 189#if defined(WIN32) 190 #define USE_NO_DAEMON 191#elif !defined(HAVE_DAEMON) || defined(__UCLIBC__) 192 #define USE_TR_DAEMON 193#else 194 #define USE_OS_DAEMON 195#endif 196 197static int 198tr_daemon( int nochdir, int noclose ) 199{ 200#if defined(USE_OS_DAEMON) 201 202 return daemon( nochdir, noclose ); 203 204#elif defined(USE_TR_DAEMON) 205 206 /* this is loosely based off of glibc's daemon() implementation 207 * http://sourceware.org/git/?p=glibc.git;a=blob_plain;f=misc/daemon.c */ 208 209 switch( fork( ) ) { 210 case -1: return -1; 211 case 0: break; 212 default: _exit(0); 213 } 214 215 if( setsid( ) == -1 ) 216 return -1; 217 218 if( !nochdir ) 219 chdir( "/" ); 220 221 if( !noclose ) { 222 int fd = open( "/dev/null", O_RDWR, 0 ); 223 dup2( fd, STDIN_FILENO ); 224 dup2( fd, STDOUT_FILENO ); 225 dup2( fd, STDERR_FILENO ); 226 close( fd ); 227 } 228 229 return 0; 230 231#else /* USE_NO_DAEMON */ 232 return 0; 233#endif 234} 235 236static const char* 237getConfigDir( int argc, const char ** argv ) 238{ 239 int c; 240 const char * configDir = NULL; 241 const char * optarg; 242 const int ind = tr_optind; 243 244 while(( c = tr_getopt( getUsage( ), argc, argv, options, &optarg ))) { 245 if( c == 'g' ) { 246 configDir = optarg; 247 break; 248 } 249 } 250 251 tr_optind = ind; 252 253 if( configDir == NULL ) 254 configDir = tr_getDefaultConfigDir( MY_NAME ); 255 256 return configDir; 257} 258 259static void 260onFileAdded( tr_session * session, const char * dir, const char * file ) 261{ 262 char * filename = tr_buildPath( dir, file, NULL ); 263 tr_ctor * ctor = tr_ctorNew( session ); 264 int err = tr_ctorSetMetainfoFromFile( ctor, filename ); 265 266 if( !err ) 267 { 268 tr_torrentNew( ctor, &err ); 269 270 if( err == TR_PARSE_ERR ) 271 tr_err( "Error parsing .torrent file \"%s\"", file ); 272 else 273 { 274 bool trash = false; 275 int test = tr_ctorGetDeleteSource( ctor, &trash ); 276 277 tr_inf( "Parsing .torrent file successful \"%s\"", file ); 278 279 if( !test && trash ) 280 { 281 tr_inf( "Deleting input .torrent file \"%s\"", file ); 282 if( remove( filename ) ) 283 tr_err( "Error deleting .torrent file: %s", tr_strerror( errno ) ); 284 } 285 else 286 { 287 char * new_filename = tr_strdup_printf( "%s.added", filename ); 288 rename( filename, new_filename ); 289 tr_free( new_filename ); 290 } 291 } 292 } 293 294 tr_ctorFree( ctor ); 295 tr_free( filename ); 296} 297 298static void 299printMessage( FILE * logfile, int level, const char * name, const char * message, const char * file, int line ) 300{ 301 if( logfile != NULL ) 302 { 303 char timestr[64]; 304 tr_getLogTimeStr( timestr, sizeof( timestr ) ); 305 if( name ) 306 fprintf( logfile, "[%s] %s %s (%s:%d)\n", timestr, name, message, file, line ); 307 else 308 fprintf( logfile, "[%s] %s (%s:%d)\n", timestr, message, file, line ); 309 } 310#ifdef HAVE_SYSLOG 311 else /* daemon... write to syslog */ 312 { 313 int priority; 314 315 /* figure out the syslog priority */ 316 switch( level ) { 317 case TR_MSG_ERR: priority = LOG_ERR; break; 318 case TR_MSG_DBG: priority = LOG_DEBUG; break; 319 default: priority = LOG_INFO; break; 320 } 321 322 if( name ) 323 syslog( priority, "%s %s (%s:%d)", name, message, file, line ); 324 else 325 syslog( priority, "%s (%s:%d)", message, file, line ); 326 } 327#endif 328} 329 330static void 331pumpLogMessages( FILE * logfile ) 332{ 333 const tr_msg_list * l; 334 tr_msg_list * list = tr_getQueuedMessages( ); 335 336 for( l=list; l!=NULL; l=l->next ) 337 printMessage( logfile, l->level, l->name, l->message, l->file, l->line ); 338 339 if( logfile != NULL ) 340 fflush( logfile ); 341 342 tr_freeMessageList( list ); 343} 344 345static tr_rpc_callback_status 346on_rpc_callback( tr_session * session UNUSED, 347 tr_rpc_callback_type type, 348 struct tr_torrent * tor UNUSED, 349 void * user_data UNUSED ) 350{ 351 if( type == TR_RPC_SESSION_CLOSE ) 352 closing = true; 353 return TR_RPC_OK; 354} 355 356int 357main( int argc, char ** argv ) 358{ 359 int c; 360 const char * optarg; 361 tr_benc settings; 362 bool boolVal; 363 bool loaded; 364 bool foreground = false; 365 bool dumpSettings = false; 366 const char * configDir = NULL; 367 const char * pid_filename; 368 dtr_watchdir * watchdir = NULL; 369 bool pidfile_created = false; 370 tr_session * session = NULL; 371 372 signal( SIGINT, gotsig ); 373 signal( SIGTERM, gotsig ); 374#ifndef WIN32 375 signal( SIGHUP, gotsig ); 376#endif 377 378 /* load settings from defaults + config file */ 379 tr_bencInitDict( &settings, 0 ); 380 tr_bencDictAddBool( &settings, TR_PREFS_KEY_RPC_ENABLED, true ); 381 configDir = getConfigDir( argc, (const char**)argv ); 382 loaded = tr_sessionLoadSettings( &settings, configDir, MY_NAME ); 383 384 /* overwrite settings from the comamndline */ 385 tr_optind = 1; 386 while(( c = tr_getopt( getUsage(), argc, (const char**)argv, options, &optarg ))) { 387 switch( c ) { 388 case 'a': tr_bencDictAddStr( &settings, TR_PREFS_KEY_RPC_WHITELIST, optarg ); 389 tr_bencDictAddBool( &settings, TR_PREFS_KEY_RPC_WHITELIST_ENABLED, true ); 390 break; 391 case 'b': tr_bencDictAddBool( &settings, TR_PREFS_KEY_BLOCKLIST_ENABLED, true ); 392 break; 393 case 'B': tr_bencDictAddBool( &settings, TR_PREFS_KEY_BLOCKLIST_ENABLED, false ); 394 break; 395 case 'c': tr_bencDictAddStr( &settings, PREF_KEY_DIR_WATCH, optarg ); 396 tr_bencDictAddBool( &settings, PREF_KEY_DIR_WATCH_ENABLED, true ); 397 break; 398 case 'C': tr_bencDictAddBool( &settings, PREF_KEY_DIR_WATCH_ENABLED, false ); 399 break; 400 case 941: tr_bencDictAddStr( &settings, TR_PREFS_KEY_INCOMPLETE_DIR, optarg ); 401 tr_bencDictAddBool( &settings, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, true ); 402 break; 403 case 942: tr_bencDictAddBool( &settings, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, false ); 404 break; 405 case 'd': dumpSettings = true; 406 break; 407 case 'e': logfile = fopen( optarg, LOGFILE_MODE_STR ); 408 if( logfile ) 409 logfileName = optarg; 410 else 411 fprintf( stderr, "Couldn't open \"%s\": %s\n", optarg, tr_strerror( errno ) ); 412 break; 413 case 'f': foreground = true; 414 break; 415 case 'g': /* handled above */ 416 break; 417 case 'V': /* version */ 418 fprintf(stderr, "%s %s\n", MY_NAME, LONG_VERSION_STRING); 419 exit( 0 ); 420 case 'o': tr_bencDictAddBool( &settings, TR_PREFS_KEY_DHT_ENABLED, true ); 421 break; 422 case 'O': tr_bencDictAddBool( &settings, TR_PREFS_KEY_DHT_ENABLED, false ); 423 break; 424 case 'p': tr_bencDictAddInt( &settings, TR_PREFS_KEY_RPC_PORT, atoi( optarg ) ); 425 break; 426 case 't': tr_bencDictAddBool( &settings, TR_PREFS_KEY_RPC_AUTH_REQUIRED, true ); 427 break; 428 case 'T': tr_bencDictAddBool( &settings, TR_PREFS_KEY_RPC_AUTH_REQUIRED, false ); 429 break; 430 case 'u': tr_bencDictAddStr( &settings, TR_PREFS_KEY_RPC_USERNAME, optarg ); 431 break; 432 case 'v': tr_bencDictAddStr( &settings, TR_PREFS_KEY_RPC_PASSWORD, optarg ); 433 break; 434 case 'w': tr_bencDictAddStr( &settings, TR_PREFS_KEY_DOWNLOAD_DIR, optarg ); 435 break; 436 case 'P': tr_bencDictAddInt( &settings, TR_PREFS_KEY_PEER_PORT, atoi( optarg ) ); 437 break; 438 case 'm': tr_bencDictAddBool( &settings, TR_PREFS_KEY_PORT_FORWARDING, true ); 439 break; 440 case 'M': tr_bencDictAddBool( &settings, TR_PREFS_KEY_PORT_FORWARDING, false ); 441 break; 442 case 'L': tr_bencDictAddInt( &settings, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, atoi( optarg ) ); 443 break; 444 case 'l': tr_bencDictAddInt( &settings, TR_PREFS_KEY_PEER_LIMIT_TORRENT, atoi( optarg ) ); 445 break; 446 case 800: paused = true; 447 break; 448 case 910: tr_bencDictAddInt( &settings, TR_PREFS_KEY_ENCRYPTION, TR_ENCRYPTION_REQUIRED ); 449 break; 450 case 911: tr_bencDictAddInt( &settings, TR_PREFS_KEY_ENCRYPTION, TR_ENCRYPTION_PREFERRED ); 451 break; 452 case 912: tr_bencDictAddInt( &settings, TR_PREFS_KEY_ENCRYPTION, TR_CLEAR_PREFERRED ); 453 break; 454 case 'i': tr_bencDictAddStr( &settings, TR_PREFS_KEY_BIND_ADDRESS_IPV4, optarg ); 455 break; 456 case 'I': tr_bencDictAddStr( &settings, TR_PREFS_KEY_BIND_ADDRESS_IPV6, optarg ); 457 break; 458 case 'r': tr_bencDictAddStr( &settings, TR_PREFS_KEY_RPC_BIND_ADDRESS, optarg ); 459 break; 460 case 953: tr_bencDictAddReal( &settings, TR_PREFS_KEY_RATIO, atof(optarg) ); 461 tr_bencDictAddBool( &settings, TR_PREFS_KEY_RATIO_ENABLED, true ); 462 break; 463 case 954: tr_bencDictAddBool( &settings, TR_PREFS_KEY_RATIO_ENABLED, false ); 464 break; 465 case 'x': tr_bencDictAddStr( &settings, PREF_KEY_PIDFILE, optarg ); 466 break; 467 case 'y': tr_bencDictAddBool( &settings, TR_PREFS_KEY_LPD_ENABLED, true ); 468 break; 469 case 'Y': tr_bencDictAddBool( &settings, TR_PREFS_KEY_LPD_ENABLED, false ); 470 break; 471 case 810: tr_bencDictAddInt( &settings, TR_PREFS_KEY_MSGLEVEL, TR_MSG_ERR ); 472 break; 473 case 811: tr_bencDictAddInt( &settings, TR_PREFS_KEY_MSGLEVEL, TR_MSG_INF ); 474 break; 475 case 812: tr_bencDictAddInt( &settings, TR_PREFS_KEY_MSGLEVEL, TR_MSG_DBG ); 476 break; 477 case 830: tr_bencDictAddBool( &settings, TR_PREFS_KEY_UTP_ENABLED, true ); 478 break; 479 case 831: tr_bencDictAddBool( &settings, TR_PREFS_KEY_UTP_ENABLED, false ); 480 break; 481 default: showUsage( ); 482 break; 483 } 484 } 485 486 if( foreground && !logfile ) 487 logfile = stderr; 488 489 if( !loaded ) 490 { 491 printMessage( logfile, TR_MSG_ERR, MY_NAME, "Error loading config file -- exiting.", __FILE__, __LINE__ ); 492 return -1; 493 } 494 495 if( dumpSettings ) 496 { 497 char * str = tr_bencToStr( &settings, TR_FMT_JSON, NULL ); 498 fprintf( stderr, "%s", str ); 499 tr_free( str ); 500 return 0; 501 } 502 503 if( !foreground && tr_daemon( true, false ) < 0 ) 504 { 505 char buf[256]; 506 tr_snprintf( buf, sizeof( buf ), "Failed to daemonize: %s", tr_strerror( errno ) ); 507 printMessage( logfile, TR_MSG_ERR, MY_NAME, buf, __FILE__, __LINE__ ); 508 exit( 1 ); 509 } 510 511 /* start the session */ 512 tr_formatter_mem_init( MEM_K, MEM_K_STR, MEM_M_STR, MEM_G_STR, MEM_T_STR ); 513 tr_formatter_size_init( DISK_K, DISK_K_STR, DISK_M_STR, DISK_G_STR, DISK_T_STR ); 514 tr_formatter_speed_init( SPEED_K, SPEED_K_STR, SPEED_M_STR, SPEED_G_STR, SPEED_T_STR ); 515 session = tr_sessionInit( "daemon", configDir, true, &settings ); 516 tr_sessionSetRPCCallback( session, on_rpc_callback, NULL ); 517 tr_ninf( NULL, "Using settings from \"%s\"", configDir ); 518 tr_sessionSaveSettings( session, configDir, &settings ); 519 520 pid_filename = NULL; 521 tr_bencDictFindStr( &settings, PREF_KEY_PIDFILE, &pid_filename ); 522 if( pid_filename && *pid_filename ) 523 { 524 FILE * fp = fopen( pid_filename, "w+" ); 525 if( fp != NULL ) 526 { 527 fprintf( fp, "%d", (int)getpid() ); 528 fclose( fp ); 529 tr_inf( "Saved pidfile \"%s\"", pid_filename ); 530 pidfile_created = true; 531 } 532 else 533 tr_err( "Unable to save pidfile \"%s\": %s", pid_filename, tr_strerror( errno ) ); 534 } 535 536 if( tr_bencDictFindBool( &settings, TR_PREFS_KEY_RPC_AUTH_REQUIRED, &boolVal ) && boolVal ) 537 tr_ninf( MY_NAME, "requiring authentication" ); 538 539 mySession = session; 540 541 /* If we got a SIGHUP during startup, process that now. */ 542 if( seenHUP ) 543 gotsig( SIGHUP ); 544 545 /* maybe add a watchdir */ 546 { 547 const char * dir; 548 549 if( tr_bencDictFindBool( &settings, PREF_KEY_DIR_WATCH_ENABLED, &boolVal ) 550 && boolVal 551 && tr_bencDictFindStr( &settings, PREF_KEY_DIR_WATCH, &dir ) 552 && dir 553 && *dir ) 554 { 555 tr_inf( "Watching \"%s\" for new .torrent files", dir ); 556 watchdir = dtr_watchdir_new( mySession, dir, onFileAdded ); 557 } 558 } 559 560 /* load the torrents */ 561 { 562 tr_torrent ** torrents; 563 tr_ctor * ctor = tr_ctorNew( mySession ); 564 if( paused ) 565 tr_ctorSetPaused( ctor, TR_FORCE, true ); 566 torrents = tr_sessionLoadTorrents( mySession, ctor, NULL ); 567 tr_free( torrents ); 568 tr_ctorFree( ctor ); 569 } 570 571#ifdef HAVE_SYSLOG 572 if( !foreground ) 573 openlog( MY_NAME, LOG_CONS|LOG_PID, LOG_DAEMON ); 574#endif 575 576 while( !closing ) { 577 tr_wait_msec( 1000 ); /* sleep one second */ 578 dtr_watchdir_update( watchdir ); 579 pumpLogMessages( logfile ); 580 } 581 582 printf( "Closing transmission session..." ); 583 tr_sessionSaveSettings( mySession, configDir, &settings ); 584 dtr_watchdir_free( watchdir ); 585 tr_sessionClose( mySession ); 586 pumpLogMessages( logfile ); 587 printf( " done.\n" ); 588 589 /* shutdown */ 590#if HAVE_SYSLOG 591 if( !foreground ) 592 { 593 syslog( LOG_INFO, "%s", "Closing session" ); 594 closelog( ); 595 } 596#endif 597 598 /* cleanup */ 599 if( pidfile_created ) 600 remove( pid_filename ); 601 tr_bencFree( &settings ); 602 return 0; 603} 604