DUNE-DAQ
DUNE Trigger and Data Acquisition software
Loading...
Searching...
No Matches
transfer_interface_bittorrent.cpp
Go to the documentation of this file.
1
10
11#include <string>
12#include <utility>
13#include <vector>
14
15namespace dunedaq::snbmodules {
16
17// return the name of a torrent status enum
18char const*
19TransferInterfaceBittorrent::state(lt::torrent_status::state_t s)
20{
21 switch (s) {
22 case lt::torrent_status::checking_files:
23 return "checking";
24 case lt::torrent_status::downloading_metadata:
25 return "dl metadata";
26 case lt::torrent_status::downloading:
27 return "downloading";
28 case lt::torrent_status::finished:
29 return "finished";
30 case lt::torrent_status::seeding:
31 return "seeding";
32 case lt::torrent_status::checking_resume_data:
33 return "checking resume";
34 default:
35 return "<>";
36 }
37}
38
40 bool is_client,
41 std::filesystem::path work_dir,
42 const IPFormat& listening_ip)
44 , ses(set_settings(listening_ip, config.get_protocol_options()["port"].get<std::string>()))
45 , m_is_client(is_client)
46 , m_listening_ip(listening_ip)
47 , m_thread([&](std::atomic<bool>& running) { this->do_work(running); })
48{
49 m_work_dir = std::move(work_dir);
50 m_thread.start_working_thread();
51
52 if (config.get_protocol_options().contains("rate_limit")) {
53 m_rate_limit = config.get_protocol_options()["rate_limit"].get<int>();
54 }
55}
56
61
62void
63TransferInterfaceBittorrent::do_work(std::atomic<bool>& running_flag)
64try {
65 bool m_done = false;
66 int finished_torrents = 0;
67 // lt::torrent_handle h;
68
69 FILE* log_file = std::fopen(get_work_dir().append("bittorrent.log").c_str(), "w+");
70
71 TLOG() << "debug : Starting bittorent work on " << m_listening_ip.get_ip_port();
72
73 while (running_flag.load() || save_on_exit) {
74
75 // Save before exit
76 if (save_on_exit && !running_flag.load()) {
77 auto const handles = ses.get_torrents();
78 for (const auto& h : handles) {
79 h.save_resume_data(lt::torrent_handle::only_if_modified | lt::torrent_handle::save_info_dict);
80 }
81 m_done = true;
82 goto done;
83 }
84
85 std::vector<lt::alert*> alerts;
86 ses.pop_alerts(&alerts);
87
88 for (lt::alert const* a : alerts) {
89 static auto const first_ts = a->timestamp();
90
91 if (log_file) {
92 std::fprintf(
93 log_file,
94 "[%ld] %s\n",
95 static_cast<std::int64_t>(duration_cast<std::chrono::milliseconds>(a->timestamp() - first_ts).count()),
96 a->message().c_str());
97 }
98
99 if (auto at = lt::alert_cast<lt::add_torrent_alert>(a)) {
100 // h = at->handle;
102 TLOG() << "debug : Added torrent " << at->torrent_name();
103 }
104
105 if (auto p = lt::alert_cast<lt::torrent_removed_alert>(a)) {
106 lt::torrent_handle h = p->handle;
107 }
108
109 if (auto p = lt::alert_cast<lt::torrent_paused_alert>(a)) {
110 lt::torrent_handle h = p->handle;
111 h.save_resume_data(lt::torrent_handle::save_info_dict);
112 }
113
114 if (auto* p = lt::alert_cast<lt::tracker_list_alert>(a)) {
115 (void)p;
116 // if (h == p->handle)
117 // {
118 // session_state.trackers = std::move(p->trackers);
119 // }
120 }
121
122 // if (auto *p = lt::alert_cast<lt::file_progress_alert>(a))
123 // {
124 // m_filename_to_metadata[h.torrent_file()->name()]->set_bytes_transferred(h.status().total_payload_download);
125 // TLOG() << "debug : Progress: " << h.status().total_payload_download;
126 // }
127
128 // if we receive the finished alert or an error, we're done
129 if (auto p = lt::alert_cast<lt::torrent_finished_alert>(a)) {
130 TLOG() << "debug : Torrent finished " << p->torrent_name();
131 finished_torrents++;
132
133 p->handle.save_resume_data(lt::torrent_handle::only_if_modified | lt::torrent_handle::save_info_dict);
134
135 m_filename_to_metadata[p->torrent_name()]->set_status(status_type::e_status::FINISHED);
136 m_filename_to_metadata[p->torrent_name()]->set_bytes_transferred(
137 m_filename_to_metadata[p->torrent_name()]->get_size());
138
139 if (finished_torrents == m_torrent_num && m_is_client) {
140 m_done = true;
141 }
142 }
143 if (auto p = lt::alert_cast<lt::torrent_error_alert>(a)) {
144 ers::error(BittorrentError(ERS_HERE, p->error.message()));
145
146 finished_torrents++;
147 if (finished_torrents == m_torrent_num && m_is_client) {
148 m_done = true;
149 }
150
151 p->handle.save_resume_data(lt::torrent_handle::only_if_modified | lt::torrent_handle::save_info_dict);
152 }
153
154 // when resume data is ready, save it
155 if (const auto* rd = lt::alert_cast<lt::save_resume_data_alert>(a)) {
156 std::ofstream of(get_work_dir().append(".resume_file_" + rd->params.name), std::ios_base::binary);
157 of.unsetf(std::ios_base::skipws);
158 auto const b = write_resume_data_buf(rd->params);
159 of.write(b.data(), static_cast<int>(b.size()));
160 if (m_done) {
161 goto done;
162 }
163 }
164
165 if (auto e = lt::alert_cast<lt::save_resume_data_failed_alert>(a)) {
166 ers::warning(BittorrentSaveResumeFileError(ERS_HERE, e->message()));
167 if (m_done) {
168 goto done;
169 }
170 }
171
172 if (lt::alert_cast<lt::peer_connect_alert>(a)) {
173 m_peer_num++;
174 m_done = false;
175 }
176
177 if (auto e = lt::alert_cast<lt::peer_error_alert>(a)) {
178 m_peer_num--;
179 // std::this_thread::sleep_for(std::chrono::seconds(1));
180 // Try to reconnect
181 // lt::error_code ec;
182 // h.connect_peer(lt::tcp::endpoint(boost::asio::ip::make_address(config->get_source_ip().get_ip(), ec),
183 // std::uint16_t(config->get_source_ip().get_port())));
184 ers::warning(BittorrentPeerDisconnectedError(ERS_HERE, e->message()));
185 // f_meta.set_error_code("peer error: " + a->message());
186
187 if (m_peer_num == 0 && m_paused == 0 && !m_is_client) {
188 m_done = true;
189 }
190 }
191
192 if (auto e = lt::alert_cast<lt::peer_disconnected_alert>(a)) {
193 m_peer_num--;
194 // wait
195 // std::this_thread::sleep_for(std::chrono::seconds(1));
196 // Try to reconnect
197 // lt::error_code ec;
198 // h.connect_peer(lt::tcp::endpoint(boost::asio::ip::make_address(config->get_source_ip().get_ip(), ec),
199 // std::uint16_t(config->get_source_ip().get_port())));
200 ers::warning(BittorrentPeerDisconnectedError(ERS_HERE, e->message()));
201 if (m_peer_num <= 0 && m_paused == 0 && !m_is_client) {
202 m_done = true;
203 }
204 }
205
206 if (auto st = lt::alert_cast<lt::state_update_alert>(a)) {
207 // TLOG() << "debug : State update alert";
208 if (st->status.empty()) {
209 continue;
210 }
211
212 for (uint64_t i = 0; i < st->status.size(); i++) {
213 lt::torrent_status const& s = st->status[i];
214
215 if (m_filename_to_metadata[s.name]->get_status() != status_type::e_status::PAUSED) {
216
217 switch (s.state) {
218 case lt::torrent_status::checking_files:
220 break;
221 case lt::torrent_status::downloading_metadata:
223 break;
224 case lt::torrent_status::downloading:
226 break;
227 case lt::torrent_status::finished:
229 break;
230 case lt::torrent_status::seeding:
231 if (m_is_client) {
233 } else {
235 }
236 break;
237 case lt::torrent_status::checking_resume_data:
239 break;
240 default:
241 break;
242 }
243
244 // if (s.num_peers == 0)
245 // {
246 // m_filename_to_metadata[s.name].set_status(status_type::e_status::WAITING);
247 // }
248 }
249
250 m_filename_to_metadata[s.name]->set_bytes_transferred(s.total_done);
251
252 TLOG() << "is_client " << m_is_client << " [" << i << "]" << s.name << " " << state(s.state) << ' '
253 << (s.download_payload_rate / 1000) << " kB/s " << (s.total_done / 1000) << " kB ("
254 << (s.progress_ppm / 10000) << "%) " << s.current_tracker << " "
255 << static_cast<std::int64_t>(duration_cast<seconds>(s.next_announce).count()) << "s (" << s.num_peers
256 << " peers) " << "\n";
257
258 // h.post_trackers();
259
260 // for (lt::announce_entry const &ae : session_state.trackers)
261 // {
262 // std::cout << ae.url << " ";
263 // if (ae.verified)
264 // {
265 // std::cout << "OK ";
266 // }
267 // else
268 // {
269 // std::cout << "-- ";
270 // }
271 // std::cout << ae.tier << "\n";
272 // }
273 // auto &peers = client_state.peers;
274 // if (print_peers && !peers.empty())
275 // {
276 // using lt::peer_info;
277 // // sort connecting towards the bottom of the list, and by peer_id
278 // // otherwise, to keep the list as stable as possible
279 // std::sort(peers.begin(), peers.end(), [](peer_info const &lhs, peer_info const &rhs)
280 // {
281 // {
282 // bool const l = bool(lhs.flags & peer_info::connecting);
283 // bool const r = bool(rhs.flags & peer_info::connecting);
284 // if (l != r) return l < r;
285 // }
286
287 // {
288 // bool const l = bool(lhs.flags & peer_info::handshake);
289 // bool const r = bool(rhs.flags & peer_info::handshake);
290 // if (l != r) return l < r;
291 // }
292
293 // return lhs.pid < rhs.pid; });
294
295 // print_peer_info(out, peers, 10);
296 // if (print_peers_legend)
297 // {
298 // print_peer_legend(out, 10);
299 // }
300 // }
301
302 // if (print_trackers)
303 // {
304 // snprintf(str, sizeof(str), "next_announce: %4" PRId64 " | current tracker: %s\x1b[K\n",
305 // std::int64_t(duration_cast<seconds>(s.next_announce).count()), s.current_tracker.c_str()); out += str;
306 // pos += 1;
307 // h.post_trackers();
308 // for (lt::announce_entry const &ae : client_state.trackers)
309 // {
310 // std::snprintf(str, sizeof(str), "%2d %-55s %s\x1b[K\n", ae.tier, ae.url.c_str(), ae.verified ? "OK
311 // " : "- "); out += str; pos += 1; int idx = 0; for (auto const &ep : ae.endpoints)
312 // {
313 // ++idx;
314 // if (pos + 1 >= terminal_height)
315 // {
316 // break;
317 // }
318 // if (!ep.enabled)
319 // {
320 // continue;
321 // }
322 // for (lt::protocol_version const v : {lt::protocol_version::V1, lt::protocol_version::V2})
323 // {
324 // if (!s.info_hashes.has(v))
325 // {
326 // continue;
327 // }
328 // auto const &av = ep.info_hashes[v];
329
330 // std::snprintf(str, sizeof(str), " [%2d] %s fails: %-3d (%-3d) %s %5d \"%s\" %s\x1b[K\n",
331 // idx, v == lt::protocol_version::V1 ? "v1" : "v2", av.fails, ae.fail_limit,
332 // to_string(int(total_seconds(av.next_announce - now)), 8).c_str(), av.min_announce > now ?
333 // int(total_seconds(av.min_announce - now)) : 0, av.last_error ?
334 // av.last_error.message().c_str() : "", av.message.c_str()); out += str; pos += 1;
335 // // we only need to show this error once, not for every
336 // // endpoint
337 // if (av.last_error == boost::asio::error::host_not_found)
338 // {
339 // goto tracker_done;
340 // }
341 // }
342 // }
343 // tracker_done:
344
345 // if (pos + 1 >= terminal_height)
346 // {
347 // break;
348 // }
349 // }
350 // }
351 }
352 // std::cout << "\x1b[K";
353 // std::cout.flush();
354 }
355 }
356 std::this_thread::sleep_for(std::chrono::milliseconds(200));
357
358 // ask the session to post a state_update_alert, to update our
359 // state output for the torrent
360 ses.post_torrent_updates();
361 ses.post_session_stats();
362
363 // save resume data once every 30 seconds
364 // if (clk::now() - last_save_resume > std::chrono::seconds(30))
365 // {
366 // h.save_resume_data(lt::torrent_handle::only_if_modified | lt::torrent_handle::save_info_dict);
367 // last_save_resume = clk::now();
368 // }
369 }
370done:
371
372 for (auto& [k, s] : m_filename_to_metadata) {
373 if (!m_is_client) {
374 s->set_status(status_type::e_status::FINISHED);
375 // deleting torrents files
376 std::filesystem::remove(get_work_dir().append(k + ".torrent"));
377 } else if (s->get_status() != status_type::e_status::FINISHED) {
378 s->set_status(status_type::e_status::ERROR);
379 s->set_error_code("Transfer interrupted");
380 }
381 }
382
383 TLOG() << "\nBittorent session done, shutting down";
384 if (log_file) {
385 std::fclose(log_file);
386 }
387 return;
388} catch (std::exception& e) {
389 // TODO: handle error
390 std::cerr << "Error: " << e.what() << std::endl;
391}
392
393bool
394TransferInterfaceBittorrent::add_magnet(lt::string_view uri, const std::filesystem::path& dest)
395try {
396 TLOG() << "debug : loading parameters from magnet " << uri.to_string();
397 lt::error_code ec;
398 lt::add_torrent_params p = lt::parse_magnet_uri(uri.to_string(), ec);
399
400 if (ec) {
401 ers::error(BittorrentInvalidMagnetLinkError(ERS_HERE, uri.to_string()));
402 return false;
403 }
404
405 // std::vector<char> resume_data;
406 // if (load_file(resume_file(p.info_hashes, session_number), resume_data))
407 // {
408 // p = lt::read_resume_data(resume_data, ec);
409 // if (ec)
410 // {
411 // std::printf(" failed to load resume data: %s\n", ec.message().c_str());
412 // }
413 // }
414
415 set_torrent_params(p, dest);
416
417 TLOG() << "debug : adding torrent";
418 ses.async_add_torrent(std::move(p));
419 return true;
420} catch (lt::system_error const& e) {
421 ers::error(BittorrentInvalidMagnetLinkError(ERS_HERE, e.code().message()));
422 return false;
423}
424
425// return magnet url
426std::string
427TransferInterfaceBittorrent::add_torrent(const std::string& torrent, const std::filesystem::path& dest)
428try {
429 using lt::storage_mode_t;
430
431 TLOG() << "debug : [" << m_torrent_num << "] " << torrent;
432
433 // lt::error_code ec;
434 lt::add_torrent_params p = lt::load_torrent_file(torrent);
435
436 // std::vector<char> resume_data;
437 // if (load_file(resume_file(atp.info_hashes, session_number), resume_data))
438 // {
439 // lt::add_torrent_params rd = lt::read_resume_data(resume_data, ec);
440 // if (ec)
441 // {
442 // std::printf(" failed to load resume data: %s\n", ec.message().c_str());
443 // }
444 // else
445 // {
446 // atp = rd;
447 // }
448 // }
449
450 set_torrent_params(p, dest);
451 std::string magnet = lt::make_magnet_uri(p);
452
453 ses.async_add_torrent(std::move(p));
454
455 return magnet;
456} catch (lt::system_error const& e) {
457 ers::error(BittorrentInvalidTorrentFileError(ERS_HERE, e.code().message()));
458 return "";
459}
460
461void
462TransferInterfaceBittorrent::set_torrent_params(lt::add_torrent_params& p, const std::filesystem::path& dest)
463{
464 TLOG() << "debug : setting torrent parameters";
465
466 bool seed_mode = !m_is_client;
467 bool super_seeding = false;
468 bool upload_mode = !m_is_client;
469
470 bool sequential_mode = true;
471 int max_connections_per_torrent = 100;
472 std::string save_path = dest;
473 // limits in bytes per seconds hqndle by session ?
474 int torrent_upload_limit = -1;
475 int torrent_download_limit = m_rate_limit;
476
477 p.max_connections = max_connections_per_torrent;
478 p.max_uploads = -1;
479 p.upload_limit = torrent_upload_limit;
480 p.download_limit = torrent_download_limit;
481 // atp.flags &= ~lt::torrent_flags::duplicate_is_error;
482
483 // not auto managed
484 p.flags &= ~lt::torrent_flags::paused;
485 p.flags &= ~lt::torrent_flags::auto_managed;
486
487 if (super_seeding) {
488 p.flags |= lt::torrent_flags::super_seeding;
489 } else {
490 p.flags &= ~lt::torrent_flags::super_seeding;
491 }
492
493 if (seed_mode) {
494 p.flags |= lt::torrent_flags::seed_mode;
495 } else {
496 p.flags &= ~lt::torrent_flags::seed_mode;
497 }
498
499 if (upload_mode) {
500 p.flags |= lt::torrent_flags::upload_mode;
501 } else {
502 p.flags &= ~lt::torrent_flags::upload_mode;
503 }
504
505 if (sequential_mode) {
506 p.flags |= lt::torrent_flags::sequential_download;
507 } else {
508 p.flags &= ~lt::torrent_flags::sequential_download;
509 }
510
511 p.save_path = save_path;
512 p.storage_mode = lt::storage_mode_allocate;
513}
514
515lt::session_params
516TransferInterfaceBittorrent::set_settings(const IPFormat& listen_interface, const std::string& listen_port)
517{
518 lt::session_params sp;
519 auto& p = sp.settings;
520
521 sp.disk_io_constructor = lt::default_disk_io_constructor;
522
523 int block_size = 1024 * 1024;
524 std::string outgoing_interface = listen_interface.get_ip();
525 std::string listen_interfaces = listen_interface.get_ip() + ":" + listen_port;
526
527 p.set_bool(lt::settings_pack::enable_dht, false);
528 p.set_int(lt::settings_pack::auto_manage_interval, 60);
529 p.set_int(lt::settings_pack::auto_manage_startup, 1);
530 p.set_int(lt::settings_pack::min_reconnect_time, 1);
531 p.set_int(lt::settings_pack::max_failcount, 10);
532
533 // p.set_str(lt::settings_pack::dht_bootstrap_nodes, "192.168.0.105:5001,192.168.0.106:5002");
534 // p.set_bool(lt::settings_pack::use_dht_as_fallback, false);
535 // p.set_bool(lt::settings_pack::dht_prefer_verified_node_ids, false);
536 // p.set_bool(lt::settings_pack::dht_restrict_routing_ips, false);
537 // p.set_bool(lt::settings_pack::dht_restrict_search_ips, false);
538
539 p.set_str(lt::settings_pack::outgoing_interfaces, outgoing_interface);
540 p.set_bool(lt::settings_pack::strict_end_game_mode, false);
541 // p.set_bool(lt::settings_pack::low_prio_disk, false);
542 p.set_bool(lt::settings_pack::smooth_connects, false);
543 p.set_bool(lt::settings_pack::allow_multiple_connections_per_ip, true);
544 p.set_bool(lt::settings_pack::announce_to_all_tiers, true);
545 p.set_bool(lt::settings_pack::announce_to_all_trackers, true);
546 p.set_bool(lt::settings_pack::auto_sequential, true);
547 // p.set_bool(lt::settings_pack::coalesce_reads, true);
548 // p.set_bool(lt::settings_pack::coalesce_writes, true);
549 // p.set_bool(lt::settings_pack::contiguous_recv_buffer, true);
550 p.set_bool(lt::settings_pack::incoming_starts_queued_torrents, true);
551
552 p.set_bool(lt::settings_pack::enable_incoming_tcp, true);
553 p.set_bool(lt::settings_pack::enable_outgoing_tcp, true);
554 p.set_bool(lt::settings_pack::enable_incoming_utp, false);
555 p.set_bool(lt::settings_pack::enable_outgoing_utp, false);
556 p.set_bool(lt::settings_pack::enable_lsd, false);
557 p.set_bool(lt::settings_pack::enable_natpmp, false);
558 p.set_bool(lt::settings_pack::enable_upnp, false);
559 p.set_bool(lt::settings_pack::prefer_rc4, false);
560 p.set_bool(lt::settings_pack::prefer_udp_trackers, true);
561 p.set_bool(lt::settings_pack::rate_limit_ip_overhead, false);
562 // p.set_bool(lt::settings_pack::rate_limit_utp, false);
563
564 p.set_int(lt::settings_pack::aio_threads, 1);
565 // p.set_int(lt::settings_pack::network_threads, 1);
566 p.set_int(lt::settings_pack::hashing_threads, 1);
567 p.set_int(lt::settings_pack::disk_io_read_mode, 3);
568 p.set_int(lt::settings_pack::disk_io_write_mode, 3);
569 p.set_int(lt::settings_pack::allowed_enc_level, 3);
570 p.set_int(lt::settings_pack::allowed_fast_set_size, 5);
571 p.set_int(lt::settings_pack::seed_choking_algorithm, 1);
572 p.set_int(lt::settings_pack::choking_algorithm, 0);
573 p.set_int(lt::settings_pack::in_enc_policy, 2);
574 p.set_int(lt::settings_pack::out_enc_policy, 2);
575 p.set_int(lt::settings_pack::mixed_mode_algorithm, 0);
576 p.set_int(lt::settings_pack::suggest_mode, 0);
577
578 p.set_int(lt::settings_pack::close_file_interval, 0);
579 p.set_int(lt::settings_pack::inactivity_timeout, 10);
580 p.set_int(lt::settings_pack::request_queue_time, 50);
581 p.set_int(lt::settings_pack::peer_timeout, 20);
582 p.set_int(lt::settings_pack::request_timeout, 10);
583 p.set_int(lt::settings_pack::predictive_piece_announce, 20);
584 p.set_int(lt::settings_pack::whole_pieces_threshold, 20);
585 p.set_int(lt::settings_pack::mmap_file_size_cutoff, 0);
586
587 // limits in bytes per seconds
588 p.set_int(lt::settings_pack::upload_rate_limit, 0);
589 p.set_int(lt::settings_pack::download_rate_limit, 0);
590 // p.set_int(lt::settings_pack::local_download_rate_limit, 0);
591 // p.set_int(lt::settings_pack::local_upload_rate_limit, 0);
592 p.set_int(lt::settings_pack::unchoke_slots_limit, -1);
593 p.set_int(lt::settings_pack::max_failcount, 3);
594 p.set_int(lt::settings_pack::max_http_recv_buffer_size, 1024 * 1024 * 8);
595 p.set_int(lt::settings_pack::max_rejects, 20);
596 p.set_int(lt::settings_pack::max_queued_disk_bytes, 1024 * 1024 * 1024);
597
598 p.set_int(lt::settings_pack::read_cache_line_size, 512);
599 // p.set_int(lt::settings_pack::cache_buffer_chunk_size, 512);
600 // p.set_int(lt::settings_pack::cache_expiry, 400);
601 p.set_int(lt::settings_pack::cache_size_volatile, 128);
602 p.set_int(lt::settings_pack::checking_mem_usage, 1024);
603 // p.set_bool(lt::settings_pack::use_read_cache, true);
604 // p.set_bool(lt::settings_pack::use_write_cache, true);
605 // p.set_bool(lt::settings_pack::use_disk_read_ahead, true);
606 p.set_bool(lt::settings_pack::use_parole_mode, false);
607 // p.set_bool(lt::settings_pack::guided_read_cache, true);
608 // p.set_bool(lt::settings_pack::volatile_read_cache, false);
609 p.set_int(lt::settings_pack::tracker_completion_timeout, 30);
610 p.set_int(lt::settings_pack::tracker_receive_timeout, 30);
611 p.set_int(lt::settings_pack::stop_tracker_timeout, 30);
612 p.set_int(lt::settings_pack::tracker_backoff, 250);
613 p.set_int(lt::settings_pack::tracker_maximum_response_length, 1024 * 1024 * 8);
614 p.set_bool(lt::settings_pack::validate_https_trackers, false);
615 p.set_int(lt::settings_pack::alert_mask, lt::alert_category::all);
616
617 p.set_str(lt::settings_pack::listen_interfaces, listen_interfaces);
618
619 if (m_is_client) {
620 // p.set_str(lt::settings_pack::user_agent, "DuneTorrentClient");
621 p.set_bool(lt::settings_pack::piece_extent_affinity, true);
622 p.set_bool(lt::settings_pack::seeding_outgoing_connections, false);
623
624 p.set_int(lt::settings_pack::tick_interval, 500);
625 p.set_int(lt::settings_pack::torrent_connect_boost, 255);
626
627 p.set_int(lt::settings_pack::connection_speed, 0);
628 p.set_int(lt::settings_pack::active_seeds, 0);
629 p.set_int(lt::settings_pack::active_downloads, 10);
630 p.set_int(lt::settings_pack::active_checking, 10);
631 p.set_int(lt::settings_pack::active_limit, 10);
632 p.set_int(lt::settings_pack::active_tracker_limit, 10);
633 p.set_int(lt::settings_pack::connections_limit, 10);
634 // p.set_int(lt::settings_pack::half_open_limit, 500);
635 p.set_int(lt::settings_pack::file_pool_size, 10);
636 p.set_int(lt::settings_pack::listen_queue_size, 10);
637 p.set_int(lt::settings_pack::max_allowed_in_request_queue, 50000);
638 p.set_int(lt::settings_pack::max_out_request_queue, 50000);
639 p.set_int(lt::settings_pack::dht_upload_rate_limit, block_size * 10);
640
641 p.set_int(lt::settings_pack::write_cache_line_size, 512);
642 // p.set_int(lt::settings_pack::cache_size, 1024 * 1);
643 // p.set_bool(lt::settings_pack::use_disk_cache_pool, true);
644 // p.set_bool(lt::settings_pack::allow_partial_disk_writes, false);
645
646 p.set_int(lt::settings_pack::send_buffer_watermark_factor, 50);
647 p.set_int(lt::settings_pack::send_buffer_low_watermark, 1024 * 10);
648 p.set_int(lt::settings_pack::send_buffer_watermark, 1024 * 500);
649 p.set_int(lt::settings_pack::send_socket_buffer_size, 1024 * 512);
650 p.set_int(lt::settings_pack::recv_socket_buffer_size, 1024 * 1024 * 1024);
651
652 p.set_bool(lt::settings_pack::no_atime_storage, true);
653 p.set_bool(lt::settings_pack::enable_set_file_valid_data, false);
654 p.set_int(lt::settings_pack::disk_write_mode, 1);
655 p.set_bool(lt::settings_pack::disable_hash_checks, false);
656 } else {
657
658 // p.set_str(lt::settings_pack::user_agent, "DuneTorrentSeeder");
659 p.set_bool(lt::settings_pack::piece_extent_affinity, false);
660 p.set_bool(lt::settings_pack::seeding_outgoing_connections, true);
661
662 p.set_int(lt::settings_pack::tick_interval, 150);
663 p.set_int(lt::settings_pack::torrent_connect_boost, 255);
664
665 p.set_int(lt::settings_pack::connection_speed, 200);
666 p.set_int(lt::settings_pack::active_seeds, 1000);
667 p.set_int(lt::settings_pack::active_downloads, 0);
668 p.set_int(lt::settings_pack::active_checking, 1000);
669 p.set_int(lt::settings_pack::active_limit, 2000);
670 p.set_int(lt::settings_pack::active_tracker_limit, 2000);
671 p.set_int(lt::settings_pack::connections_limit, 8000);
672 // p.set_int(lt::settings_pack::half_open_limit, 50);
673 p.set_int(lt::settings_pack::file_pool_size, 20);
674 p.set_int(lt::settings_pack::listen_queue_size, 3000);
675 p.set_int(lt::settings_pack::max_allowed_in_request_queue, 2000);
676 p.set_int(lt::settings_pack::max_out_request_queue, 2000);
677 p.set_int(lt::settings_pack::dht_upload_rate_limit, block_size * 8);
678
679 p.set_int(lt::settings_pack::write_cache_line_size, 128);
680 // p.set_int(lt::settings_pack::cache_size, 1024 * 50);
681 // p.set_bool(lt::settings_pack::use_disk_cache_pool, false);
682 // p.set_bool(lt::settings_pack::allow_partial_disk_writes, false);
683
684 p.set_int(lt::settings_pack::send_buffer_watermark_factor, 150);
685 p.set_int(lt::settings_pack::send_buffer_low_watermark, 1024 * 10);
686 p.set_int(lt::settings_pack::send_buffer_watermark, 1024 * 1024 * 1024);
687 p.set_int(lt::settings_pack::send_socket_buffer_size, 1024 * 1024 * 1024);
688 p.set_int(lt::settings_pack::recv_socket_buffer_size, 1024 * 512);
689
690 // p.set_bool(lt::settings_pack::no_atime_storage, true);
691 // p.set_bool(lt::settings_pack::enable_set_file_valid_data, false);
692 // p.set_int(lt::settings_pack::disk_write_mode, 1);
693 // p.set_bool(lt::settings_pack::disable_hash_checks, false);
694 }
695
696 return sp;
697}
698
699std::vector<char>
701{
702 std::fstream in;
703 in.exceptions(std::ifstream::failbit);
704 in.open(filename.c_str(), std::ios_base::in | std::ios_base::binary);
705 in.seekg(0, std::ios_base::end);
706 size_t const size = static_cast<size_t>(in.tellg());
707 in.seekg(0, std::ios_base::beg);
708 std::vector<char> ret(size);
709 in.read(ret.data(), static_cast<int>(ret.size()));
710 return ret;
711}
712
713std::string
715{
716 if (f.empty()) {
717 return f;
718 }
719
720 if (f == "/") {
721 return "";
722 }
723
724 auto len = f.size();
725 // if the last character is / or \ ignore it
726 if (f[len - 1] == '/' || f[len - 1] == '\\') {
727 --len;
728 }
729 while (len > 0) {
730 --len;
731 if (f[len] == '/' || f[len] == '\\') {
732 break;
733 }
734 }
735
736 if (f[len] == '/' || f[len] == '\\') {
737 ++len;
738 }
739 return std::string(f.c_str(), len);
740}
741
742// do not include files and folders whose
743// name starts with a .
744bool
746{
747 if (f.empty()) {
748 return false;
749 }
750
751 char const* first = f.c_str();
752 char const* sep = strrchr(first, '/');
753
754 // if there is no parent path, just set 'sep'
755 // to point to the filename.
756 // if there is a parent path, skip the '/' character
757 if (sep == nullptr) {
758 sep = first;
759 } else {
760 ++sep; // NOLINT
761 }
762
763 // return false if the first character of the filename is a .
764 if (sep[0] == '.') // NOLINT
765 {
766 return false;
767 }
768
769 return true;
770}
771
772bool
773TransferInterfaceBittorrent::make_torrent(std::filesystem::path full_path,
774 int piece_size,
775 const std::string& tracker,
776 const std::string& outfile)
777try {
778 std::string creator_str = "libtorrent";
779 std::string comment_str;
780
781 std::vector<std::string> web_seeds;
782 std::vector<std::string> trackers;
783 std::vector<std::string> collections;
784 std::vector<lt::sha1_hash> similar;
785 lt::create_flags_t flags = {};
786 std::string root_cert;
787
788 flags |= lt::create_torrent::v2_only;
789 flags |= lt::create_torrent::modification_time;
790
791 lt::file_storage fs;
792 if (full_path.is_relative()) {
793 full_path = std::filesystem::absolute(full_path);
794 }
795
796 lt::add_files(fs, full_path.string(), file_filter, flags);
797 if (fs.num_files() == 0) {
798 std::cerr << "no files specified.\n";
799 return true;
800 }
801
802 lt::create_torrent t(fs, piece_size, flags);
803 int tier = 0;
804 for (std::string const& tr : trackers) {
805 if (tr == "-") {
806 ++tier;
807 } else {
808 t.add_tracker(tr, tier);
809 }
810 }
811
812 t.add_tracker(tracker);
813 t.set_priv(false);
814
815 auto const num = t.num_pieces();
816 lt::set_piece_hashes(
817 t, branch_path(full_path), [num](lt::piece_index_t const p) { std::cerr << "\r" << p << "/" << num; });
818
819 std::cerr << "\n";
820 t.set_creator(creator_str.c_str());
821 if (!comment_str.empty()) {
822 t.set_comment(comment_str.c_str());
823 }
824
825 // create the torrent and print it to stdout
826 std::vector<char> torrent;
827
828 // Silent the output for the logs
829 // std::cout.setstate(std::ios_base::failbit);
830 lt::bencode(back_inserter(torrent), t.generate());
831 // std::cout.clear();
832
833 if (!outfile.empty()) {
834 std::fstream out;
835 out.exceptions(std::ifstream::failbit);
836 out.open(outfile.c_str(), std::ios_base::out | std::ios_base::binary);
837 out.write(torrent.data(), static_cast<int>(torrent.size()));
838 } else {
839 // TODO Aug-14-2022 Leo Joly leo.vincent.andre.joly@cern.ch : Add error code
840 // std::cout.write(torrent.data(), int(torrent.size()));
841 return false;
842 }
843
844 return true;
845} catch (std::exception& e) {
846 std::cerr << "ERROR: " << e.what() << "\n";
847 return false;
848}
849
850void
851TransferInterfaceBittorrent::generate_torrents_files(const std::filesystem::path& dest, const std::string& tracker)
852{
853 for (const auto& f_meta : get_transfer_options().get_transfers_meta()) {
854 std::filesystem::path tmp = dest;
855 make_torrent(f_meta->get_file_path(),
856 static_cast<int>(pow(2, 23)),
857 tracker,
858 tmp.append(f_meta->get_file_name() + ".torrent").string());
859 }
860}
861
862bool
864{
865 TLOG() << "debug : uploading " << f_meta.get_file_name();
866
867 if (add_torrent(get_work_dir().append(f_meta.get_file_name() + ".torrent"),
868 f_meta.get_file_path().remove_filename()) == "") {
869 f_meta.set_error_code("failed to add torrent to session");
870 return false;
871 }
872
873 m_filename_to_metadata[f_meta.get_file_name()] = &f_meta;
874 return true;
875}
876
877bool
879{
880 TLOG() << "debug : starting download " << f_meta.get_file_name();
881
882 // need to add before adding magnet because can instant access after adding magnet
883 m_filename_to_metadata[f_meta.get_file_name()] = &f_meta;
884
885 if (add_magnet(f_meta.get_magnet_link(), dest)) {
886 TLOG() << "debug : added magnet passed ";
887 } else {
888 // erasing from map because we failed to add magnet
890 f_meta.set_error_code("failed to add magnet link to session");
891 return false;
892 }
893
894 // add_torrent(get_work_dir().append(f_meta.get_file_name() + ".torrent"), get_work_dir().append(".."));
895 return true;
896}
897
898bool
900{
901 auto handles = ses.get_torrents();
902 for (const auto& h : handles) {
903 if (h.torrent_file()->name() == f_meta.get_file_name()) {
904 m_paused++;
905 h.pause(lt::torrent_handle::graceful_pause);
906 TLOG() << "debug : pausing " << f_meta.get_file_name() << " and saving pause data in " << get_work_dir().string()
907 << "/.resume_file_" << f_meta.get_file_name();
908 break;
909 }
910 }
911
912 return true;
913}
914
915bool
917{
918
919 bool found = false;
920 auto const handles = ses.get_torrents();
921 for (const auto& h : handles) {
922 if (h.torrent_file()->name() == f_meta.get_file_name()) {
923 m_paused--;
924 h.resume();
925 // lt::error_code ec;
926 // TODO
927 // h.connect_peer(lt::tcp::endpoint(boost::asio::ip::make_address("192.168.0.106", ec), std::uint16_t(5010)));
928 // if (!peer.empty())
929 // {
930 // auto port = peer.find_last_of(':');
931 // if (port != std::string::npos)
932 // {
933 // peer[port++] = '\0';
934 // char const* ip = peer.data();
935 // int const peer_port = atoi(peer.data() + port);
936 // error_code ec;
937 // if (peer_port > 0)
938 // {
939 // h.connect_peer(tcp::endpoint(asio::ip::make_address(ip, ec), std::uint16_t(peer_port)));
940 // }
941 // }
942 // }
943 found = true;
944 break;
945 }
946 }
947
948 if (!found) {
949 // load resume data from disk and pass it in as we add the magnet link
950 auto buf = load_file(get_work_dir().append(".resume_file" + f_meta.get_file_name()));
951 lt::add_torrent_params atp;
952
953 if (buf.size()) {
954 atp = lt::read_resume_data(buf);
955 } else {
956 ers::error(BittorrentLoadResumeFileError(ERS_HERE, f_meta.get_file_name()));
957 f_meta.set_error_code("failed to load resume data");
958 return false;
959 }
960
961 m_filename_to_metadata[f_meta.get_file_name()] = &f_meta;
962 ses.async_add_torrent(std::move(atp));
963 }
964
965 return true;
966}
967
968bool
970{
971 auto const handles = ses.get_torrents();
972 for (const auto& h : handles) {
973 if (h.torrent_file()->name() == f_meta.get_file_name()) {
974 // Remove torrent from session
975 ses.remove_torrent(h);
976 break;
977 }
978 }
979
980 // wait for the session to remove the torrent
981 std::this_thread::sleep_for(std::chrono::seconds(1));
983
984 // remove resume data
985 std::filesystem::remove(get_work_dir().append(".resume_file" + f_meta.get_file_name()));
986
987 // remove torrent file if uploader or file if downloader
988 if (!m_is_client) {
989 std::filesystem::remove(get_work_dir().append(f_meta.get_file_name() + ".torrent"));
990 } else {
991 std::filesystem::remove(get_work_dir().append(f_meta.get_file_name()));
992 }
993
994 return true;
995}
996
997// TODO necessary ?
998bool
1000{
1001 (void)f_meta;
1002 return true;
1003}
1004
1005// Custom disk IO interface
1006// struct temp_storage
1007// {
1008// explicit temp_storage(lt::file_storage const &fs) : m_files(fs) {}
1009
1010// lt::span<char const> readv(lt::peer_request const r, lt::storage_error &ec) const
1011// {
1012// auto const i = m_file_data.find(r.piece);
1013// if (i == m_file_data.end())
1014// {
1015// ec.operation = lt::operation_t::file_read;
1016// ec.ec = boost::asio::error::eof;
1017// return {};
1018// }
1019// if (int(i->second.size()) <= r.start)
1020// {
1021// ec.operation = lt::operation_t::file_read;
1022// ec.ec = boost::asio::error::eof;
1023// return {};
1024// }
1025// return {i->second.data() + r.start, std::min(r.length, int(i->second.size()) - r.start)};
1026// }
1027// void writev(lt::span<char const> const b, lt::piece_index_t const piece, int const offset)
1028// {
1029// auto &data = m_file_data[piece];
1030// if (data.empty())
1031// {
1032// // allocate the whole piece, otherwise we'll invalidate the pointers
1033// // we have returned back to libtorrent
1034// int const size = piece_size(piece);
1035// data.resize(std::size_t(size));
1036// }
1037// TORRENT_ASSERT(offset + b.size() <= int(data.size()));
1038// std::memcpy(data.data() + offset, b.data(), std::size_t(b.size()));
1039// }
1040// lt::sha1_hash hash(lt::piece_index_t const piece, lt::span<lt::sha256_hash> const block_hashes, lt::storage_error
1041// &ec) const
1042// {
1043// auto const i = m_file_data.find(piece);
1044// if (i == m_file_data.end())
1045// {
1046// ec.operation = lt::operation_t::file_read;
1047// ec.ec = boost::asio::error::eof;
1048// return {};
1049// }
1050// if (!block_hashes.empty())
1051// {
1052// int const piece_size2 = m_files.piece_size2(piece);
1053// int const blocks_in_piece2 = m_files.blocks_in_piece2(piece);
1054// char const *buf = i->second.data();
1055// std::int64_t offset = 0;
1056// for (int k = 0; k < blocks_in_piece2; ++k)
1057// {
1058// lt::hasher256 h2;
1059// std::ptrdiff_t const len2 = std::min(lt::default_block_size, int(piece_size2 - offset));
1060// h2.update({buf, len2});
1061// buf += len2;
1062// offset += len2;
1063// block_hashes[k] = h2.final();
1064// }
1065// }
1066// return lt::hasher(i->second).final();
1067// }
1068// lt::sha256_hash hash2(lt::piece_index_t const piece, int const offset, lt::storage_error &ec)
1069// {
1070// auto const i = m_file_data.find(piece);
1071// if (i == m_file_data.end())
1072// {
1073// ec.operation = lt::operation_t::file_read;
1074// ec.ec = boost::asio::error::eof;
1075// return {};
1076// }
1077
1078// int const piece_size = m_files.piece_size2(piece);
1079
1080// std::ptrdiff_t const len = std::min(lt::default_block_size, piece_size - offset);
1081
1082// lt::span<char const> b = {i->second.data() + offset, len};
1083// return lt::hasher256(b).final();
1084// }
1085
1086// private:
1087// int piece_size(lt::piece_index_t piece) const
1088// {
1089// int const num_pieces = static_cast<int>((m_files.total_size() + m_files.piece_length() - 1) /
1090// m_files.piece_length()); return static_cast<int>(piece) < num_pieces - 1
1091// ? m_files.piece_length()
1092// : static_cast<int>(m_files.total_size() - std::int64_t(num_pieces - 1) * m_files.piece_length());
1093// }
1094
1095// lt::file_storage const &m_files;
1096// std::map<lt::piece_index_t, std::vector<char>> m_file_data;
1097// };
1098
1099// lt::storage_index_t pop(std::vector<lt::storage_index_t> &q)
1100// {
1101// TORRENT_ASSERT(!q.empty());
1102// lt::storage_index_t const ret = q.back();
1103// q.pop_back();
1104// return ret;
1105// }
1106
1107// struct temp_disk_io final : lt::disk_interface, lt::buffer_allocator_interface
1108// {
1109// explicit temp_disk_io(lt::io_context &ioc) : m_ioc(ioc) {}
1110
1111// void settings_updated() override {}
1112
1113// lt::storage_holder new_torrent(lt::storage_params const &params, std::shared_ptr<void> const &) override
1114// {
1115// lt::storage_index_t const idx = m_free_slots.empty()
1116// ? m_torrents.end_index()
1117// : pop(m_free_slots);
1118// auto storage = std::make_unique<temp_storage>(params.files);
1119// if (idx == m_torrents.end_index())
1120// {
1121// m_torrents.emplace_back(std::move(storage));
1122// }
1123// else
1124// {
1125// m_torrents[idx] = std::move(storage);
1126// }
1127// return lt::storage_holder(idx, *this);
1128// }
1129
1130// void remove_torrent(lt::storage_index_t const idx) override
1131// {
1132// m_torrents[idx].reset();
1133// m_free_slots.push_back(idx);
1134// }
1135
1136// void abort(bool) override {}
1137
1138// void async_read(lt::storage_index_t storage, lt::peer_request const &r, std::function<void(lt::disk_buffer_holder
1139// block, lt::storage_error const &se)> handler, lt::disk_job_flags_t) override
1140// {
1141// // this buffer is owned by the storage. It will remain valid for as
1142// // long as the torrent remains in the session. We don't need any lifetime
1143// // management of it.
1144// lt::storage_error error;
1145// lt::span<char const> b = m_torrents[storage]->readv(r, error);
1146
1147// post(m_ioc, [handler, error, b, this]
1148// { handler(lt::disk_buffer_holder(*this, const_cast<char *>(b.data()), int(b.size())), error); });
1149// }
1150
1151// bool async_write(lt::storage_index_t storage, lt::peer_request const &r, char const *buf,
1152// std::shared_ptr<lt::disk_observer>, std::function<void(lt::storage_error const &)> handler, lt::disk_job_flags_t)
1153// override
1154// {
1155// lt::span<char const> const b = {buf, r.length};
1156
1157// m_torrents[storage]->writev(b, r.piece, r.start);
1158
1159// post(m_ioc, [=]
1160// { handler(lt::storage_error()); });
1161// return false;
1162// }
1163
1164// void async_hash(lt::storage_index_t storage, lt::piece_index_t const piece, lt::span<lt::sha256_hash>
1165// block_hashes, lt::disk_job_flags_t, std::function<void(lt::piece_index_t, lt::sha1_hash const &,
1166// lt::storage_error const &)> handler) override
1167// {
1168// lt::storage_error error;
1169// lt::sha1_hash const hash = m_torrents[storage]->hash(piece, block_hashes, error);
1170// post(m_ioc, [=]
1171// { handler(piece, hash, error); });
1172// }
1173
1174// void async_hash2(lt::storage_index_t storage, lt::piece_index_t const piece, int const offset,
1175// lt::disk_job_flags_t, std::function<void(lt::piece_index_t, lt::sha256_hash const &, lt::storage_error const &)>
1176// handler) override
1177// {
1178// lt::storage_error error;
1179// lt::sha256_hash const hash = m_torrents[storage]->hash2(piece, offset, error);
1180// post(m_ioc, [=]
1181// { handler(piece, hash, error); });
1182// }
1183
1184// void async_move_storage(lt::storage_index_t, std::string p, lt::move_flags_t, std::function<void(lt::status_t,
1185// std::string const &, lt::storage_error const &)> handler) override
1186// {
1187// post(m_ioc, [=]
1188// { handler(lt::status_t::fatal_disk_error, p,
1189// lt::storage_error(lt::error_code(boost::system::errc::operation_not_supported, lt::system_category())));
1190// });
1191// }
1192
1193// void async_release_files(lt::storage_index_t, std::function<void()>) override {}
1194
1195// void async_delete_files(lt::storage_index_t, lt::remove_flags_t, std::function<void(lt::storage_error const &)>
1196// handler) override
1197// {
1198// post(m_ioc, [=]
1199// { handler(lt::storage_error()); });
1200// }
1201
1202// void async_check_files(lt::storage_index_t, lt::add_torrent_params const *, lt::aux::vector<std::string,
1203// lt::file_index_t>, std::function<void(lt::status_t, lt::storage_error const &)> handler) override
1204// {
1205// post(m_ioc, [=]
1206// { handler(lt::status_t::no_error, lt::storage_error()); });
1207// }
1208
1209// void async_rename_file(lt::storage_index_t, lt::file_index_t const idx, std::string const name,
1210// std::function<void(std::string const &, lt::file_index_t, lt::storage_error const &)> handler) override
1211// {
1212// post(m_ioc, [=]
1213// { handler(name, idx, lt::storage_error()); });
1214// }
1215
1216// void async_stop_torrent(lt::storage_index_t, std::function<void()> handler) override
1217// {
1218// post(m_ioc, handler);
1219// }
1220
1221// void async_set_file_priority(lt::storage_index_t, lt::aux::vector<lt::download_priority_t, lt::file_index_t>
1222// prio, std::function<void(lt::storage_error const &, lt::aux::vector<lt::download_priority_t, lt::file_index_t>)>
1223// handler) override
1224// {
1225// post(m_ioc, [=]
1226// { handler(lt::storage_error(lt::error_code(
1227// boost::system::errc::operation_not_supported, lt::system_category())),
1228// std::move(prio)); });
1229// }
1230
1231// void async_clear_piece(lt::storage_index_t, lt::piece_index_t index, std::function<void(lt::piece_index_t)>
1232// handler) override
1233// {
1234// post(m_ioc, [=]
1235// { handler(index); });
1236// }
1237
1238// // implements buffer_allocator_interface
1239// void free_disk_buffer(char *) override
1240// {
1241// // never free any buffer. We only return buffers owned by the storage
1242// // object
1243// }
1244
1245// void update_stats_counters(lt::counters &) const override {}
1246
1247// std::vector<lt::open_file_state> get_status(lt::storage_index_t) const override
1248// {
1249// return {};
1250// }
1251
1252// void submit_jobs() override {}
1253
1254// private:
1255// lt::aux::vector<std::shared_ptr<temp_storage>, lt::storage_index_t> m_torrents;
1256
1257// // slots that are unused in the m_torrents vector
1258// std::vector<lt::storage_index_t> m_free_slots;
1259
1260// // callbacks are posted on this
1261// lt::io_context &m_ioc;
1262// };
1263
1264// std::unique_ptr<lt::disk_interface> temp_disk_constructor(
1265// lt::io_context &ioc, lt::settings_interface const &, lt::counters &)
1266// {
1267// return std::make_unique<temp_disk_io>(ioc);
1268// }
1269
1270} // namespace dunedaq::snbmodules
#define ERS_HERE
Class that represents an IP address and a port TODO: should be replaced by something better ?
Definition ip_format.hpp:26
std::string get_ip_port() const
Get the IP address and the port in the format "ip:port".
Definition ip_format.hpp:57
std::string get_ip() const
TransferInterfaceBittorrent(GroupMetadata &config, bool is_client, std::filesystem::path work_dir, const IPFormat &listening_ip)
void set_torrent_params(lt::add_torrent_params &p, const std::filesystem::path &dest)
static std::vector< char > load_file(std::string const &filename)
bool make_torrent(std::filesystem::path full_path, int piece_size, const std::string &tracker, const std::string &outfile)
bool download_file(TransferMetadata &f_meta, std::filesystem::path dest) override
bool add_magnet(lt::string_view uri, const std::filesystem::path &dest)
std::map< std::string, TransferMetadata * > m_filename_to_metadata
lt::session_params set_settings(const IPFormat &listen_interface, const std::string &listen_port)
void generate_torrents_files(const std::filesystem::path &dest, const std::string &tracker)
std::string add_torrent(const std::string &torrent, const std::filesystem::path &dest)
void set_error_code(std::string error_code)
std::filesystem::path get_file_path() const
void stop_working_thread()
Stop the working thread.
#define TLOG(...)
Definition macro.hpp:22
FELIX Initialization std::string initerror FELIX queue timed out
FELIX Initialization std::string initerror FELIX queue timed std::string queuename Unexpected chunk size
Unsupported std::string uri Execution of command std::string error Failed to create CommandFacility uri
Definition Issues.hpp:77
void warning(const Issue &issue)
Definition ers.hpp:115
void error(const Issue &issue)
Definition ers.hpp:81
@ PREPARING
waiting for the transfer to start, can be waiting to receive expected files metadata