From 1d04c4dcd0923dacef3521e4b376a09bbfc93b46 Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Thu, 3 Dec 2009 05:11:57 +0000 Subject: [PATCH] added session saving and loading functions --- ChangeLog | 1 + docs/manual.rst | 21 ++ include/libtorrent/aux_/session_impl.hpp | 3 + include/libtorrent/session.hpp | 3 + src/session.cpp | 12 + src/session_impl.cpp | 317 +++++++++++++++++++++++ test/test_primitives.cpp | 71 ++++- 7 files changed, 427 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index c892193d0..3523cc5fe 100644 --- a/ChangeLog +++ b/ChangeLog @@ -82,6 +82,7 @@ * added torrent_handle::force_dht_announce() * added torrent_info::remap_files() * support min_interval tracker extension + * added session saving and loading functions release 0.14.8 diff --git a/docs/manual.rst b/docs/manual.rst index 5c4d99522..557d8cb01 100644 --- a/docs/manual.rst +++ b/docs/manual.rst @@ -18,6 +18,7 @@ the ``session``, it contains the main loop that serves all torrents. The basic usage is as follows: * construct a session +* load session state from settings file (see `load_state() save_state()`_) * start extensions (see `add_extension()`_). * start DHT, LSD, UPnP, NAT-PMP etc (see `start_dht() stop_dht() set_dht_settings() dht_state() is_dht_running()`_ `start_lsd() stop_lsd()`_, `start_upnp() stop_upnp()`_ and `start_natpmp() stop_natpmp()`_) @@ -30,6 +31,7 @@ The basic usage is as follows: * save resume data for all torrent_handles (optional, see `save_resume_data()`_) +* save session state (see `load_state() save_state()`_) * destruct session object Each class and function is described in this manual. @@ -115,6 +117,9 @@ The ``session`` class has the following synopsis:: | add_default_plugins , int alert_mask = alert::error_notification); + void load_state(lazy_entry const& e); + void save_state(entry& e) const; + torrent_handle add_torrent( add_torrent_params const& params); torrent_handle add_torrent( @@ -270,6 +275,22 @@ returns. So, it's advised that any kind of interface (such as windows) are close destructing the session object. Because it can take a few second for it to finish. The timeout can be set with ``set_settings()``. +load_state() save_state() +------------------------- + + :: + + void load_state(lazy_entry const& e); + void save_state(entry& e) const; + +loads and saves all session settings, including dht_settings, encryption settings and proxy +settings. ``save_state`` writes all keys to the ``entry`` that's passed in, which needs to +either not be initialized, or initialized as a dictionary. + +``load_state`` expects a ``lazy_entry`` which can be built from a bencoded buffer with +``lazy_bdecode``. + + pause() resume() is_paused() ---------------------------- diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp index f4ccf48c6..dea17abec 100644 --- a/include/libtorrent/aux_/session_impl.hpp +++ b/include/libtorrent/aux_/session_impl.hpp @@ -271,6 +271,9 @@ namespace libtorrent void announce_lsd(sha1_hash const& ih); + void save_state(entry& e) const; + void load_state(lazy_entry const& e); + void set_peer_proxy(proxy_settings const& s) { m_peer_proxy = s; diff --git a/include/libtorrent/session.hpp b/include/libtorrent/session.hpp index f5e34756c..1b89ead78 100644 --- a/include/libtorrent/session.hpp +++ b/include/libtorrent/session.hpp @@ -176,6 +176,9 @@ namespace libtorrent ~session(); + void save_state(entry& e) const; + void load_state(lazy_entry const& e); + // returns a list of all torrents in this session std::vector get_torrents() const; diff --git a/src/session.cpp b/src/session.cpp index 264e0efa5..4eeffdcbb 100644 --- a/src/session.cpp +++ b/src/session.cpp @@ -301,6 +301,18 @@ namespace libtorrent m_impl->abort(); } + void session::save_state(entry& e) const + { + mutex::scoped_lock l(m_impl->m_mutex); + m_impl->save_state(e); + } + + void session::load_state(lazy_entry const& e) + { + mutex::scoped_lock l(m_impl->m_mutex); + m_impl->load_state(e); + } + #ifndef TORRENT_DISABLE_EXTENSIONS void session::add_extension(boost::function(torrent*, void*)> ext) { diff --git a/src/session_impl.cpp b/src/session_impl.cpp index 9df71352a..a2ceb6665 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -171,6 +171,213 @@ namespace aux { } }; + enum { std_string = 0, character = 1, short_integer = 2 + , integer = 3, floating_point = 4, boolean = 5}; + + // this is used to map struct entries + // to names in a bencoded dictionary to + // save and load the struct + struct bencode_map_entry + { + char const* name; + int offset; // struct offset + int type; + }; + +#define TORRENT_SETTING(t, x) {#x, offsetof(session_settings,x), t}, + + bencode_map_entry session_settings_map[] = + { + TORRENT_SETTING(std_string, user_agent) + TORRENT_SETTING(integer, tracker_completion_timeout) + TORRENT_SETTING(integer, tracker_receive_timeout) + TORRENT_SETTING(integer, stop_tracker_timeout) + TORRENT_SETTING(integer, tracker_maximum_response_length) + TORRENT_SETTING(integer, piece_timeout) + TORRENT_SETTING(integer, request_timeout) + TORRENT_SETTING(integer, request_queue_time) + TORRENT_SETTING(integer, max_allowed_in_request_queue) + TORRENT_SETTING(integer, max_out_request_queue) + TORRENT_SETTING(integer, whole_pieces_threshold) + TORRENT_SETTING(integer, peer_timeout) + TORRENT_SETTING(integer, urlseed_timeout) + TORRENT_SETTING(integer, urlseed_pipeline_size) + TORRENT_SETTING(integer, urlseed_wait_retry) + TORRENT_SETTING(integer, file_pool_size) + TORRENT_SETTING(boolean, allow_multiple_connections_per_ip) + TORRENT_SETTING(integer, max_failcount) + TORRENT_SETTING(integer, min_reconnect_time) + TORRENT_SETTING(integer, peer_connect_timeout) + TORRENT_SETTING(boolean, ignore_limits_on_local_network) + TORRENT_SETTING(integer, connection_speed) + TORRENT_SETTING(boolean, send_redundant_have) + TORRENT_SETTING(boolean, lazy_bitfields) + TORRENT_SETTING(integer, inactivity_timeout) + TORRENT_SETTING(integer, unchoke_interval) + TORRENT_SETTING(integer, optimistic_unchoke_interval) + TORRENT_SETTING(std_string, announce_ip) + TORRENT_SETTING(integer, num_want) + TORRENT_SETTING(integer, initial_picker_threshold) + TORRENT_SETTING(integer, allowed_fast_set_size) + TORRENT_SETTING(integer, max_queued_disk_bytes) + TORRENT_SETTING(integer, handshake_timeout) +#ifndef TORRENT_DISABLE_DHT + TORRENT_SETTING(boolean, use_dht_as_fallback) +#endif + TORRENT_SETTING(boolean, free_torrent_hashes) + TORRENT_SETTING(boolean, upnp_ignore_nonrouters) + TORRENT_SETTING(integer, send_buffer_watermark) + TORRENT_SETTING(boolean, auto_upload_slots) + TORRENT_SETTING(boolean, auto_upload_slots_rate_based) + TORRENT_SETTING(boolean, use_parole_mode) + TORRENT_SETTING(integer, cache_size) + TORRENT_SETTING(integer, cache_buffer_chunk_size) + TORRENT_SETTING(integer, cache_expiry) + TORRENT_SETTING(boolean, use_read_cache) + TORRENT_SETTING(integer, disk_io_write_mode) + TORRENT_SETTING(integer, disk_io_read_mode) + TORRENT_SETTING(boolean, coalesce_reads) + TORRENT_SETTING(boolean, coalesce_writes) + TORRENT_SETTING(character, peer_tos) + TORRENT_SETTING(integer, active_downloads) + TORRENT_SETTING(integer, active_seeds) + TORRENT_SETTING(integer, active_limit) + TORRENT_SETTING(boolean, auto_manage_prefer_seeds) + TORRENT_SETTING(boolean, dont_count_slow_torrents) + TORRENT_SETTING(integer, auto_manage_interval) + TORRENT_SETTING(floating_point, share_ratio_limit) + TORRENT_SETTING(floating_point, seed_time_ratio_limit) + TORRENT_SETTING(integer, seed_time_limit) + TORRENT_SETTING(floating_point, peer_turnover) + TORRENT_SETTING(floating_point, peer_turnover_cutoff) + TORRENT_SETTING(boolean, close_redundant_connections) + TORRENT_SETTING(integer, auto_scrape_interval) + TORRENT_SETTING(integer, auto_scrape_min_interval) + TORRENT_SETTING(integer, max_peerlist_size) + TORRENT_SETTING(integer, max_paused_peerlist_size) + TORRENT_SETTING(integer, min_announce_interval) + TORRENT_SETTING(boolean, prioritize_partial_pieces) + TORRENT_SETTING(integer, auto_manage_startup) + TORRENT_SETTING(boolean, rate_limit_ip_overhead) + TORRENT_SETTING(boolean, announce_to_all_trackers) + TORRENT_SETTING(boolean, announce_to_all_tiers) + TORRENT_SETTING(boolean, prefer_udp_trackers) + TORRENT_SETTING(boolean, strict_super_seeding) + TORRENT_SETTING(integer, seeding_piece_quota) + TORRENT_SETTING(integer, max_sparse_regions) +#ifndef TORRENT_DISABLE_MLOCK + TORRENT_SETTING(boolean, lock_disk_cache) +#endif + TORRENT_SETTING(integer, max_rejects) + TORRENT_SETTING(integer, recv_socket_buffer_size) + TORRENT_SETTING(integer, send_socket_buffer_size) + TORRENT_SETTING(boolean, optimize_hashing_for_speed) + TORRENT_SETTING(integer, file_checks_delay_per_block) + TORRENT_SETTING(integer, disk_cache_algorithm) + TORRENT_SETTING(integer, read_cache_line_size) + TORRENT_SETTING(integer, write_cache_line_size) + TORRENT_SETTING(integer, optimistic_disk_retry) + TORRENT_SETTING(boolean, disable_hash_checks) + TORRENT_SETTING(boolean, allow_reordered_disk_operations) + TORRENT_SETTING(boolean, allow_i2p_mixed) + TORRENT_SETTING(integer, max_suggest_pieces) + }; + +#undef TORRENT_SETTING +#define TORRENT_SETTING(t, x) {#x, offsetof(proxy_settings,x), t}, + + bencode_map_entry proxy_settings_map[] = + { + TORRENT_SETTING(std_string, hostname) + TORRENT_SETTING(integer, port) + TORRENT_SETTING(std_string, username) + TORRENT_SETTING(std_string, password) + TORRENT_SETTING(integer, type) + }; +#undef TORRENT_SETTING + +#ifndef TORRENT_DISABLE_DHT +#define TORRENT_SETTING(t, x) {#x, offsetof(dht_settings,x), t}, + bencode_map_entry dht_settings_map[] = + { + TORRENT_SETTING(integer, max_peers_reply) + TORRENT_SETTING(integer, search_branching) + TORRENT_SETTING(integer, service_port) + TORRENT_SETTING(integer, max_fail_count) + TORRENT_SETTING(integer, max_torrent_search_reply) + }; +#undef TORRENT_SETTING +#endif + +#ifndef TORRENT_DISABLE_ENCRYPTION +#define TORRENT_SETTING(t, x) {#x, offsetof(pe_settings,x), t}, + bencode_map_entry pe_settings_map[] = + { + TORRENT_SETTING(integer, out_enc_policy) + TORRENT_SETTING(integer, in_enc_policy) + TORRENT_SETTING(integer, allowed_enc_level) + TORRENT_SETTING(boolean, prefer_rc4) + }; +#undef TORRENT_SETTING +#endif + + void load_struct(lazy_entry const& e, void* s, bencode_map_entry const* m, int num) + { + for (int i = 0; i < num; ++i) + { + lazy_entry const* key = e.dict_find(m[i].name); + if (key == 0) continue; + void* dest = ((char*)s) + m[i].offset; + switch (m[i].type) + { + case std_string: + { + if (key->type() != lazy_entry::string_t) continue; + *((std::string*)dest) = key->string_value(); + break; + } + case character: + case boolean: + case integer: + case floating_point: + { + if (key->type() != lazy_entry::int_t) continue; + size_type val = key->int_value(); + switch (m[i].type) + { + case character: *((char*)dest) = val; break; + case integer: *((int*)dest) = val; break; + case floating_point: *((float*)dest) = float(val) / 1000.f; break; + case boolean: *((bool*)dest) = val; break; + } + } + } + } + } + + void save_struct(entry& e, void const* s, bencode_map_entry const* m, int num) + { + for (int i = 0; i < num; ++i) + { + char const* key = m[i].name; + void const* src = ((char*)s) + m[i].offset; + entry& val = e[key]; + TORRENT_ASSERT(val.type() == entry::undefined_t); + switch (m[i].type) + { + case std_string: + { + val = *((std::string*)src); + break; + } + case character: val = *((char*)src); break; + case integer: val = *((int*)src); break; + case floating_point: val = size_type(*((float*)src) * 1000.f); break; + case boolean: val = *((bool*)src); break; + } + } + } + #ifdef TORRENT_STATS int session_impl::logging_allocator::allocations = 0; int session_impl::logging_allocator::allocated_bytes = 0; @@ -380,6 +587,116 @@ namespace aux { m_thread.reset(new thread(boost::bind(&session_impl::main_thread, this))); } + void session_impl::save_state(entry& e) const + { + save_struct(e["settings"], &m_settings, session_settings_map + , sizeof(session_settings_map)/sizeof(session_settings_map[0])); +#ifndef TORRENT_DISABLE_DHT + save_struct(e["dht"], &dht_settings(), dht_settings_map + , sizeof(dht_settings_map)/sizeof(dht_settings_map[0])); + save_struct(e["dht proxy"], &dht_proxy(), proxy_settings_map + , sizeof(proxy_settings_map)/sizeof(proxy_settings_map[0])); +#endif +#if TORRENT_USE_I2P + save_struct(e["i2p"], &i2p_proxy(), proxy_settings_map + , sizeof(proxy_settings_map)/sizeof(proxy_settings_map[0])); +#endif +#ifndef TORRENT_DISABLE_ENCRYPTION + save_struct(e["encryption"], &get_pe_settings(), pe_settings_map + , sizeof(pe_settings_map)/sizeof(pe_settings_map[0])); +#endif + + save_struct(e["peer proxy"], &peer_proxy(), proxy_settings_map + , sizeof(proxy_settings_map)/sizeof(proxy_settings_map[0])); + save_struct(e["web proxy"], &web_seed_proxy(), proxy_settings_map + , sizeof(proxy_settings_map)/sizeof(proxy_settings_map[0])); + save_struct(e["tracker proxy"], &tracker_proxy(), proxy_settings_map + , sizeof(proxy_settings_map)/sizeof(proxy_settings_map[0])); + } + + void session_impl::load_state(lazy_entry const& e) + { + lazy_entry const* settings; + + settings = e.dict_find_dict("settings"); + if (settings) + { + session_settings s; + load_struct(*settings, &s, session_settings_map + , sizeof(session_settings_map)/sizeof(session_settings_map[0])); + set_settings(s); + } + +#ifndef TORRENT_DISABLE_DHT + settings = e.dict_find_dict("dht"); + if (settings) + { + dht_settings s; + load_struct(*settings, &s, dht_settings_map + , sizeof(dht_settings_map)/sizeof(dht_settings_map[0])); + set_dht_settings(s); + } + + settings = e.dict_find_dict("dht proxy"); + if (settings) + { + proxy_settings s; + load_struct(*settings, &s, proxy_settings_map + , sizeof(proxy_settings_map)/sizeof(proxy_settings_map[0])); + set_dht_proxy(s); + } +#endif + +#if TORRENT_USE_I2P + settings = e.dict_find_dict("i2p"); + if (settings) + { + proxy_settings s; + load_struct(*settings, &s, proxy_settings_map + , sizeof(proxy_settings_map)/sizeof(proxy_settings_map[0])); + set_i2p_proxy(s); + } +#endif + +#ifndef TORRENT_DISABLE_ENCRYPTION + settings = e.dict_find_dict("encryption"); + if (settings) + { + pe_settings s; + load_struct(*settings, &s, pe_settings_map + , sizeof(pe_settings_map)/sizeof(pe_settings_map[0])); + set_pe_settings(s); + } +#endif + + settings = e.dict_find_dict("peer proxy"); + if (settings) + { + proxy_settings s; + load_struct(*settings, &s, proxy_settings_map + , sizeof(proxy_settings_map)/sizeof(proxy_settings_map[0])); + set_peer_proxy(s); + } + + settings = e.dict_find_dict("web proxy"); + if (settings) + { + proxy_settings s; + load_struct(*settings, &s, proxy_settings_map + , sizeof(proxy_settings_map)/sizeof(proxy_settings_map[0])); + set_web_seed_proxy(s); + } + + settings = e.dict_find_dict("tracker proxy"); + if (settings) + { + proxy_settings s; + load_struct(*settings, &s, proxy_settings_map + , sizeof(proxy_settings_map)/sizeof(proxy_settings_map[0])); + set_tracker_proxy(s); + } + } + #ifndef TORRENT_DISABLE_GEO_IP namespace { diff --git a/test/test_primitives.cpp b/test/test_primitives.cpp index 3b680877c..779d4e59b 100644 --- a/test/test_primitives.cpp +++ b/test/test_primitives.cpp @@ -42,6 +42,8 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/broadcast_socket.hpp" #include "libtorrent/identify_client.hpp" #include "libtorrent/file.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/bencode.hpp" #ifndef TORRENT_DISABLE_DHT #include "libtorrent/kademlia/node_id.hpp" #include "libtorrent/kademlia/routing_table.hpp" @@ -367,6 +369,73 @@ int test_main() { using namespace libtorrent; error_code ec; + int ret = 0; + + { + // test session state load/restore + session* s = new session(fingerprint("LT",0,0,0,0), 0); + + session_settings sett; + sett.user_agent = "test"; + sett.tracker_receive_timeout = 1234; + sett.file_pool_size = 543; + sett.urlseed_wait_retry = 74; + sett.file_pool_size = 754; + sett.initial_picker_threshold = 351; + sett.upnp_ignore_nonrouters = 5326; + sett.coalesce_writes = 623; + sett.auto_scrape_interval = 753; + sett.close_redundant_connections = 245; + sett.auto_scrape_interval = 235; + sett.auto_scrape_min_interval = 62; + s->set_settings(sett); +/* +#ifndef TORRENT_DISABLE_DHT + dht_settings dht_sett; + s->set_dht_settings(dht_sett); +#endif +*/ + entry session_state; + s->save_state(session_state); + + delete s; + s = new session(fingerprint("LT",0,0,0,0), 0); + + std::vector buf; + bencode(std::back_inserter(buf), session_state); + lazy_entry session_state2; + ret = lazy_bdecode(&buf[0], &buf[0] + buf.size(), session_state2); + TEST_CHECK(ret == 0); + + printf("session_state\n%s\n", print_entry(session_state2).c_str()); + s->load_state(session_state2); +#define CMP_SET(x) TEST_CHECK(s->settings().x == sett.x) + + CMP_SET(user_agent); + CMP_SET(tracker_receive_timeout); + CMP_SET(file_pool_size); + CMP_SET(urlseed_wait_retry); + CMP_SET(file_pool_size); + CMP_SET(initial_picker_threshold); + CMP_SET(upnp_ignore_nonrouters); + CMP_SET(coalesce_writes); + CMP_SET(auto_scrape_interval); + CMP_SET(close_redundant_connections); + CMP_SET(auto_scrape_interval); + CMP_SET(auto_scrape_min_interval); + CMP_SET(max_peerlist_size); + CMP_SET(max_paused_peerlist_size); + CMP_SET(min_announce_interval); + CMP_SET(prioritize_partial_pieces); + CMP_SET(auto_manage_startup); + CMP_SET(rate_limit_ip_overhead); + CMP_SET(announce_to_all_trackers); + CMP_SET(announce_to_all_tiers); + CMP_SET(prefer_udp_trackers); + CMP_SET(strict_super_seeding); + CMP_SET(seeding_piece_quota); + delete s; + } // test path functions TEST_EQUAL(combine_path("test1/", "test2"), "test1/test2"); @@ -461,7 +530,7 @@ int test_main() char const* tags[10]; char tags_str[] = " this is\ta test\t string\x01to be split and it cannot " "extend over the limit of elements \t"; - int ret = split_string(tags, 10, tags_str); + ret = split_string(tags, 10, tags_str); TEST_CHECK(ret == 10); TEST_CHECK(strcmp(tags[0], "this") == 0);