3 Copyright (c) 2003, Arvid Norberg
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
10 * Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 * Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in
14 the documentation and/or other materials provided with the distribution.
15 * Neither the name of the author nor the names of its
16 contributors may be used to endorse or promote products derived
17 from this software without specific prior written permission.
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 POSSIBILITY OF SUCH DAMAGE.
38 #include "libtorrent/config.hpp"
41 #pragma warning(push, 1)
44 #include <boost/filesystem/operations.hpp>
45 #include <boost/filesystem/convenience.hpp>
46 #include <boost/filesystem/fstream.hpp>
47 #include <boost/filesystem/exception.hpp>
48 #include <boost/bind.hpp>
49 #include <boost/program_options.hpp>
50 #include <boost/regex.hpp>
56 #include "libtorrent/extensions/metadata_transfer.hpp"
57 #include "libtorrent/extensions/ut_metadata.hpp"
58 #include "libtorrent/extensions/ut_pex.hpp"
59 #include "libtorrent/extensions/smart_ban.hpp"
61 #include "libtorrent/entry.hpp"
62 #include "libtorrent/bencode.hpp"
63 #include "libtorrent/session.hpp"
64 #include "libtorrent/identify_client.hpp"
65 #include "libtorrent/alert_types.hpp"
66 #include "libtorrent/ip_filter.hpp"
67 #include "libtorrent/magnet_uri.hpp"
68 #include "libtorrent/bitfield.hpp"
69 #include "libtorrent/file.hpp"
76 # define for if (false) {} else for
82 bool sleep_and_input(char* c
)
95 CONSOLE_SCREEN_BUFFER_INFO si
;
96 HANDLE h
= GetStdHandle(STD_OUTPUT_HANDLE
);
97 GetConsoleScreenBufferInfo(h
, &si
);
100 FillConsoleOutputCharacter(h
, ' ', si
.dwSize
.X
* si
.dwSize
.Y
, c
, &n
);
101 SetConsoleCursorPosition(h
, c
);
111 #include <sys/ioctl.h>
113 #define ANSI_TERMINAL_COLORS
119 termios new_settings
;
120 tcgetattr(0,&stored_settings
);
121 new_settings
= stored_settings
;
122 // Disable canonical mode, and set buffer size to 1 byte
123 new_settings
.c_lflag
&= (~ICANON
);
124 new_settings
.c_cc
[VTIME
] = 0;
125 new_settings
.c_cc
[VMIN
] = 1;
126 tcsetattr(0,TCSANOW
,&new_settings
);
128 ~set_keypress() { tcsetattr(0,TCSANOW
,&stored_settings
); }
129 termios stored_settings
;
132 bool sleep_and_input(char* c
)
134 // sets the terminal to single-character mode
135 // and resets when destructed
141 timeval tv
= {0, 500000};
142 if (select(1, &set
, 0, 0, &tv
) > 0)
152 std::cout
<< "\033[2J\033[0;0H";
157 bool print_peers
= false;
158 bool print_log
= false;
159 bool print_downloads
= false;
160 bool print_piece_bar
= false;
161 bool print_file_progress
= false;
162 bool sequential_download
= false;
164 bool print_ip
= true;
165 bool print_as
= false;
166 bool print_timers
= false;
167 bool print_block
= false;
168 bool print_peer_rate
= false;
169 bool print_fails
= false;
170 bool print_send_bufs
= true;
171 std::ofstream g_log_file
;
173 int active_torrent
= 0;
175 char const* esc(char const* code
)
177 #ifdef ANSI_TERMINAL_COLORS
178 // this is a silly optimization
179 // to avoid copying of strings
180 enum { num_strings
= 200 };
181 static char buf
[num_strings
][20];
182 static int round_robin
= 0;
183 char* ret
= buf
[round_robin
];
185 if (round_robin
>= num_strings
) round_robin
= 0;
190 while (code
[j
]) ret
[i
++] = code
[j
++];
199 std::string
to_string(int v
, int width
)
202 s
.flags(std::ios_base::right
);
209 std::string
& to_string(float v
, int width
, int precision
= 3)
211 // this is a silly optimization
212 // to avoid copying of strings
213 enum { num_strings
= 20 };
214 static std::string buf
[num_strings
];
215 static int round_robin
= 0;
216 std::string
& ret
= buf
[round_robin
];
218 if (round_robin
>= num_strings
) round_robin
= 0;
220 int size
= std::sprintf(&ret
[0], "%*.*f", width
, precision
, v
);
221 ret
.resize((std::min
)(size
, width
));
225 std::string
const& add_suffix(float val
)
227 const char* prefix
[] = {"kB", "MB", "GB", "TB"};
228 const int num_prefix
= sizeof(prefix
) / sizeof(const char*);
229 for (int i
= 0; i
< num_prefix
; ++i
)
232 if (fabs(val
) < 1000.f
)
234 std::string
& ret
= to_string(val
, 4);
239 std::string
& ret
= to_string(val
, 4);
244 std::string
const& piece_bar(libtorrent::bitfield
const& p
, int width
)
246 #ifdef ANSI_TERMINAL_COLORS
247 static const char* lookup
[] =
249 // black, blue, cyan, white
250 "40", "44", "46", "47"
253 const int table_size
= sizeof(lookup
) / sizeof(lookup
[0]);
255 static const char char_lookup
[] =
256 { ' ', '.', ':', '-', '+', '*', '#'};
258 const int table_size
= sizeof(char_lookup
) / sizeof(char_lookup
[0]);
261 double piece_per_char
= p
.size() / double(width
);
262 static std::string bar
;
264 bar
.reserve(width
* 6);
267 // the [piece, piece + pieces_per_char) range is the pieces that are represented by each character
269 for (int i
= 0; i
< width
; ++i
, piece
+= piece_per_char
)
273 int end
= (std::max
)(int(piece
+ piece_per_char
), int(piece
) + 1);
274 for (int k
= int(piece
); k
< end
; ++k
, ++num_pieces
)
275 if (p
[k
]) ++num_have
;
276 int color
= int(std::ceil(num_have
/ float(num_pieces
) * (table_size
- 1)));
277 #ifdef ANSI_TERMINAL_COLORS
278 bar
+= esc(lookup
[color
]);
281 bar
+= char_lookup
[color
];
284 #ifdef ANSI_TERMINAL_COLORS
291 std::string
const& progress_bar(float progress
, int width
, char const* code
= "33")
293 static std::string bar
;
295 bar
.reserve(width
+ 10);
297 int progress_chars
= static_cast<int>(progress
* width
+ .5f
);
299 std::fill_n(std::back_inserter(bar
), progress_chars
, '#');
301 std::fill_n(std::back_inserter(bar
), width
- progress_chars
, '-');
305 int peer_index(libtorrent::tcp::endpoint addr
, std::vector
<libtorrent::peer_info
> const& peers
)
307 using namespace libtorrent
;
308 std::vector
<peer_info
>::const_iterator i
= std::find_if(peers
.begin()
309 , peers
.end(), bind(&peer_info::ip
, _1
) == addr
);
310 if (i
== peers
.end()) return -1;
312 return i
- peers
.begin();
315 void print_peer_info(std::ostream
& out
, std::vector
<libtorrent::peer_info
> const& peers
)
317 using namespace libtorrent
;
318 if (print_ip
) out
<< "IP ";
319 #ifndef TORRENT_DISABLE_GEO_IP
320 if (print_as
) out
<< "AS ";
322 out
<< "down (total | peak ) up (total | peak ) sent-req recv flags source ";
323 if (print_fails
) out
<< "fail hshf ";
324 if (print_send_bufs
) out
<< "rq sndb quota rcvb ";
325 if (print_timers
) out
<< "inactive wait timeout ";
327 if (print_block
) out
<< "block-progress ";
328 #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES
331 if (print_peer_rate
) out
<< "peer-rate ";
334 for (std::vector
<peer_info
>::const_iterator i
= peers
.begin();
335 i
!= peers
.end(); ++i
)
337 if (i
->flags
& (peer_info::handshake
))
343 std::stringstream ip
;
344 ip
<< i
->ip
.address().to_string() << ":" << i
->ip
.port();
346 out
<< ip
.str() << " ";
349 #ifndef TORRENT_DISABLE_GEO_IP
352 std::string as_name
= i
->inet_as_name
;
353 if (as_name
.size() > 42) as_name
.resize(42);
355 out
<< as_name
<< " ";
359 out
<< esc("32") << (i
->down_speed
> 0 ? add_suffix(i
->down_speed
) + "/s " : " ")
360 << "(" << (i
->total_download
> 0 ? add_suffix(i
->total_download
) : " ") << "|"
361 << (i
->download_rate_peak
> 0 ? add_suffix(i
->download_rate_peak
) + "/s" : " ") << ") " << esc("0")
362 << esc("31") << (i
->up_speed
> 0 ? add_suffix(i
->up_speed
) + "/s ": " ")
363 << "(" << (i
->total_upload
> 0 ? add_suffix(i
->total_upload
) : " ") << "|"
364 << (i
->upload_rate_peak
> 0 ? add_suffix(i
->upload_rate_peak
) + "/s" : " ") << ") " << esc("0")
365 << to_string(i
->download_queue_length
, 3) << " ("
366 << to_string(i
->target_dl_queue_length
, 3) << ") "
367 << to_string(i
->upload_queue_length
, 3) << " "
368 << ((i
->flags
& peer_info::interesting
)?'I':'.')
369 << ((i
->flags
& peer_info::choked
)?'C':'.')
370 << ((i
->flags
& peer_info::remote_interested
)?'i':'.')
371 << ((i
->flags
& peer_info::remote_choked
)?'c':'.')
372 << ((i
->flags
& peer_info::supports_extensions
)?'e':'.')
373 << ((i
->flags
& peer_info::local_connection
)?'l':'r')
374 << ((i
->flags
& peer_info::seed
)?'s':'.')
375 << ((i
->flags
& peer_info::on_parole
)?'p':'.')
376 << ((i
->flags
& peer_info::optimistic_unchoke
)?'O':'.')
377 << ((i
->read_state
== peer_info::bw_torrent
)?'t':
378 (i
->read_state
== peer_info::bw_global
)?'r':
379 (i
->read_state
== peer_info::bw_network
)?'R':'.')
380 << ((i
->write_state
== peer_info::bw_torrent
)?'t':
381 (i
->write_state
== peer_info::bw_global
)?'w':
382 (i
->write_state
== peer_info::bw_network
)?'W':'.')
383 << ((i
->flags
& peer_info::snubbed
)?'S':'.')
384 #ifndef TORRENT_DISABLE_ENCRYPTION
385 << ((i
->flags
& peer_info::rc4_encrypted
)?'E':
386 (i
->flags
& peer_info::plaintext_encrypted
)?'e':'.')
391 << ((i
->source
& peer_info::tracker
)?"T":"_")
392 << ((i
->source
& peer_info::pex
)?"P":"_")
393 << ((i
->source
& peer_info::dht
)?"D":"_")
394 << ((i
->source
& peer_info::lsd
)?"L":"_")
395 << ((i
->source
& peer_info::resume_data
)?"R":"_") << " ";
398 out
<< to_string(i
->failcount
, 3) << " "
399 << to_string(i
->num_hashfails
, 3) << " ";
403 out
<< to_string(i
->requests_in_buffer
, 2) << " "
404 << to_string(i
->used_send_buffer
, 6) << " ("<< add_suffix(i
->send_buffer_size
) << ") "
405 << to_string(i
->send_quota
, 5) << " "
406 << to_string(i
->used_receive_buffer
, 6) << " ("<< add_suffix(i
->receive_buffer_size
) << ") ";
410 out
<< to_string(total_seconds(i
->last_active
), 8) << " "
411 << to_string(total_seconds(i
->last_request
), 4) << " "
412 << to_string(i
->request_timeout
, 7) << " ";
414 out
<< add_suffix(i
->pending_disk_bytes
) << " "
415 << to_string(i
->rtt
, 4) << " ";
419 if (i
->downloading_piece_index
>= 0)
422 i
->downloading_progress
/ float(i
->downloading_total
), 14);
426 out
<< progress_bar(0.f
, 14);
430 #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES
431 if (i
->country
[0] == 0)
437 out
<< " " << i
->country
[0] << i
->country
[1];
440 if (print_peer_rate
) out
<< " " << (i
->remote_dl_rate
> 0 ? add_suffix(i
->remote_dl_rate
) + "/s ": " ");
443 if (i
->flags
& peer_info::handshake
)
445 out
<< esc("31") << " waiting for handshake" << esc("0") << "\n";
447 else if (i
->flags
& peer_info::connecting
)
449 out
<< esc("31") << " connecting to peer" << esc("0") << "\n";
451 else if (i
->flags
& peer_info::queued
)
453 out
<< esc("33") << " queued" << esc("0") << "\n";
457 out
<< " " << i
->client
<< "\n";
462 typedef std::multimap
<std::string
, libtorrent::torrent_handle
> handles_t
;
465 using boost::filesystem::path
;
466 using boost::filesystem::exists
;
467 using boost::filesystem::directory_iterator
;
468 using boost::filesystem::extension
;
471 // monitored_dir is true if this torrent is added because
472 // it was found in the directory that is monitored. If it
473 // is, it should be remembered so that it can be removed
474 // if it's no longer in that directory.
475 void add_torrent(libtorrent::session
& ses
477 , std::string
const& torrent
478 , float preferred_ratio
480 , path
const& save_path
482 , int torrent_upload_limit
483 , int torrent_download_limit
)
485 using namespace libtorrent
;
487 boost::intrusive_ptr
<torrent_info
> t(new torrent_info(torrent
.c_str()));
489 std::cout
<< t
->name() << "\n";
491 add_torrent_params p
;
492 lazy_entry resume_data
;
494 std::string filename
= (save_path
/ (t
->name() + ".fastresume")).string();
496 std::vector
<char> buf
;
497 if (load_file(filename
.c_str(), buf
) == 0)
498 p
.resume_data
= &buf
;
501 p
.save_path
= save_path
;
502 p
.storage_mode
= compact_mode
? storage_mode_compact
: storage_mode_sparse
;
504 p
.duplicate_is_error
= false;
505 p
.auto_managed
= true;
506 torrent_handle h
= ses
.add_torrent(p
);
508 handles
.insert(std::make_pair(
509 monitored_dir
?std::string(torrent
):std::string(), h
));
511 h
.set_max_connections(50);
512 h
.set_max_uploads(-1);
513 h
.set_ratio(preferred_ratio
);
514 h
.set_upload_limit(torrent_upload_limit
);
515 h
.set_download_limit(torrent_download_limit
);
516 #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES
517 h
.resolve_countries(true);
521 void scan_dir(path
const& dir_path
522 , libtorrent::session
& ses
524 , float preferred_ratio
526 , path
const& save_path
527 , int torrent_upload_limit
528 , int torrent_download_limit
)
530 std::set
<std::string
> valid
;
532 using namespace libtorrent
;
534 for (directory_iterator
i(dir_path
), end
; i
!= end
; ++i
)
536 if (extension(*i
) != ".torrent") continue;
537 std::string file
= i
->string();
539 handles_t::iterator k
= handles
.find(file
);
540 if (k
!= handles
.end())
546 // the file has been added to the dir, start
548 add_torrent(ses
, handles
, file
, preferred_ratio
, compact_mode
549 , save_path
, true, torrent_upload_limit
, torrent_download_limit
);
553 // remove the torrents that are no longer in the directory
555 for (handles_t::iterator i
= handles
.begin(); !handles
.empty() && i
!= handles
.end();)
557 if (i
->first
.empty() || valid
.find(i
->first
) != valid
.end())
563 torrent_handle
& h
= i
->second
;
570 h
.auto_managed(false);
572 // the alert handler for save_resume_data_alert
573 // will save it to disk and remove the torrent
574 h
.save_resume_data();
580 libtorrent::torrent_handle
get_active_torrent(handles_t
const& handles
)
582 if (active_torrent
>= handles
.size()
583 || active_torrent
< 0) return libtorrent::torrent_handle();
584 handles_t::const_iterator i
= handles
.begin();
585 std::advance(i
, active_torrent
);
589 void print_alert(libtorrent::alert
const* a
, std::ostream
& os
)
591 using namespace libtorrent
;
593 #ifdef ANSI_TERMINAL_COLORS
594 if (a
->category() & alert::error_notification
)
598 else if (a
->category() & (alert::peer_notification
| alert::storage_notification
))
603 os
<< "[" << time_now_string() << "] " << a
->message();
604 #ifdef ANSI_TERMINAL_COLORS
608 if (g_log_file
.good())
609 g_log_file
<< "[" << time_now_string() << "] " << a
->message() << std::endl
;
612 void handle_alert(libtorrent::session
& ses
, libtorrent::alert
* a
)
614 using namespace libtorrent
;
616 if (torrent_finished_alert
* p
= dynamic_cast<torrent_finished_alert
*>(a
))
618 p
->handle
.set_max_connections(30);
620 // write resume data for the finished torrent
621 torrent_handle h
= p
->handle
;
622 h
.save_resume_data();
624 else if (save_resume_data_alert
* p
= dynamic_cast<save_resume_data_alert
*>(a
))
626 torrent_handle h
= p
->handle
;
627 TORRENT_ASSERT(p
->resume_data
);
630 boost::filesystem::ofstream
out(h
.save_path() / (h
.name() + ".fastresume")
631 , std::ios_base::binary
);
632 out
.unsetf(std::ios_base::skipws
);
633 bencode(std::ostream_iterator
<char>(out
), *p
->resume_data
);
634 if (h
.is_paused() && !h
.is_auto_managed()) ses
.remove_torrent(h
);
639 static char const* state_str
[] =
640 {"checking (q)", "checking", "connecting", "dl metadata"
641 , "downloading", "finished", "seeding", "allocating"};
643 int main(int ac
, char* av
[])
645 #if BOOST_VERSION < 103400
646 using boost::filesystem::no_check
;
647 path::default_name_check(no_check
);
651 float preferred_ratio
;
654 int torrent_upload_limit
;
655 int torrent_download_limit
;
656 int upload_slots_limit
;
658 std::string save_path_str
;
659 std::string log_level
;
660 std::string log_file_name
;
661 std::string ip_filter_file
;
662 std::string allocation_mode
;
663 std::string in_monitor_dir
;
664 std::string bind_to_interface
;
666 std::string proxy_login
;
667 std::string proxy_type
;
670 int bind_port_start
= 0;
671 int bind_port_end
= 0;
673 namespace po
= boost::program_options
;
677 po::options_description
desc("supported options");
679 ("help,h", "display this help message")
680 ("port,p", po::value
<int>(&listen_port
)->default_value(6881)
681 , "set listening port")
682 ("ratio,r", po::value
<float>(&preferred_ratio
)->default_value(0)
683 , "set the preferred upload/download ratio. 0 means infinite. Values "
684 "smaller than 1 are clamped to 1.")
685 ("max-download-rate,d", po::value
<int>(&download_limit
)->default_value(0)
686 , "the maximum download rate given in kB/s. 0 means infinite.")
687 ("max-upload-rate,u", po::value
<int>(&upload_limit
)->default_value(0)
688 , "the maximum upload rate given in kB/s. 0 means infinite.")
689 ("max-torrent-upload-rate", po::value
<int>(&torrent_upload_limit
)->default_value(20)
690 , "the maximum upload rate for an individual torrent, given in kB/s. 0 means infinite.")
691 ("max-torrent-download-rate", po::value
<int>(&torrent_download_limit
)->default_value(0)
692 , "the maximum download rate for an individual torrent, given in kB/s. 0 means infinite.")
693 ("max-upload-slots", po::value
<int>(&upload_slots_limit
)->default_value(5)
694 , "the maximum number of upload slots. 0 means infinite.")
695 ("save-path,s", po::value
<std::string
>(&save_path_str
)->default_value("./")
696 , "the path where the downloaded file/folder should be placed.")
697 ("log-level,l", po::value
<std::string
>(&log_level
)->default_value("info")
698 , "sets the level at which events are logged [debug | info | warning | fatal].")
699 ("log-file,f", po::value
<std::string
>(&log_file_name
)->default_value("")
700 , "sets a file to log all events to")
701 ("ip-filter,f", po::value
<std::string
>(&ip_filter_file
)->default_value("")
702 , "sets the path to the ip-filter file used to block access from certain "
704 ("allocation-mode,a", po::value
<std::string
>(&allocation_mode
)->default_value("full")
705 , "sets mode used for allocating the downloaded files on disk. "
706 "Possible options are [full | compact]")
707 ("input-file,i", po::value
<std::vector
<std::string
> >()
708 , "adds an input .torrent file. At least one is required. arguments "
709 "without any flag are implicitly an input file. To start a torrentless "
710 "download, use <info-hash>@<tracker-url> instead of specifying a file.")
711 ("monitor-dir,m", po::value
<std::string
>(&in_monitor_dir
)
712 , "monitors the given directory, looking for .torrent files and "
713 "automatically starts downloading them. It will stop downloading "
714 "torrent files that are removed from the directory")
715 ("poll-interval,t", po::value
<int>(&poll_interval
)->default_value(2)
716 , "if a directory is being monitored, this is the interval (given "
717 "in seconds) between two refreshes of the directory listing")
718 ("wait-retry,w", po::value
<int>(&wait_retry
)->default_value(30)
719 , "if the download of a url seed failes, this is the interval (given "
720 "in seconds) to wait until the next retry")
721 ("half-open-limit,o", po::value
<int>(&half_open_limit
)->default_value(-1)
722 , "Sets the maximum number of simultaneous half-open tcp connections")
723 ("bind,b", po::value
<std::string
>(&bind_to_interface
)->default_value("")
724 , "Sets the local interface to bind outbound and the listen "
726 ("proxy-server,x", po::value
<std::string
>(&proxy
)->default_value("")
727 , "Sets the http proxy to be used for tracker and web seeds "
728 "connections. The string is expected to be on the form: "
729 "<hostname>:<port>. If no port is specified, 8080 is assumed")
730 ("proxy-login,n", po::value
<std::string
>(&proxy_login
)->default_value("")
731 , "Sets the username and password used to authenticate with the http "
732 "proxy. The string should be given in the form: <username>:<password>")
733 ("proxy-type", po::value
<std::string
>(&proxy_type
)->default_value("socks5")
734 , "Sets the type of proxy to use [socks5 | http] ")
735 ("bind-port-start", po::value
<int>(&bind_port_start
)->default_value(0)
736 , "The lower port number that outgoing connections will be bound to")
737 ("bind-port-end", po::value
<int>(&bind_port_end
)->default_value(0)
738 , "The upper port number that outgoing connections will be bound to")
741 po::positional_options_description p
;
742 p
.add("input-file", -1);
744 po::variables_map vm
;
745 po::store(po::command_line_parser(ac
, av
).
746 options(desc
).positional(p
).run(), vm
);
749 // make sure the arguments stays within the usable limits
750 path
monitor_dir(in_monitor_dir
);
751 if (listen_port
< 0 || listen_port
> 65525) listen_port
= 6881;
752 if (preferred_ratio
!= 0 && preferred_ratio
< 1.f
) preferred_ratio
= 1.f
;
753 upload_limit
*= 1000;
754 torrent_upload_limit
*= 1000;
755 torrent_download_limit
*= 1000;
756 download_limit
*= 1000;
757 if (download_limit
<= 0) download_limit
= -1;
758 if (upload_limit
<= 0) upload_limit
= -1;
759 if (torrent_upload_limit
<= 0) torrent_upload_limit
= -1;
760 if (torrent_download_limit
<= 0) torrent_download_limit
= -1;
761 if (poll_interval
< 2) poll_interval
= 2;
762 if (wait_retry
< 0) wait_retry
= 0;
763 if (half_open_limit
< 1) half_open_limit
= -1;
764 if (upload_slots_limit
<= 0) upload_slots_limit
= -1;
765 if (!monitor_dir
.empty() && !exists(monitor_dir
))
767 std::cerr
<< "The monitor directory doesn't exist: " << monitor_dir
.string() << std::endl
;
772 || vm
.count("input-file") + vm
.count("monitor-dir") == 0)
774 std::cout
<< desc
<< "\n";
778 if (!log_file_name
.empty())
779 g_log_file
.open(log_file_name
.c_str());
781 bool compact_allocation_mode
= (allocation_mode
== "compact");
783 using namespace libtorrent
;
785 std::vector
<std::string
> input
;
786 if (vm
.count("input-file") > 0)
787 input
= vm
["input-file"].as
< std::vector
<std::string
> >();
789 session_settings settings
;
794 std::size_t i
= proxy
.find(':');
795 ps
.hostname
= proxy
.substr(0, i
);
796 if (i
== std::string::npos
) ps
.port
= 8080;
797 else ps
.port
= atoi(proxy
.substr(i
+ 1).c_str());
798 if (proxy_type
== "socks5")
799 ps
.type
= proxy_settings::socks5
;
801 ps
.type
= proxy_settings::http
;
803 if (!proxy_login
.empty())
805 std::size_t i
= proxy_login
.find(':');
806 if (i
== std::string::npos
)
808 std::cerr
<< "Proxy login did not match the required format: "
809 << proxy_login
<< std::endl
;
812 ps
.username
= proxy_login
.substr(0, i
);
813 ps
.password
= proxy_login
.substr(i
+ 1);
814 if (proxy_type
== "socks5")
815 ps
.type
= proxy_settings::socks5_pw
;
817 ps
.type
= proxy_settings::http_pw
;
821 settings
.user_agent
= "client_test/" LIBTORRENT_VERSION
;
822 settings
.urlseed_wait_retry
= wait_retry
;
824 settings
.outgoing_ports
.first
= bind_port_start
;
825 settings
.outgoing_ports
.second
= bind_port_end
;
827 std::deque
<std::string
> events
;
829 ptime next_dir_scan
= time_now();
831 // the string is the filename of the .torrent file, but only if
832 // it was added through the directory monitor. It is used to
833 // be able to remove torrents that were added via the directory
834 // monitor when they're not in the directory anymore.
837 #ifndef TORRENT_DISABLE_GEO_IP
838 ses
.load_asnum_db("GeoIPASNum.dat");
839 ses
.load_country_db("GeoIP.dat");
843 // NAT-PMP port mapping
845 // Local service discovery (finds peers on the local network)
847 ses
.add_extension(&create_metadata_plugin
);
848 ses
.add_extension(&create_ut_pex_plugin
);
849 ses
.add_extension(&create_ut_metadata_plugin
);
850 ses
.add_extension(&create_smart_ban_plugin
);
852 ses
.set_max_uploads(upload_slots_limit
);
853 ses
.set_max_half_open_connections(half_open_limit
);
854 ses
.set_download_rate_limit(download_limit
);
855 ses
.set_upload_rate_limit(upload_limit
);
856 ses
.listen_on(std::make_pair(listen_port
, listen_port
+ 10)
857 , bind_to_interface
.c_str());
858 ses
.set_settings(settings
);
859 ses
.set_tracker_proxy(ps
);
860 ses
.set_peer_proxy(ps
);
861 ses
.set_web_seed_proxy(ps
);
863 if (log_level
== "debug")
864 ses
.set_severity_level(alert::debug
);
865 else if (log_level
== "warning")
866 ses
.set_severity_level(alert::warning
);
867 else if (log_level
== "fatal")
868 ses
.set_severity_level(alert::fatal
);
870 ses
.set_severity_level(alert::info
);
872 boost::filesystem::ifstream
ses_state_file(".ses_state"
873 , std::ios_base::binary
);
874 ses_state_file
.unsetf(std::ios_base::skipws
);
875 ses
.load_state(bdecode(
876 std::istream_iterator
<char>(ses_state_file
)
877 , std::istream_iterator
<char>()));
879 #ifndef TORRENT_DISABLE_DHT
880 settings
.use_dht_as_fallback
= false;
882 boost::filesystem::ifstream
dht_state_file(".dht_state"
883 , std::ios_base::binary
);
884 dht_state_file
.unsetf(std::ios_base::skipws
);
887 std::istream_iterator
<char>(dht_state_file
)
888 , std::istream_iterator
<char>());
889 ses
.start_dht(dht_state
);
890 ses
.add_dht_router(std::make_pair(std::string("router.bittorrent.com")
892 ses
.add_dht_router(std::make_pair(std::string("router.utorrent.com")
894 ses
.add_dht_router(std::make_pair(std::string("router.bitcomet.com")
898 // look for ipfilter.dat
900 // reads emule ipfilter files.
901 // with the following format:
903 // <first-ip> - <last-ip> , <access> , <comment>
905 // first-ip is an ip address that defines the first
906 // address of the range
907 // last-ip is the last ip address in the range
908 // access is a number specifying the access control
909 // for this ip-range. Right now values > 127 = allowed
910 // and numbers <= 127 = blocked
911 // the rest of the line is ignored
913 // In the original spec ranges may not overlap, but
914 // here ranges may overlap, and it is the last added
915 // rule that has precedence for addresses that may fall
916 // into more than one range.
917 if (!ip_filter_file
.empty())
919 std::ifstream
in(ip_filter_file
.c_str());
924 in
.getline(line
, 300);
925 int len
= in
.gcount();
926 if (len
<= 0) continue;
927 if (line
[0] == '#') continue;
930 std::stringstream
ln(line
);
931 ln
>> a
>> dummy
>> b
>> dummy
>> c
>> dummy
>> d
>> dummy
;
932 address_v4
start((a
<< 24) + (b
<< 16) + (c
<< 8) + d
);
933 ln
>> a
>> dummy
>> b
>> dummy
>> c
>> dummy
>> d
>> dummy
;
934 address_v4
last((a
<< 24) + (b
<< 16) + (c
<< 8) + d
);
937 if (flags
<= 127) flags
= ip_filter::blocked
;
939 if (ln
.fail()) break;
940 filter
.add_rule(start
, last
, flags
);
942 ses
.set_ip_filter(filter
);
944 boost::filesystem::path
save_path(save_path_str
);
946 // load the torrents given on the commandline
947 boost::regex
ex("([0-9A-Fa-f]{40})@(.+)");
948 for (std::vector
<std::string
>::const_iterator i
= input
.begin();
949 i
!= input
.end(); ++i
)
951 #ifndef BOOST_NO_EXCEPTIONS
955 // first see if this is a torrentless download
956 if (i
->substr(0, 7) == "magnet:")
958 std::cout
<< "adding MANGET link: " << *i
<< std::endl
;
959 torrent_handle h
= add_magnet_uri(ses
, *i
, save_path
960 , compact_allocation_mode
? storage_mode_compact
961 : storage_mode_sparse
);
963 handles
.insert(std::make_pair(std::string(), h
));
965 h
.set_max_connections(50);
966 h
.set_max_uploads(-1);
967 h
.set_ratio(preferred_ratio
);
968 h
.set_upload_limit(torrent_upload_limit
);
969 h
.set_download_limit(torrent_download_limit
);
973 if (boost::regex_match(i
->c_str(), what
, ex
))
975 sha1_hash info_hash
= boost::lexical_cast
<sha1_hash
>(what
[1]);
977 add_torrent_params p
;
978 p
.name
= std::string(what
[2]).c_str();
979 p
.info_hash
= info_hash
;
980 p
.save_path
= save_path
;
981 p
.storage_mode
= compact_allocation_mode
? storage_mode_compact
: storage_mode_sparse
;
983 p
.duplicate_is_error
= false;
984 p
.auto_managed
= true;
985 torrent_handle h
= ses
.add_torrent(p
);
987 handles
.insert(std::make_pair(std::string(), h
));
989 h
.set_max_connections(50);
990 h
.set_max_uploads(-1);
991 h
.set_ratio(preferred_ratio
);
992 h
.set_upload_limit(torrent_upload_limit
);
993 h
.set_download_limit(torrent_download_limit
);
996 // if it's a torrent file, open it as usual
997 add_torrent(ses
, handles
, i
->c_str(), preferred_ratio
998 , compact_allocation_mode
, save_path
, false
999 , torrent_upload_limit
, torrent_download_limit
);
1000 #ifndef BOOST_NO_EXCEPTIONS
1002 catch (std::exception
& e
)
1004 std::cout
<< e
.what() << "\n";
1010 std::vector
<peer_info
> peers
;
1011 std::vector
<partial_piece_info
> queue
;
1016 while (sleep_and_input(&c
))
1020 // escape code, read another character
1026 if (c
!= '[') break;
1036 if (active_torrent
< 0) active_torrent
= 0;
1042 if (active_torrent
>= handles
.size()) active_torrent
= handles
.size() - 1;
1048 if (ses
.is_paused()) ses
.resume();
1054 std::cout
<< "saving peers for torrents" << std::endl
;
1056 std::vector
<peer_list_entry
> peers
;
1057 for (handles_t::iterator i
= handles
.begin();
1058 i
!= handles
.end(); ++i
)
1060 i
->second
.get_full_peer_list(peers
);
1061 std::ofstream
f(("peers_" + i
->second
.name()).c_str());
1062 for (std::vector
<peer_list_entry
>::iterator k
= peers
.begin()
1063 , end(peers
.end()); k
!= end
; ++k
)
1065 f
<< k
->ip
.address()
1066 #ifndef TORRENT_DISABLE_GEO_IP
1067 << "\t" << ses
.as_for_ip(k
->ip
.address())
1076 // keep track of the number of resume data
1077 // alerts to wait for
1078 int num_resume_data
= 0;
1079 for (handles_t::iterator i
= handles
.begin();
1080 i
!= handles
.end(); ++i
)
1082 torrent_handle
& h
= i
->second
;
1083 if (!h
.is_valid() || !h
.has_metadata()) continue;
1086 std::cout
<< "pausing " << h
.name() << std::endl
;
1088 // save_resume_data will generate an alert when it's done
1089 h
.save_resume_data();
1092 std::cout
<< "waiting for resume data" << std::endl
;
1094 while (num_resume_data
> 0)
1096 alert
const* a
= ses
.wait_for_alert(seconds(30));
1099 std::cout
<< " aborting with " << num_resume_data
<< " outstanding "
1100 "torrents to save resume data for" << std::endl
;
1104 std::auto_ptr
<alert
> holder
= ses
.pop_alert();
1106 ::print_alert(holder
.get(), std::cout
);
1107 std::cout
<< std::endl
;
1109 save_resume_data_alert
const* rd
= dynamic_cast<save_resume_data_alert
const*>(a
);
1113 if (!rd
->resume_data
) continue;
1115 torrent_handle h
= rd
->handle
;
1116 boost::filesystem::ofstream
out(h
.save_path()
1117 / (h
.get_torrent_info().name() + ".fastresume"), std::ios_base::binary
);
1118 out
.unsetf(std::ios_base::skipws
);
1119 bencode(std::ostream_iterator
<char>(out
), *rd
->resume_data
);
1126 torrent_handle h
= get_active_torrent(handles
);
1127 if (h
.is_valid()) h
.force_recheck();
1132 torrent_handle h
= get_active_torrent(handles
);
1133 if (h
.is_valid()) h
.force_reannounce();
1138 torrent_handle h
= get_active_torrent(handles
);
1139 if (h
.is_valid()) h
.set_sequential_download(!h
.is_sequential_download());
1144 torrent_handle h
= get_active_torrent(handles
);
1147 if (!h
.is_auto_managed() && h
.is_paused())
1149 h
.auto_managed(true);
1153 h
.auto_managed(false);
1161 torrent_handle h
= get_active_torrent(handles
);
1162 if (h
.is_valid()) h
.clear_error();
1166 if (c
== 'i') print_peers
= !print_peers
;
1167 if (c
== 'l') print_log
= !print_log
;
1168 if (c
== 'd') print_downloads
= !print_downloads
;
1169 if (c
== 'f') print_file_progress
= !print_file_progress
;
1170 if (c
== 'a') print_piece_bar
= !print_piece_bar
;
1172 if (c
== '1') print_ip
= !print_ip
;
1173 if (c
== '2') print_as
= !print_as
;
1174 if (c
== '3') print_timers
= !print_timers
;
1175 if (c
== '4') print_block
= !print_block
;
1176 if (c
== '5') print_peer_rate
= !print_peer_rate
;
1177 if (c
== '6') print_fails
= !print_fails
;
1178 if (c
== '7') print_send_bufs
= !print_send_bufs
;
1180 if (c
== 'q') break;
1182 int terminal_width
= 80;
1187 ioctl(STDOUT_FILENO
, TIOCGWINSZ
, (char*)&size
);
1188 terminal_width
= size
.ws_col
;
1192 // loop through the alert queue to see if anything has happened.
1193 std::auto_ptr
<alert
> a
;
1194 a
= ses
.pop_alert();
1195 std::string now
= time_now_string();
1198 std::stringstream event_string
;
1200 ::print_alert(a
.get(), event_string
);
1201 ::handle_alert(ses
, a
.get());
1203 events
.push_back(event_string
.str());
1204 if (events
.size() >= 20) events
.pop_front();
1206 a
= ses
.pop_alert();
1209 session_status sess_stat
= ses
.status();
1211 std::stringstream out
;
1212 out
<< "[q] quit [i] toggle peers [d] toggle downloading pieces [p] toggle paused "
1213 "[a] toggle piece bar [s] toggle download sequential [f] toggle files "
1214 "[j] force recheck [space] toggle session pause [c] clear error\n"
1215 "[1] toggle IP [2] toggle AS [3] toggle timers [4] toggle block progress "
1216 "[5] toggle peer rate [6] toggle failures [7] toggle send buffers\n";
1218 int torrent_index
= 0;
1219 torrent_handle active_handle
;
1220 for (handles_t::iterator i
= handles
.begin();
1221 i
!= handles
.end(); ++torrent_index
)
1223 torrent_handle
& h
= i
->second
;
1234 #ifdef ANSI_TERMINAL_COLORS
1235 char const* term
= "\x1b[0m";
1237 char const* term
= "";
1239 if (active_torrent
== torrent_index
)
1241 term
= "\x1b[0m\x1b[7m";
1242 out
<< esc("7") << "*";
1249 int queue_pos
= h
.queue_position();
1250 if (queue_pos
== -1) out
<< "- ";
1251 else out
<< std::setw(3) << queue_pos
;
1253 if (h
.is_paused()) out
<< esc("34");
1254 else out
<< esc("37");
1255 out
<< std::setw(40) << std::setiosflags(std::ios::left
);
1257 std::string name
= h
.name();
1258 if (name
.size() > 40) name
.resize(40);
1263 torrent_status s
= h
.status();
1265 bool paused
= h
.is_paused();
1266 bool auto_managed
= h
.is_auto_managed();
1267 out
<< std::setw(13) << std::setiosflags(std::ios::left
);
1268 if (!s
.error
.empty())
1270 out
<< esc("31") << "error " << s
.error
;
1271 out
<< esc("0") << std::endl
;
1275 if (paused
&& !auto_managed
) out
<< "paused";
1276 else if (paused
&& auto_managed
) out
<< "queued";
1277 else out
<< state_str
[s
.state
];
1280 int downloaders
= 0;
1282 if (s
.num_complete
>= 0) seeds
= s
.num_complete
;
1283 else seeds
= s
.list_seeds
;
1285 if (s
.num_incomplete
>= 0) downloaders
= s
.num_incomplete
;
1286 else downloaders
= s
.list_peers
- s
.list_seeds
;
1288 out
<< "download: " << "(" << esc("32") << add_suffix(s
.total_download
) << term
<< ") "
1289 "upload: " << esc("31") << (s
.upload_rate
> 0 ? add_suffix(s
.upload_rate
) + "/s ": " ") << term
1290 << "(" << esc("31") << add_suffix(s
.total_upload
) << term
<< ") "
1291 << "swarm: " << to_string(downloaders
, 4) << ":" << to_string(seeds
, 4)
1292 << " bw queue: (" << s
.up_bandwidth_queue
<< " | " << s
.down_bandwidth_queue
<< ") "
1293 "all-time (Rx: " << esc("32") << add_suffix(s
.all_time_download
) << term
1294 << " Tx: " << esc("31") << add_suffix(s
.all_time_upload
) << term
<< ") "
1295 << std::hex
<< s
.seed_rank
<< std::dec
<< " "
1296 << s
.last_scrape
<< "\n" << esc("0");
1298 if (s
.state
!= torrent_status::seeding
)
1300 char const* progress_bar_color
= "33"; // yellow
1301 if (s
.state
== torrent_status::checking_files
1302 || s
.state
== torrent_status::downloading_metadata
)
1304 progress_bar_color
= "35"; // magenta
1306 else if (s
.current_tracker
.empty())
1308 progress_bar_color
= "31"; // red
1310 else if (sess_stat
.has_incoming_connections
)
1312 progress_bar_color
= "32"; // green
1314 out
<< " progress: " << esc("32") << s
.total_done
<< esc("0") << " Bytes ";
1318 out
<< (s
.progress
*100) << "% ";
1319 out
<< progress_bar(s
.progress
, terminal_width
- 37, progress_bar_color
) << "\n";
1320 if (print_piece_bar
&& s
.progress
< 1.f
)
1321 out
<< " " << piece_bar(s
.pieces
, terminal_width
- 5) << "\n";
1322 out
<< " peers: " << esc("37") << s
.num_peers
<< esc("0") << " (" << esc("37") << s
.connect_candidates
<< esc("0") << ") "
1323 << "seeds: " << esc("37") << s
.num_seeds
<< esc("0") << " "
1324 << "distributed copies: " << esc("37") << s
.distributed_copies
<< esc("0")
1325 // << " magnet-link: " << make_magnet_uri(h) << "\n"
1326 << " download: " << esc("32") << (s
.download_rate
> 0 ? add_suffix(s
.download_rate
) + "/s ": " ") << esc("0");
1327 boost::posix_time::time_duration t
= s
.next_announce
;
1328 out
<< " next announce: " << esc("37")
1329 << to_string(t
.hours(), 2) << ":"
1330 << to_string(t
.minutes(), 2) << ":"
1331 << to_string(t
.seconds(), 2) << esc("0") << " ";
1332 out
<< "tracker: " << esc("36") << s
.current_tracker
<< esc("0") << "\n";
1335 if (torrent_index
!= active_torrent
) continue;
1339 cache_status cs
= ses
.get_cache_status();
1340 if (cs
.blocks_read
< 1) cs
.blocks_read
= 1;
1341 if (cs
.blocks_written
< 1) cs
.blocks_written
= 1;
1343 out
<< "==== conns: " << sess_stat
.num_peers
1344 << " down: " << esc("32") << add_suffix(sess_stat
.download_rate
) << "/s" << esc("0")
1345 << " (" << esc("32") << add_suffix(sess_stat
.total_download
) << esc("0") << ") "
1346 " up: " << esc("31") << add_suffix(sess_stat
.upload_rate
) << "/s " << esc("0")
1347 << " (" << esc("31") << add_suffix(sess_stat
.total_upload
) << esc("0") << ")"
1348 " waste: " << add_suffix(sess_stat
.total_redundant_bytes
)
1349 << " fail: " << add_suffix(sess_stat
.total_failed_bytes
)
1350 << " unchoked: " << sess_stat
.num_unchoked
<< " / " << sess_stat
.allowed_upload_slots
1351 << " bw queues: (" << sess_stat
.up_bandwidth_queue
1352 << " | " << sess_stat
.down_bandwidth_queue
<< ") "
1353 " write cache hits: " << ((cs
.blocks_written
- cs
.writes
) * 100 / cs
.blocks_written
) << "% "
1354 " read cache hits: " << (cs
.blocks_read_hit
* 100 / cs
.blocks_read
) << "% "
1355 " cache size: " << add_suffix(cs
.cache_size
* 16 * 1024)
1356 << " (" << add_suffix(cs
.read_cache_size
* 16 * 1024) << ")"
1357 " ====" << std::endl
;
1359 if (active_handle
.is_valid())
1361 torrent_handle h
= active_handle
;
1362 torrent_status s
= h
.status();
1364 if ((print_downloads
&& s
.state
!= torrent_status::seeding
)
1366 h
.get_peer_info(peers
);
1368 out
<< "====== " << h
.name() << " ======" << std::endl
;
1370 if (print_peers
&& !peers
.empty())
1371 print_peer_info(out
, peers
);
1373 if (print_downloads
)
1375 h
.get_download_queue(queue
);
1376 std::sort(queue
.begin(), queue
.end(), bind(&partial_piece_info::piece_index
, _1
)
1377 < bind(&partial_piece_info::piece_index
, _2
));
1379 std::vector
<cached_piece_info
> pieces
;
1380 ses
.get_cache_info(h
.info_hash(), pieces
);
1382 for (std::vector
<partial_piece_info
>::iterator i
= queue
.begin();
1383 i
!= queue
.end(); ++i
)
1385 cached_piece_info
* cp
= 0;
1386 std::vector
<cached_piece_info
>::iterator cpi
= std::find_if(pieces
.begin(), pieces
.end()
1387 , bind(&cached_piece_info::piece
, _1
) == i
->piece_index
);
1388 if (cpi
!= pieces
.end()) cp
= &*cpi
;
1390 out
<< to_string(i
->piece_index
, 4) << ": [";
1391 for (int j
= 0; j
< i
->blocks_in_piece
; ++j
)
1393 int index
= peer_index(i
->blocks
[j
].peer
, peers
);
1396 str
[0] = (index
< 10)?'0' + index
:'A' + index
- 10;
1398 #ifdef ANSI_TERMINAL_COLORS
1399 if (cp
&& cp
->blocks
[j
]) out
<< esc("36;7") << str
<< esc("0");
1400 else if (i
->blocks
[j
].bytes_progress
> 0
1401 && i
->blocks
[j
].state
== block_info::requested
)
1403 if (i
->blocks
[j
].num_peers
> 1)
1407 out
<< to_string(i
->blocks
[j
].bytes_progress
/ float(i
->blocks
[j
].block_size
) * 10, 1) << esc("0");
1409 else if (i
->blocks
[j
].state
== block_info::finished
) out
<< esc("32;7") << str
<< esc("0");
1410 else if (i
->blocks
[j
].state
== block_info::writing
) out
<< esc("35;7") << str
<< esc("0");
1411 else if (i
->blocks
[j
].state
== block_info::requested
) out
<< str
;
1414 if (cp
&& cp
->blocks
[j
]) out
<< "c";
1415 else if (i
->blocks
[j
].state
== block_info::finished
) out
<< "#";
1416 else if (i
->blocks
[j
].state
== block_info::writing
) out
<< "+";
1417 else if (i
->blocks
[j
].state
== block_info::requested
) out
<< str
;
1421 char const* piece_state
[4] = {"", "slow", "medium", "fast"};
1422 out
<< "] " << piece_state
[i
->piece_state
];
1423 if (cp
) out
<< (i
->piece_state
> 0?" | ":"") << "cache age: " << (total_milliseconds(time_now() - cp
->last_use
) / 1000.f
);
1427 for (std::vector
<cached_piece_info
>::iterator i
= pieces
.begin()
1428 , end(pieces
.end()); i
!= end
; ++i
)
1430 if (i
->kind
!= cached_piece_info::read_cache
) continue;
1431 out
<< to_string(i
->piece
, 4) << ": [";
1432 for (std::vector
<bool>::iterator k
= i
->blocks
.begin()
1433 , end(i
->blocks
.end()); k
!= end
; ++k
)
1435 #ifdef ANSI_TERMINAL_COLORS
1436 if (*k
) out
<< esc("33;7") << " " << esc("0");
1443 out
<< "] " << "cache age: "
1444 << (total_milliseconds(time_now() - i
->last_use
) / 1000.f
)
1447 out
<< "___________________________________\n";
1450 if (print_file_progress
1451 && s
.state
!= torrent_status::seeding
1452 && h
.has_metadata())
1454 std::vector
<size_type
> file_progress
;
1455 h
.file_progress(file_progress
);
1456 torrent_info
const& info
= h
.get_torrent_info();
1457 for (int i
= 0; i
< info
.num_files(); ++i
)
1459 float progress
= info
.file_at(i
).size
> 0
1460 ?float(file_progress
[i
]) / info
.file_at(i
).size
:1;
1461 if (file_progress
[i
] == info
.file_at(i
).size
)
1462 out
<< progress_bar(1.f
, 100, "32");
1464 out
<< progress_bar(progress
, 100, "33");
1465 out
<< " " << to_string(progress
* 100.f
, 5) << "% "
1466 << add_suffix(file_progress
[i
]) << " "
1467 << info
.file_at(i
).path
.leaf() << "\n";
1470 out
<< "___________________________________\n";
1477 for (std::deque
<std::string
>::iterator i
= events
.begin();
1478 i
!= events
.end(); ++i
)
1485 puts(out
.str().c_str());
1487 if (!monitor_dir
.empty()
1488 && next_dir_scan
< time_now())
1490 scan_dir(monitor_dir
, ses
, handles
, preferred_ratio
1491 , compact_allocation_mode
, save_path
, torrent_upload_limit
1492 , torrent_download_limit
);
1493 next_dir_scan
= time_now() + seconds(poll_interval
);
1497 std::cout
<< "saving session state" << std::endl
;
1499 entry session_state
= ses
.state();
1500 boost::filesystem::ofstream
out(".ses_state"
1501 , std::ios_base::binary
);
1502 out
.unsetf(std::ios_base::skipws
);
1503 bencode(std::ostream_iterator
<char>(out
), session_state
);
1506 #ifndef TORRENT_DISABLE_DHT
1507 std::cout
<< "saving DHT state" << std::endl
;
1508 dht_state
= ses
.dht_state();
1509 boost::filesystem::ofstream
out(".dht_state"
1510 , std::ios_base::binary
);
1511 out
.unsetf(std::ios_base::skipws
);
1512 bencode(std::ostream_iterator
<char>(out
), dht_state
);
1514 std::cout
<< "closing session" << std::endl
;
1516 #ifndef BOOST_NO_EXCEPTIONS
1517 catch (std::exception
& e
)
1519 std::cout
<< e
.what() << "\n";