source: branches/2.2x/cli/cli.c @ 12009

Last change on this file since 12009 was 11709, checked in by jordan, 11 years ago

Update the copyright year in the source code comments.

The Berne Convention says that the copyright year is moot, so instead of adding another year to each file as in previous years, I've removed the year altogether from the source code comments in libtransmission, gtk, qt, utils, daemon, and cli.

Juliusz's copyright notice in tr-dht and Johannes' copyright notice in tr-lpd have been left alone; it didn't seem appropriate to modify them.

  • Property svn:keywords set to Date Rev Author Id
File size: 14.4 KB
Line 
1/******************************************************************************
2 * $Id: cli.c 11709 2011-01-19 13:48:47Z livings124 $
3 *
4 * Copyright (c) Transmission authors and contributors
5 *
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:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
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 *****************************************************************************/
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <unistd.h>
29#include <signal.h>
30
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 */
37
38/***
39****
40***/
41
42#define MEM_K 1024
43#define MEM_K_STR "KiB"
44#define MEM_M_STR "MiB"
45#define MEM_G_STR "GiB"
46#define MEM_T_STR "TiB"
47
48#define DISK_K 1024
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"
54
55#define SPEED_K 1024
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"
61
62/***
63****
64***/
65
66#define LINEWIDTH 80
67#define MY_CONFIG_NAME "transmission"
68#define MY_READABLE_NAME "transmission-cli"
69
70static tr_bool showVersion = FALSE;
71static tr_bool verify                = 0;
72static sig_atomic_t gotsig           = 0;
73static sig_atomic_t manualUpdate     = 0;
74
75static const char * torrentPath  = NULL;
76
77static const struct tr_option options[] =
78{
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 }
98};
99
100static const char *
101getUsage( void )
102{
103    return "A fast and easy BitTorrent client\n"
104           "\n"
105           "Usage: " MY_READABLE_NAME " [options] <file|url|magnet>";
106}
107
108static int parseCommandLine( tr_benc*, int argc, const char ** argv );
109
110static void         sigHandler( int signal );
111
112static char*
113tr_strlratio( char * buf,
114              double ratio,
115              size_t buflen )
116{
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 );
125    else
126        tr_snprintf( buf, buflen, "%.0f", ratio );
127    return buf;
128}
129
130static tr_bool waitingOnWeb;
131
132static void
133onTorrentFileDownloaded( tr_session   * session UNUSED,
134                         long           response_code UNUSED,
135                         const void   * response,
136                         size_t         response_byte_count,
137                         void         * ctor )
138{
139    tr_ctorSetMetainfo( ctor, response, response_byte_count );
140    waitingOnWeb = FALSE;
141}
142
143static void
144getStatusStr( const tr_stat * st,
145              char *          buf,
146              size_t          buflen )
147{
148    if( st->activity & TR_STATUS_CHECK_WAIT )
149    {
150        tr_snprintf( buf, buflen, "Waiting to verify local files" );
151    }
152    else if( st->activity & TR_STATUS_CHECK )
153    {
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 ) );
158    }
159    else if( st->activity & TR_STATUS_DOWNLOAD )
160    {
161        char upStr[80];
162        char dnStr[80];
163        char ratioStr[80];
164
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 ) );
168
169        tr_snprintf( buf, buflen,
170            "Progress: %.1f%%, "
171            "dl from %d of %d peers (%s), "
172            "ul to %d (%s) "
173            "[%s]",
174            tr_truncd( 100 * st->percentDone, 1 ),
175            st->peersSendingToUs, st->peersConnected, upStr,
176            st->peersGettingFromUs, dnStr,
177            ratioStr );
178    }
179    else if( st->activity & TR_STATUS_SEED )
180    {
181        char upStr[80];
182        char ratioStr[80];
183
184        tr_formatter_speed_KBps( upStr, st->pieceUploadSpeed_KBps, sizeof( upStr ) );
185        tr_strlratio( ratioStr, st->ratio, sizeof( ratioStr ) );
186
187        tr_snprintf( buf, buflen,
188                     "Seeding, uploading to %d of %d peer(s), %s [%s]",
189                     st->peersGettingFromUs, st->peersConnected, upStr, ratioStr );
190    }
191    else *buf = '\0';
192}
193
194static const char*
195getConfigDir( int argc, const char ** argv )
196{
197    int c;
198    const char * configDir = NULL;
199    const char * optarg;
200    const int ind = tr_optind;
201
202    while(( c = tr_getopt( getUsage( ), argc, argv, options, &optarg ))) {
203        if( c == 'g' ) {
204            configDir = optarg;
205            break;
206        }
207    }
208
209    tr_optind = ind;
210
211    if( configDir == NULL )
212        configDir = tr_getDefaultConfigDir( MY_CONFIG_NAME );
213
214    return configDir;
215}
216
217int
218main( int argc, char ** argv )
219{
220    int           error;
221    tr_session  * h;
222    tr_ctor     * ctor;
223    tr_torrent  * tor = NULL;
224    tr_benc       settings;
225    const char  * configDir;
226    uint8_t     * fileContents;
227    size_t        fileLength;
228
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 );
232
233    printf( "%s %s\n", MY_READABLE_NAME, LONG_VERSION_STRING );
234
235    /* user needs to pass in at least one argument */
236    if( argc < 2 ) {
237        tr_getopt_usage( MY_READABLE_NAME, getUsage( ), options );
238        return EXIT_FAILURE;
239    }
240
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 );
245
246    /* the command line overrides defaults */
247    if( parseCommandLine( &settings, argc, (const char**)argv ) )
248        return EXIT_FAILURE;
249
250    if( showVersion )
251        return 0;
252
253    /* Check the options for validity */
254    if( !torrentPath ) {
255        fprintf( stderr, "No torrent specified!\n" );
256        return EXIT_FAILURE;
257    }
258
259    h = tr_sessionInit( "cli", configDir, FALSE, &settings );
260
261    ctor = tr_ctorNew( h );
262
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 );
271        waitingOnWeb = TRUE;
272        while( waitingOnWeb ) tr_wait_msec( 1000 );
273    } else {
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 );
278        return EXIT_FAILURE;
279    }
280    tr_free( fileContents );
281
282    tor = tr_torrentNew( ctor, &error );
283    tr_ctorFree( ctor );
284    if( !tor )
285    {
286        fprintf( stderr, "Failed opening torrent file `%s'\n", torrentPath );
287        tr_sessionClose( h );
288        return EXIT_FAILURE;
289    }
290
291    signal( SIGINT, sigHandler );
292#ifndef WIN32
293    signal( SIGHUP, sigHandler );
294#endif
295    tr_torrentStart( tor );
296
297    if( verify )
298    {
299        verify = 0;
300        tr_torrentVerify( tor );
301    }
302
303    for( ; ; )
304    {
305        char            line[LINEWIDTH];
306        const tr_stat * st;
307        const char * messageName[] = { NULL, "Tracker gave a warning:",
308                                             "Tracker gave an error:",
309                                             "Error:" };
310
311        tr_wait_msec( 200 );
312
313        if( gotsig )
314        {
315            gotsig = 0;
316            printf( "\nStopping torrent...\n" );
317            tr_torrentStop( tor );
318        }
319
320        if( manualUpdate )
321        {
322            manualUpdate = 0;
323            if( !tr_torrentCanManualUpdate( tor ) )
324                fprintf(
325                    stderr,
326                    "\nReceived SIGHUP, but can't send a manual update now\n" );
327            else
328            {
329                fprintf( stderr,
330                         "\nReceived SIGHUP: manual update scheduled\n" );
331                tr_torrentManualUpdate( tor );
332            }
333        }
334
335        st = tr_torrentStat( tor );
336        if( st->activity & TR_STATUS_STOPPED )
337            break;
338
339        getStatusStr( st, line, sizeof( line ) );
340        printf( "\r%-*s", LINEWIDTH, line );
341
342        if( messageName[st->error] )
343            fprintf( stderr, "\n%s: %s\n", messageName[st->error], st->errorString );
344    }
345
346    tr_sessionSaveSettings( h, configDir, &settings );
347
348    printf( "\n" );
349    tr_bencFree( &settings );
350    tr_sessionClose( h );
351    return EXIT_SUCCESS;
352}
353
354/***
355****
356****
357****
358***/
359
360static int
361parseCommandLine( tr_benc * d, int argc, const char ** argv )
362{
363    int          c;
364    const char * optarg;
365
366    while(( c = tr_getopt( getUsage( ), argc, argv, options, &optarg )))
367    {
368        switch( c )
369        {
370            case 'b': tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED, TRUE );
371                      break;
372            case 'B': tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED, FALSE );
373                      break;
374            case 'd': tr_bencDictAddInt ( d, TR_PREFS_KEY_DSPEED_KBps, atoi( optarg ) );
375                      tr_bencDictAddBool( d, TR_PREFS_KEY_DSPEED_ENABLED, TRUE );
376                      break;
377            case 'D': tr_bencDictAddBool( d, TR_PREFS_KEY_DSPEED_ENABLED, FALSE );
378                      break;
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 );
381                      break;
382            case 'g': /* handled above */
383                      break;
384            case 'm': tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING, TRUE );
385                      break;
386            case 'M': tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING, FALSE );
387                      break;
388            case 'p': tr_bencDictAddInt( d, TR_PREFS_KEY_PEER_PORT, atoi( optarg ) );
389                      break;
390            case 't': tr_bencDictAddInt( d, TR_PREFS_KEY_PEER_SOCKET_TOS, atoi( optarg ) );
391                      break;
392            case 'u': tr_bencDictAddInt( d, TR_PREFS_KEY_USPEED_KBps, atoi( optarg ) );
393                      tr_bencDictAddBool( d, TR_PREFS_KEY_USPEED_ENABLED, TRUE );
394                      break;
395            case 'U': tr_bencDictAddBool( d, TR_PREFS_KEY_USPEED_ENABLED, FALSE );
396                      break;
397            case 'v': verify = TRUE;
398                      break;
399            case 'V': showVersion = TRUE;
400                      break;
401            case 'w': tr_bencDictAddStr( d, TR_PREFS_KEY_DOWNLOAD_DIR, optarg );
402                      break;
403            case 910: tr_bencDictAddInt( d, TR_PREFS_KEY_ENCRYPTION, TR_ENCRYPTION_REQUIRED );
404                      break;
405            case 911: tr_bencDictAddInt( d, TR_PREFS_KEY_ENCRYPTION, TR_ENCRYPTION_PREFERRED );
406                      break;
407            case 912: tr_bencDictAddInt( d, TR_PREFS_KEY_ENCRYPTION, TR_CLEAR_PREFERRED );
408                      break;
409            case TR_OPT_UNK:
410                      if( torrentPath == NULL )
411                          torrentPath = optarg;
412                      break;
413            default: return 1;
414        }
415    }
416
417    return 0;
418}
419
420static void
421sigHandler( int signal )
422{
423    switch( signal )
424    {
425        case SIGINT:
426            gotsig = 1; break;
427
428#ifndef WIN32
429        case SIGHUP:
430            manualUpdate = 1; break;
431
432#endif
433        default:
434            break;
435    }
436}
437
Note: See TracBrowser for help on using the repository browser.