source: trunk/cli/cli.c @ 13913

Last change on this file since 13913 was 13913, checked in by jordan, 8 years ago

(libT) change the API signature for tr_torrentVerify() s.t. client code can be notified when the verify is finished

  • Property svn:keywords set to Date Rev Author Id
File size: 14.4 KB
Line 
1/******************************************************************************
2 * $Id: cli.c 13913 2013-01-31 21:58:25Z jordan $
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> /* fprintf () */
26#include <stdlib.h> /* atoi () */
27#include <string.h> /* memcmp () */
28#include <signal.h>
29
30#include <libtransmission/transmission.h>
31#include <libtransmission/tr-getopt.h>
32#include <libtransmission/utils.h> /* tr_wait_msec */
33#include <libtransmission/variant.h>
34#include <libtransmission/version.h>
35#include <libtransmission/web.h> /* tr_webRun */
36
37/***
38****
39***/
40
41#define MEM_K 1024
42#define MEM_K_STR "KiB"
43#define MEM_M_STR "MiB"
44#define MEM_G_STR "GiB"
45#define MEM_T_STR "TiB"
46
47#define DISK_K 1000
48#define DISK_B_STR   "B"
49#define DISK_K_STR "kB"
50#define DISK_M_STR "MB"
51#define DISK_G_STR "GB"
52#define DISK_T_STR "TB"
53
54#define SPEED_K 1000
55#define SPEED_B_STR  "B/s"
56#define SPEED_K_STR "kB/s"
57#define SPEED_M_STR "MB/s"
58#define SPEED_G_STR "GB/s"
59#define SPEED_T_STR "TB/s"
60
61/***
62****
63***/
64
65#define LINEWIDTH 80
66#define MY_CONFIG_NAME "transmission"
67#define MY_READABLE_NAME "transmission-cli"
68
69static bool showVersion              = false;
70static bool verify                   = false;
71static sig_atomic_t gotsig           = false;
72static sig_atomic_t manualUpdate     = false;
73
74static const char * torrentPath  = NULL;
75
76static const struct tr_option options[] =
77{
78  { 'b', "blocklist",            "Enable peer blocklists", "b",  0, NULL },
79  { 'B', "no-blocklist",         "Disable peer blocklists", "B",  0, NULL },
80  { 'd', "downlimit",            "Set max download speed in "SPEED_K_STR, "d",  1, "<speed>" },
81  { 'D', "no-downlimit",         "Don't limit the download speed", "D",  0, NULL },
82  { 910, "encryption-required",  "Encrypt all peer connections", "er", 0, NULL },
83  { 911, "encryption-preferred", "Prefer encrypted peer connections", "ep", 0, NULL },
84  { 912, "encryption-tolerated", "Prefer unencrypted peer connections", "et", 0, NULL },
85  { 'f', "finish",               "Run a script when the torrent finishes", "f", 1, "<script>" },
86  { 'g', "config-dir",           "Where to find configuration files", "g", 1, "<path>" },
87  { 'm', "portmap",              "Enable portmapping via NAT-PMP or UPnP", "m",  0, NULL },
88  { 'M', "no-portmap",           "Disable portmapping", "M",  0, NULL },
89  { 'p', "port", "Port for incoming peers (Default: " TR_DEFAULT_PEER_PORT_STR ")", "p", 1, "<port>" },
90  { 't', "tos", "Peer socket TOS (0 to 255, default=" TR_DEFAULT_PEER_SOCKET_TOS_STR ")", "t", 1, "<tos>" },
91  { 'u', "uplimit",              "Set max upload speed in "SPEED_K_STR, "u",  1, "<speed>"   },
92  { 'U', "no-uplimit",           "Don't limit the upload speed", "U",  0, NULL        },
93  { 'v', "verify",               "Verify the specified torrent", "v",  0, NULL        },
94  { 'V', "version",              "Show version number and exit", "V", 0, NULL },
95  { 'w', "download-dir",         "Where to save downloaded data", "w",  1, "<path>"    },
96  { 0, NULL, NULL, NULL, 0, NULL }
97};
98
99static const char *
100getUsage (void)
101{
102  return "A fast and easy BitTorrent client\n"
103         "\n"
104         "Usage: " MY_READABLE_NAME " [options] <file|url|magnet>";
105}
106
107static int parseCommandLine (tr_variant*, int argc, const char ** argv);
108
109static void sigHandler (int signal);
110
111static char*
112tr_strlratio (char * buf, double ratio, size_t buflen)
113{
114  if ((int)ratio == TR_RATIO_NA)
115    tr_strlcpy (buf, _ ("None"), buflen);
116  else if ((int)ratio == TR_RATIO_INF)
117    tr_strlcpy (buf, "Inf", buflen);
118  else if (ratio < 10.0)
119    tr_snprintf (buf, buflen, "%.2f", ratio);
120  else if (ratio < 100.0)
121    tr_snprintf (buf, buflen, "%.1f", ratio);
122  else
123    tr_snprintf (buf, buflen, "%.0f", ratio);
124
125  return buf;
126}
127
128static bool waitingOnWeb;
129
130static void
131onTorrentFileDownloaded (tr_session   * session UNUSED,
132                         bool           did_connect UNUSED,
133                         bool           did_timeout 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, dnStr,
176                   st->peersGettingFromUs, upStr,
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
192    {
193      *buf = '\0';
194    }
195}
196
197static const char*
198getConfigDir (int argc, const char ** argv)
199{
200  int c;
201  const char * configDir = NULL;
202  const char * optarg;
203  const int ind = tr_optind;
204
205  while ((c = tr_getopt (getUsage (), argc, argv, options, &optarg)))
206    {
207      if (c == 'g')
208        {
209          configDir = optarg;
210          break;
211        }
212    }
213
214  tr_optind = ind;
215
216  if (configDir == NULL)
217    configDir = tr_getDefaultConfigDir (MY_CONFIG_NAME);
218
219  return configDir;
220}
221
222int
223main (int argc, char ** argv)
224{
225  int           error;
226  tr_session  * h;
227  tr_ctor     * ctor;
228  tr_torrent  * tor = NULL;
229  tr_variant       settings;
230  const char  * configDir;
231  uint8_t     * fileContents;
232  size_t        fileLength;
233  const char  * str;
234
235  tr_formatter_mem_init (MEM_K, MEM_K_STR, MEM_M_STR, MEM_G_STR, MEM_T_STR);
236  tr_formatter_size_init (DISK_K,DISK_K_STR, DISK_M_STR, DISK_G_STR, DISK_T_STR);
237  tr_formatter_speed_init (SPEED_K, SPEED_K_STR, SPEED_M_STR, SPEED_G_STR, SPEED_T_STR);
238
239  printf ("%s %s\n", MY_READABLE_NAME, LONG_VERSION_STRING);
240
241  /* user needs to pass in at least one argument */
242  if (argc < 2)
243    {
244      tr_getopt_usage (MY_READABLE_NAME, getUsage (), options);
245      return EXIT_FAILURE;
246    }
247
248  /* load the defaults from config file + libtransmission defaults */
249  tr_variantInitDict (&settings, 0);
250  configDir = getConfigDir (argc, (const char**)argv);
251  tr_sessionLoadSettings (&settings, configDir, MY_CONFIG_NAME);
252
253  /* the command line overrides defaults */
254  if (parseCommandLine (&settings, argc, (const char**)argv))
255    return EXIT_FAILURE;
256
257  if (showVersion)
258    return EXIT_SUCCESS;
259
260  /* Check the options for validity */
261  if (!torrentPath)
262    {
263      fprintf (stderr, "No torrent specified!\n");
264      return EXIT_FAILURE;
265    }
266
267  if (tr_variantDictFindStr (&settings, TR_KEY_download_dir, &str, NULL))
268    {
269      if (!tr_fileExists (str, NULL))
270        {
271          tr_mkdirp (str, 0700);
272
273          if (!tr_fileExists (str, NULL))
274            {
275              fprintf (stderr, "Unable to create download directory \"%s\"!\n", str);
276              return EXIT_FAILURE;
277            }
278        }
279    }
280
281  h = tr_sessionInit ("cli", configDir, false, &settings);
282
283  ctor = tr_ctorNew (h);
284
285  fileContents = tr_loadFile (torrentPath, &fileLength);
286  tr_ctorSetPaused (ctor, TR_FORCE, false);
287  if (fileContents != NULL)
288    {
289      tr_ctorSetMetainfo (ctor, fileContents, fileLength);
290    }
291  else if (!memcmp (torrentPath, "magnet:?", 8))
292    {
293      tr_ctorSetMetainfoFromMagnetLink (ctor, torrentPath);
294    }
295  else if (!memcmp (torrentPath, "http", 4))
296    {
297      tr_webRun (h, torrentPath, NULL, NULL, onTorrentFileDownloaded, ctor);
298      waitingOnWeb = true;
299      while (waitingOnWeb)
300        tr_wait_msec (1000);
301    }
302  else
303    {
304      fprintf (stderr, "ERROR: Unrecognized torrent \"%s\".\n", torrentPath);
305      fprintf (stderr, " * If you're trying to create a torrent, use transmission-create.\n");
306      fprintf (stderr, " * If you're trying to see a torrent's info, use transmission-show.\n");
307      tr_sessionClose (h);
308      return EXIT_FAILURE;
309    }
310
311  tr_free (fileContents);
312
313  tor = tr_torrentNew (ctor, &error);
314  tr_ctorFree (ctor);
315  if (!tor)
316    {
317      fprintf (stderr, "Failed opening torrent file `%s'\n", torrentPath);
318      tr_sessionClose (h);
319      return EXIT_FAILURE;
320    }
321
322  signal (SIGINT, sigHandler);
323#ifndef WIN32
324  signal (SIGHUP, sigHandler);
325#endif
326  tr_torrentStart (tor);
327
328  if (verify)
329    {
330      verify = false;
331      tr_torrentVerify (tor, NULL, NULL);
332    }
333
334  for (;;)
335    {
336      char  line[LINEWIDTH];
337      const tr_stat * st;
338      const char * messageName[] = { NULL, "Tracker gave a warning:",
339                                           "Tracker gave an error:",
340                                           "Error:" };
341
342      tr_wait_msec (200);
343
344      if (gotsig)
345        {
346          gotsig = false;
347          printf ("\nStopping torrent...\n");
348          tr_torrentStop (tor);
349        }
350
351      if (manualUpdate)
352        {
353          manualUpdate = false;
354          if (!tr_torrentCanManualUpdate (tor))
355            {
356              fprintf (stderr, "\nReceived SIGHUP, but can't send a manual update now\n");
357            }
358          else
359            {
360              fprintf (stderr, "\nReceived SIGHUP: manual update scheduled\n");
361              tr_torrentManualUpdate (tor);
362            }
363        }
364
365      st = tr_torrentStat (tor);
366      if (st->activity == TR_STATUS_STOPPED)
367        break;
368
369      getStatusStr (st, line, sizeof (line));
370      printf ("\r%-*s", LINEWIDTH, line);
371
372      if (messageName[st->error])
373        fprintf (stderr, "\n%s: %s\n", messageName[st->error], st->errorString);
374    }
375
376  tr_sessionSaveSettings (h, configDir, &settings);
377
378  printf ("\n");
379  tr_variantFree (&settings);
380  tr_sessionClose (h);
381  return EXIT_SUCCESS;
382}
383
384/***
385****
386****
387***/
388
389static int
390parseCommandLine (tr_variant * d, int argc, const char ** argv)
391{
392  int c;
393  const char * optarg;
394
395  while ((c = tr_getopt (getUsage (), argc, argv, options, &optarg)))
396    {
397      switch (c)
398        {
399          case 'b':
400            tr_variantDictAddBool (d, TR_KEY_blocklist_enabled, true);
401            break;
402
403          case 'B': tr_variantDictAddBool (d, TR_KEY_blocklist_enabled, false);
404            break;
405
406          case 'd':
407            tr_variantDictAddInt (d, TR_KEY_speed_limit_down, atoi (optarg));
408            tr_variantDictAddBool (d, TR_KEY_speed_limit_down_enabled, true);
409            break;
410
411          case 'D': tr_variantDictAddBool (d, TR_KEY_speed_limit_down_enabled, false);
412            break;
413
414          case 'f':
415            tr_variantDictAddStr (d, TR_KEY_script_torrent_done_filename, optarg);
416            tr_variantDictAddBool (d, TR_KEY_script_torrent_done_enabled, true);
417            break;
418
419          case 'g': /* handled above */
420            break;
421
422          case 'm':
423            tr_variantDictAddBool (d, TR_KEY_port_forwarding_enabled, true);
424            break;
425
426          case 'M':
427            tr_variantDictAddBool (d, TR_KEY_port_forwarding_enabled, false);
428            break;
429
430          case 'p':
431            tr_variantDictAddInt (d, TR_KEY_peer_port, atoi (optarg));
432            break;
433
434          case 't':
435            tr_variantDictAddInt (d, TR_KEY_peer_socket_tos, atoi (optarg));
436            break;
437
438          case 'u':
439            tr_variantDictAddInt (d, TR_KEY_speed_limit_up, atoi (optarg));
440            tr_variantDictAddBool (d, TR_KEY_speed_limit_up_enabled, true);
441            break;
442
443          case 'U':
444            tr_variantDictAddBool (d, TR_KEY_speed_limit_up_enabled, false);
445            break;
446
447          case 'v':
448            verify = true;
449            break;
450
451          case 'V':
452            showVersion = true;
453            break;
454
455          case 'w':
456            tr_variantDictAddStr (d, TR_KEY_download_dir, optarg);
457            break;
458
459          case 910:
460            tr_variantDictAddInt (d, TR_KEY_encryption, TR_ENCRYPTION_REQUIRED);
461            break;
462
463          case 911:
464            tr_variantDictAddInt (d, TR_KEY_encryption, TR_ENCRYPTION_PREFERRED);
465            break;
466
467          case 912:
468            tr_variantDictAddInt (d, TR_KEY_encryption, TR_CLEAR_PREFERRED);
469            break;
470
471          case TR_OPT_UNK:
472            if (torrentPath == NULL)
473              torrentPath = optarg;
474            break;
475
476          default:
477            return 1;
478        }
479    }
480
481  return 0;
482}
483
484static void
485sigHandler (int signal)
486{
487  switch (signal)
488    {
489      case SIGINT:
490        gotsig = true;
491        break;
492
493#ifndef WIN32
494      case SIGHUP:
495        manualUpdate = true;
496        break;
497
498#endif
499      default:
500        break;
501    }
502}
503
Note: See TracBrowser for help on using the repository browser.