1 /******************************************************************************
2 * $Id: cli.c 11335 2010-10-18 03:11:51Z charles $
4 * Copyright (c) 2005-2006 Transmission authors and contributors
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
31 #include <libtransmission/transmission.h>
32 #include <libtransmission/bencode.h>
33 #include <libtransmission/tr-getopt.h>
34 #include <libtransmission/utils.h> /* tr_wait_msec */
35 #include <libtransmission/version.h>
36 #include <libtransmission/web.h> /* tr_webRun */
43 #define MEM_K_STR "KiB"
44 #define MEM_M_STR "MiB"
45 #define MEM_G_STR "GiB"
46 #define MEM_T_STR "TiB"
49 #define DISK_B_STR "B"
50 #define DISK_K_STR "KiB"
51 #define DISK_M_STR "MiB"
52 #define DISK_G_STR "GiB"
53 #define DISK_T_STR "TiB"
56 #define SPEED_B_STR "B/s"
57 #define SPEED_K_STR "KiB/s"
58 #define SPEED_M_STR "MiB/s"
59 #define SPEED_G_STR "GiB/s"
60 #define SPEED_T_STR "TiB/s"
67 #define MY_CONFIG_NAME "transmission"
68 #define MY_READABLE_NAME "transmission-cli"
70 static tr_bool showVersion
= FALSE
;
71 static tr_bool verify
= 0;
72 static sig_atomic_t gotsig
= 0;
73 static sig_atomic_t manualUpdate
= 0;
75 static const char * torrentPath
= NULL
;
77 static const struct tr_option options
[] =
79 { 'b', "blocklist", "Enable peer blocklists", "b", 0, NULL
},
80 { 'B', "no-blocklist", "Disable peer blocklists", "B", 0, NULL
},
81 { 'd', "downlimit", "Set max download speed in "SPEED_K_STR
, "d", 1, "<speed>" },
82 { 'D', "no-downlimit", "Don't limit the download speed", "D", 0, NULL
},
83 { 910, "encryption-required", "Encrypt all peer connections", "er", 0, NULL
},
84 { 911, "encryption-preferred", "Prefer encrypted peer connections", "ep", 0, NULL
},
85 { 912, "encryption-tolerated", "Prefer unencrypted peer connections", "et", 0, NULL
},
86 { 'f', "finish", "Run a script when the torrent finishes", "f", 1, "<script>" },
87 { 'g', "config-dir", "Where to find configuration files", "g", 1, "<path>" },
88 { 'm', "portmap", "Enable portmapping via NAT-PMP or UPnP", "m", 0, NULL
},
89 { 'M', "no-portmap", "Disable portmapping", "M", 0, NULL
},
90 { 'p', "port", "Port for incoming peers (Default: " TR_DEFAULT_PEER_PORT_STR
")", "p", 1, "<port>" },
91 { 't', "tos", "Peer socket TOS (0 to 255, default=" TR_DEFAULT_PEER_SOCKET_TOS_STR
")", "t", 1, "<tos>" },
92 { 'u', "uplimit", "Set max upload speed in "SPEED_K_STR
, "u", 1, "<speed>" },
93 { 'U', "no-uplimit", "Don't limit the upload speed", "U", 0, NULL
},
94 { 'v', "verify", "Verify the specified torrent", "v", 0, NULL
},
95 { 'V', "version", "Show version number and exit", "V", 0, NULL
},
96 { 'w', "download-dir", "Where to save downloaded data", "w", 1, "<path>" },
97 { 0, NULL
, NULL
, NULL
, 0, NULL
}
103 return "A fast and easy BitTorrent client\n"
105 "Usage: " MY_READABLE_NAME
" [options] <file|url|magnet>";
108 static int parseCommandLine( tr_benc
*, int argc
, const char ** argv
);
110 static void sigHandler( int signal
);
113 tr_strlratio( char * buf
,
117 if( (int)ratio
== TR_RATIO_NA
)
118 tr_strlcpy( buf
, _( "None" ), buflen
);
119 else if( (int)ratio
== TR_RATIO_INF
)
120 tr_strlcpy( buf
, "Inf", buflen
);
121 else if( ratio
< 10.0 )
122 tr_snprintf( buf
, buflen
, "%.2f", ratio
);
123 else if( ratio
< 100.0 )
124 tr_snprintf( buf
, buflen
, "%.1f", ratio
);
126 tr_snprintf( buf
, buflen
, "%.0f", ratio
);
130 static tr_bool waitingOnWeb
;
133 onTorrentFileDownloaded( tr_session
* session UNUSED
,
134 long response_code UNUSED
,
135 const void * response
,
136 size_t response_byte_count
,
139 tr_ctorSetMetainfo( ctor
, response
, response_byte_count
);
140 waitingOnWeb
= FALSE
;
144 getStatusStr( const tr_stat
* st
,
148 if( st
->activity
& TR_STATUS_CHECK_WAIT
)
150 tr_snprintf( buf
, buflen
, "Waiting to verify local files" );
152 else if( st
->activity
& TR_STATUS_CHECK
)
154 tr_snprintf( buf
, buflen
,
155 "Verifying local files (%.2f%%, %.2f%% valid)",
156 tr_truncd( 100 * st
->recheckProgress
, 2 ),
157 tr_truncd( 100 * st
->percentDone
, 2 ) );
159 else if( st
->activity
& TR_STATUS_DOWNLOAD
)
165 tr_formatter_speed_KBps( upStr
, st
->pieceUploadSpeed_KBps
, sizeof( upStr
) );
166 tr_formatter_speed_KBps( dnStr
, st
->pieceDownloadSpeed_KBps
, sizeof( dnStr
) );
167 tr_strlratio( ratioStr
, st
->ratio
, sizeof( ratioStr
) );
169 tr_snprintf( buf
, buflen
,
171 "dl from %d of %d peers (%s), "
174 tr_truncd( 100 * st
->percentDone
, 1 ),
175 st
->peersSendingToUs
, st
->peersConnected
, upStr
,
176 st
->peersGettingFromUs
, dnStr
,
179 else if( st
->activity
& TR_STATUS_SEED
)
184 tr_formatter_speed_KBps( upStr
, st
->pieceUploadSpeed_KBps
, sizeof( upStr
) );
185 tr_strlratio( ratioStr
, st
->ratio
, sizeof( ratioStr
) );
187 tr_snprintf( buf
, buflen
,
188 "Seeding, uploading to %d of %d peer(s), %s [%s]",
189 st
->peersGettingFromUs
, st
->peersConnected
, upStr
, ratioStr
);
195 getConfigDir( int argc
, const char ** argv
)
198 const char * configDir
= NULL
;
200 const int ind
= tr_optind
;
202 while(( c
= tr_getopt( getUsage( ), argc
, argv
, options
, &optarg
))) {
211 if( configDir
== NULL
)
212 configDir
= tr_getDefaultConfigDir( MY_CONFIG_NAME
);
218 main( int argc
, char ** argv
)
223 tr_torrent
* tor
= NULL
;
225 const char * configDir
;
226 uint8_t * fileContents
;
229 tr_formatter_mem_init( MEM_K
, MEM_K_STR
, MEM_M_STR
, MEM_G_STR
, MEM_T_STR
);
230 tr_formatter_size_init( DISK_K
,DISK_K_STR
, DISK_M_STR
, DISK_G_STR
, DISK_T_STR
);
231 tr_formatter_speed_init( SPEED_K
, SPEED_K_STR
, SPEED_M_STR
, SPEED_G_STR
, SPEED_T_STR
);
233 printf( "%s %s\n", MY_READABLE_NAME
, LONG_VERSION_STRING
);
235 /* user needs to pass in at least one argument */
237 tr_getopt_usage( MY_READABLE_NAME
, getUsage( ), options
);
241 /* load the defaults from config file + libtransmission defaults */
242 tr_bencInitDict( &settings
, 0 );
243 configDir
= getConfigDir( argc
, (const char**)argv
);
244 tr_sessionLoadSettings( &settings
, configDir
, MY_CONFIG_NAME
);
246 /* the command line overrides defaults */
247 if( parseCommandLine( &settings
, argc
, (const char**)argv
) )
253 /* Check the options for validity */
255 fprintf( stderr
, "No torrent specified!\n" );
259 h
= tr_sessionInit( "cli", configDir
, FALSE
, &settings
);
261 ctor
= tr_ctorNew( h
);
263 fileContents
= tr_loadFile( torrentPath
, &fileLength
);
264 tr_ctorSetPaused( ctor
, TR_FORCE
, FALSE
);
265 if( fileContents
!= NULL
) {
266 tr_ctorSetMetainfo( ctor
, fileContents
, fileLength
);
267 } else if( !memcmp( torrentPath
, "magnet:?", 8 ) ) {
268 tr_ctorSetMetainfoFromMagnetLink( ctor
, torrentPath
);
269 } else if( !memcmp( torrentPath
, "http", 4 ) ) {
270 tr_webRun( h
, torrentPath
, NULL
, onTorrentFileDownloaded
, ctor
);
272 while( waitingOnWeb
) tr_wait_msec( 1000 );
274 fprintf( stderr
, "ERROR: Unrecognized torrent \"%s\".\n", torrentPath
);
275 fprintf( stderr
, " * If you're trying to create a torrent, use transmission-create.\n" );
276 fprintf( stderr
, " * If you're trying to see a torrent's info, use transmission-show.\n" );
277 tr_sessionClose( h
);
280 tr_free( fileContents
);
282 tor
= tr_torrentNew( ctor
, &error
);
286 fprintf( stderr
, "Failed opening torrent file `%s'\n", torrentPath
);
287 tr_sessionClose( h
);
291 signal( SIGINT
, sigHandler
);
293 signal( SIGHUP
, sigHandler
);
295 tr_torrentStart( tor
);
300 tr_torrentVerify( tor
);
305 char line
[LINEWIDTH
];
307 const char * messageName
[] = { NULL
, "Tracker gave a warning:",
308 "Tracker gave an error:",
316 printf( "\nStopping torrent...\n" );
317 tr_torrentStop( tor
);
323 if( !tr_torrentCanManualUpdate( tor
) )
326 "\nReceived SIGHUP, but can't send a manual update now\n" );
330 "\nReceived SIGHUP: manual update scheduled\n" );
331 tr_torrentManualUpdate( tor
);
335 st
= tr_torrentStat( tor
);
336 if( st
->activity
& TR_STATUS_STOPPED
)
339 getStatusStr( st
, line
, sizeof( line
) );
340 printf( "\r%-*s", LINEWIDTH
, line
);
342 if( messageName
[st
->error
] )
343 fprintf( stderr
, "\n%s: %s\n", messageName
[st
->error
], st
->errorString
);
346 tr_sessionSaveSettings( h
, configDir
, &settings
);
349 tr_bencFree( &settings
);
350 tr_sessionClose( h
);
361 parseCommandLine( tr_benc
* d
, int argc
, const char ** argv
)
366 while(( c
= tr_getopt( getUsage( ), argc
, argv
, options
, &optarg
)))
370 case 'b': tr_bencDictAddBool( d
, TR_PREFS_KEY_BLOCKLIST_ENABLED
, TRUE
);
372 case 'B': tr_bencDictAddBool( d
, TR_PREFS_KEY_BLOCKLIST_ENABLED
, FALSE
);
374 case 'd': tr_bencDictAddInt ( d
, TR_PREFS_KEY_DSPEED_KBps
, atoi( optarg
) );
375 tr_bencDictAddBool( d
, TR_PREFS_KEY_DSPEED_ENABLED
, TRUE
);
377 case 'D': tr_bencDictAddBool( d
, TR_PREFS_KEY_DSPEED_ENABLED
, FALSE
);
379 case 'f': tr_bencDictAddStr( d
, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME
, optarg
);
380 tr_bencDictAddBool( d
, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED
, TRUE
);
382 case 'g': /* handled above */
384 case 'm': tr_bencDictAddBool( d
, TR_PREFS_KEY_PORT_FORWARDING
, TRUE
);
386 case 'M': tr_bencDictAddBool( d
, TR_PREFS_KEY_PORT_FORWARDING
, FALSE
);
388 case 'p': tr_bencDictAddInt( d
, TR_PREFS_KEY_PEER_PORT
, atoi( optarg
) );
390 case 't': tr_bencDictAddInt( d
, TR_PREFS_KEY_PEER_SOCKET_TOS
, atoi( optarg
) );
392 case 'u': tr_bencDictAddInt( d
, TR_PREFS_KEY_USPEED_KBps
, atoi( optarg
) );
393 tr_bencDictAddBool( d
, TR_PREFS_KEY_USPEED_ENABLED
, TRUE
);
395 case 'U': tr_bencDictAddBool( d
, TR_PREFS_KEY_USPEED_ENABLED
, FALSE
);
397 case 'v': verify
= TRUE
;
399 case 'V': showVersion
= TRUE
;
401 case 'w': tr_bencDictAddStr( d
, TR_PREFS_KEY_DOWNLOAD_DIR
, optarg
);
403 case 910: tr_bencDictAddInt( d
, TR_PREFS_KEY_ENCRYPTION
, TR_ENCRYPTION_REQUIRED
);
405 case 911: tr_bencDictAddInt( d
, TR_PREFS_KEY_ENCRYPTION
, TR_ENCRYPTION_PREFERRED
);
407 case 912: tr_bencDictAddInt( d
, TR_PREFS_KEY_ENCRYPTION
, TR_CLEAR_PREFERRED
);
410 if( torrentPath
== NULL
)
411 torrentPath
= optarg
;
421 sigHandler( int signal
)
430 manualUpdate
= 1; break;