source: trunk/cli/cli.c @ 14581

Last change on this file since 14581 was 14581, checked in by mikedld, 6 years ago

Remove unused session tag

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