1 // rTorrent - BitTorrent client
2 // Copyright (C) 2005-2011, Jari Sundell
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 //
18 // In addition, as a special exception, the copyright holders give
19 // permission to link the code of portions of this program with the
20 // OpenSSL library under certain conditions as described in each
21 // individual source file, and distribute linked combinations
22 // including the two.
23 //
24 // You must obey the GNU General Public License in all respects for
25 // all of the code used other than OpenSSL. If you modify file(s)
26 // with this exception, you may extend this exception to your version
27 // of the file(s), but you are not obligated to do so. If you do not
28 // wish to do so, delete this exception statement from your version.
29 // If you delete this exception statement from all source files in the
30 // program, then also delete it here.
31 //
32 // Contact: Jari Sundell <jaris@ifi.uio.no>
33 //
34 // Skomakerveien 33
35 // 3185 Skoppum, NORWAY
36
37 #include "config.h"
38
39 #include <torrent/download/resource_manager.h>
40 #include <torrent/download/choke_group.h>
41 #include <torrent/download/choke_queue.h>
42 #include <torrent/utils/option_strings.h>
43
44 #include "ui/root.h"
45 #include "rpc/parse.h"
46 #include "rpc/parse_commands.h"
47
48 #include "globals.h"
49 #include "control.h"
50 #include "command_helpers.h"
51
52 // For cg_d_group.
53 #include "core/download.h"
54
55 // A hack to allow testing of the new choke_group API without the
56 // working parts present.
57 #define USE_CHOKE_GROUP 0
58
59 #if USE_CHOKE_GROUP
60
61 int64_t
cg_get_index(const torrent::Object & raw_args)62 cg_get_index(const torrent::Object& raw_args) {
63 const torrent::Object& arg = (raw_args.is_list() && !raw_args.as_list().empty()) ? raw_args.as_list().front() : raw_args;
64
65 int64_t index = 0;
66
67 if (arg.is_string()) {
68 if (!rpc::parse_whole_value_nothrow(arg.as_string().c_str(), &index))
69 return torrent::resource_manager()->group_index_of(arg.as_string());
70
71 } else {
72 index = arg.as_value();
73 }
74
75 if (index < 0)
76 index = (int64_t)torrent::resource_manager()->group_size() + index;
77
78 return std::min<uint64_t>(index, torrent::resource_manager()->group_size());
79 }
80
81 torrent::choke_group*
cg_get_group(const torrent::Object & raw_args)82 cg_get_group(const torrent::Object& raw_args) {
83 return torrent::resource_manager()->group_at(cg_get_index(raw_args));
84 }
85
86 int64_t
cg_d_group(core::Download * download)87 cg_d_group(core::Download* download) {
88 return torrent::resource_manager()->entry_at(download->main()).group();
89 }
90
91 const std::string&
cg_d_group_name(core::Download * download)92 cg_d_group_name(core::Download* download) {
93 return torrent::resource_manager()->group_at(torrent::resource_manager()->entry_at(download->main()).group())->name();
94 }
95
96 void
cg_d_group_set(core::Download * download,const torrent::Object & arg)97 cg_d_group_set(core::Download* download, const torrent::Object& arg) {
98 torrent::resource_manager()->set_group(torrent::resource_manager()->find_throw(download->main()), cg_get_index(arg));
99 }
100
101 torrent::Object
apply_cg_list()102 apply_cg_list() {
103 torrent::Object::list_type result;
104
105 for (torrent::ResourceManager::group_iterator
106 itr = torrent::resource_manager()->group_begin(),
107 last = torrent::resource_manager()->group_end(); itr != last; itr++)
108 result.push_back((*itr)->name());
109
110 return torrent::Object::from_list(result);
111 }
112
113 torrent::Object
apply_cg_insert(const std::string & arg)114 apply_cg_insert(const std::string& arg) {
115 int64_t dummy;
116
117 if (rpc::parse_whole_value_nothrow(arg.c_str(), &dummy))
118 throw torrent::input_error("Cannot use a value string as choke group name.");
119
120 torrent::resource_manager()->push_group(arg);
121
122 return torrent::Object();
123 }
124
125 //
126 // The hacked version:
127 //
128 #else
129
130 std::vector<torrent::choke_group*> cg_list_hack;
131
132 int64_t
cg_get_index(const torrent::Object & raw_args)133 cg_get_index(const torrent::Object& raw_args) {
134 const torrent::Object& arg = (raw_args.is_list() && !raw_args.as_list().empty()) ? raw_args.as_list().front() : raw_args;
135
136 int64_t index = 0;
137
138 if (arg.is_string()) {
139 if (!rpc::parse_whole_value_nothrow(arg.as_string().c_str(), &index)) {
140 std::vector<torrent::choke_group*>::iterator itr = std::find_if(cg_list_hack.begin(), cg_list_hack.end(),
141 rak::equal(arg.as_string(), std::mem_fun(&torrent::choke_group::name)));
142
143 if (itr == cg_list_hack.end())
144 throw torrent::input_error("Choke group not found.");
145
146 return std::distance(cg_list_hack.begin(), itr);
147 }
148
149 } else {
150 index = arg.as_value();
151 }
152
153 if (index < 0)
154 index = (int64_t)cg_list_hack.size() + index;
155
156 if ((size_t)index >= cg_list_hack.size())
157 throw torrent::input_error("Choke group not found.");
158
159 return index;
160 }
161
162 torrent::choke_group*
cg_get_group(const torrent::Object & raw_args)163 cg_get_group(const torrent::Object& raw_args) {
164 int64_t index = cg_get_index(raw_args);
165
166 if ((size_t)index >= cg_list_hack.size())
167 throw torrent::input_error("Choke group not found.");
168
169 return cg_list_hack.at(index);
170 }
171
cg_d_group(core::Download * download)172 int64_t cg_d_group(core::Download* download) { return download->group(); }
cg_d_group_set(core::Download * download,const torrent::Object & arg)173 void cg_d_group_set(core::Download* download, const torrent::Object& arg) { download->set_group(cg_get_index(arg)); }
174
175 torrent::Object
apply_cg_list()176 apply_cg_list() {
177 torrent::Object::list_type result;
178
179 for (std::vector<torrent::choke_group*>::iterator itr = cg_list_hack.begin(), last = cg_list_hack.end(); itr != last; itr++)
180 result.push_back((*itr)->name());
181
182 return torrent::Object::from_list(result);
183 }
184
185 torrent::Object
apply_cg_insert(const std::string & arg)186 apply_cg_insert(const std::string& arg) {
187 int64_t dummy;
188
189 if (rpc::parse_whole_value_nothrow(arg.c_str(), &dummy))
190 throw torrent::input_error("Cannot use a value string as choke group name.");
191
192 if (arg.empty() ||
193 std::find_if(cg_list_hack.begin(), cg_list_hack.end(),
194 rak::equal(arg, std::mem_fun(&torrent::choke_group::name))) != cg_list_hack.end())
195 throw torrent::input_error("Duplicate name for choke group.");
196
197 cg_list_hack.push_back(new torrent::choke_group());
198 cg_list_hack.back()->set_name(arg);
199
200 cg_list_hack.back()->up_queue()->set_heuristics(torrent::choke_queue::HEURISTICS_UPLOAD_LEECH);
201 cg_list_hack.back()->down_queue()->set_heuristics(torrent::choke_queue::HEURISTICS_DOWNLOAD_LEECH);
202
203 return torrent::Object();
204 }
205
206 torrent::Object
apply_cg_index_of(const std::string & arg)207 apply_cg_index_of(const std::string& arg) {
208 std::vector<torrent::choke_group*>::iterator itr =
209 std::find_if(cg_list_hack.begin(), cg_list_hack.end(), rak::equal(arg, std::mem_fun(&torrent::choke_group::name)));
210
211 if (itr == cg_list_hack.end())
212 throw torrent::input_error("Choke group not found.");
213
214 return std::distance(cg_list_hack.begin(), itr);
215 }
216
217 //
218 // End of choke group hack.
219 //
220 #endif
221
222
223 torrent::Object
apply_cg_max_set(const torrent::Object::list_type & args,bool is_up)224 apply_cg_max_set(const torrent::Object::list_type& args, bool is_up) {
225 if (args.size() != 2)
226 throw torrent::input_error("Incorrect number of arguments.");
227
228 int64_t second_arg = 0;
229 rpc::parse_whole_value(args.back().as_string().c_str(), &second_arg);
230
231 if (is_up)
232 cg_get_group(args.front())->up_queue()->set_max_unchoked(second_arg);
233 else
234 cg_get_group(args.front())->down_queue()->set_max_unchoked(second_arg);
235
236 return torrent::Object();
237 }
238
239 torrent::Object
apply_cg_heuristics_set(const torrent::Object::list_type & args,bool is_up)240 apply_cg_heuristics_set(const torrent::Object::list_type& args, bool is_up) {
241 if (args.size() != 2)
242 throw torrent::input_error("Incorrect number of arguments.");
243
244 int t = torrent::option_find_string(is_up ? torrent::OPTION_CHOKE_HEURISTICS_UPLOAD : torrent::OPTION_CHOKE_HEURISTICS_DOWNLOAD,
245 args.back().as_string().c_str());
246
247 if (is_up)
248 cg_get_group(args.front())->up_queue()->set_heuristics((torrent::choke_queue::heuristics_enum)t);
249 else
250 cg_get_group(args.front())->down_queue()->set_heuristics((torrent::choke_queue::heuristics_enum)t);
251
252 return torrent::Object();
253 }
254
255 torrent::Object
apply_cg_tracker_mode_set(const torrent::Object::list_type & args)256 apply_cg_tracker_mode_set(const torrent::Object::list_type& args) {
257 if (args.size() != 2)
258 throw torrent::input_error("Incorrect number of arguments.");
259
260 int t = torrent::option_find_string(torrent::OPTION_TRACKER_MODE, args.back().as_string().c_str());
261
262 cg_get_group(args.front())->set_tracker_mode((torrent::choke_group::tracker_mode_enum)t);
263
264 return torrent::Object();
265 }
266
267 #define CG_GROUP_AT() std::bind(&cg_get_group, std::placeholders::_2)
268 #define CHOKE_GROUP(direction) std::bind(direction, CG_GROUP_AT())
269
270 /*
271
272 <cg_index> -> '0'..'(choke_group.size)'
273 -> '-1'..'-(choke_group.size)'
274 -> '<group_name>'
275
276 (choke_group.list) -> List of group names.
277 (choke_group.size) -> Number of groups.
278
279 (choke_group.insert,"group_name")
280
281 Adds a new group with default settings, use index '-1' to accessing it
282 immediately afterwards.
283
284 (choke_group.index_of,"group_name") -> <group_index>
285
286 Throws if the group name was not found.
287
288 (choke_group.general.size,<cg_index>) -> <size>
289
290 Number of torrents in this group.
291
292 (choke_group.tracker.mode,<cg_index>) -> "tracker_mode"
293 (choke_group.tracker.mode.set,<cg_index>,"tracker_mode")
294
295 Decide on how aggressive a tracker should be, see
296 'strings.tracker_mode' for list of available options
297
298 (choke_group.up.rate,<cg_index>) -> <bytes/second>
299 (choke_group.down.rate,<cg_index>) -> <bytes/second>
300
301 Upload / download rate for the aggregate of all torrents in this
302 particular group.
303
304 (choke_group.up.max,<cg_index>) -> <max_upload_slots>
305 (choke_group.up.max.unlimited,<cg_index>) -> <max_upload_slots>
306 (choke_group.up.max.set,<cg_index>, <max_upload_slots>)
307 (choke_group.down.max,<cg_index>) -> <max_download_slots>
308 (choke_group.down.max.unlimited,<cg_index>) -> <max_download_slots>
309 (choke_group.down.max.set,<cg_index>, <max_download_slots)
310
311 Number of unchoked upload / download peers regulated on a group basis.
312
313 (choke_group.up.total,<cg_index>) -> <number of queued and unchoked interested peers>
314 (choke_group.up.queued,<cg_index>) -> <number of queued interested peers>
315 (choke_group.up.unchoked,<cg_index>) -> <number of unchoked uploads>
316 (choke_group.down.total,<cg_index>) -> <number of queued and unchoked interested peers>
317 (choke_group.down.queued,<cg_index>) -> <number of queued interested peers>
318 (choke_group.down.unchoked,<cg_index>) -> <number of unchoked uploads>
319
320 (choke_group.up.heuristics,<cg_index>) -> "heuristics"
321 (choke_group.up.heuristics.set,<cg_index>,"heuristics")
322 (choke_group.down.heuristics,<cg_index>) -> "heuristics"
323 (choke_group.down.heuristics.set,<cg_index>,"heuristics")
324
325 Heuristics are used for deciding what peers to choke and unchoke, see
326 'strings.choke_heuristics{,_download,_upload}' for a list of available
327 options.
328
329 (d.group) -> <choke_group_index>
330 (d.group.name) -> "choke_group_name"
331 (d.group.set,<cg_index>)
332
333 */
334
335
336 void
initialize_command_groups()337 initialize_command_groups() {
338 CMD2_ANY ("choke_group.list", std::bind(&apply_cg_list));
339 CMD2_ANY_STRING ("choke_group.insert", std::bind(&apply_cg_insert, std::placeholders::_2));
340
341 #if USE_CHOKE_GROUP
342 CMD2_ANY ("choke_group.size", std::bind(&torrent::ResourceManager::group_size, torrent::resource_manager()));
343 CMD2_ANY_STRING ("choke_group.index_of", std::bind(&torrent::ResourceManager::group_index_of, torrent::resource_manager(), std::placeholders::_2));
344 #else
345 apply_cg_insert("default");
346
347 CMD2_ANY ("choke_group.size", std::bind(&std::vector<torrent::choke_group*>::size, cg_list_hack));
348 CMD2_ANY_STRING ("choke_group.index_of", std::bind(&apply_cg_index_of, std::placeholders::_2));
349 #endif
350
351 // Commands specific for a group. Supports as the first argument the
352 // name, the index or a negative index.
353 CMD2_ANY ("choke_group.general.size", std::bind(&torrent::choke_group::size, CG_GROUP_AT()));
354
355 CMD2_ANY ("choke_group.tracker.mode", std::bind(&torrent::option_as_string, torrent::OPTION_TRACKER_MODE,
356 std::bind(&torrent::choke_group::tracker_mode, CG_GROUP_AT())));
357 CMD2_ANY_LIST ("choke_group.tracker.mode.set", std::bind(&apply_cg_tracker_mode_set, std::placeholders::_2));
358
359 CMD2_ANY ("choke_group.up.rate", std::bind(&torrent::choke_group::up_rate, CG_GROUP_AT()));
360 CMD2_ANY ("choke_group.down.rate", std::bind(&torrent::choke_group::down_rate, CG_GROUP_AT()));
361
362 CMD2_ANY ("choke_group.up.max.unlimited", std::bind(&torrent::choke_queue::is_unlimited, CHOKE_GROUP(&torrent::choke_group::up_queue)));
363 CMD2_ANY ("choke_group.up.max", std::bind(&torrent::choke_queue::max_unchoked_signed, CHOKE_GROUP(&torrent::choke_group::up_queue)));
364 CMD2_ANY_LIST ("choke_group.up.max.set", std::bind(&apply_cg_max_set, std::placeholders::_2, true));
365
366 CMD2_ANY ("choke_group.up.total", std::bind(&torrent::choke_queue::size_total, CHOKE_GROUP(&torrent::choke_group::up_queue)));
367 CMD2_ANY ("choke_group.up.queued", std::bind(&torrent::choke_queue::size_queued, CHOKE_GROUP(&torrent::choke_group::up_queue)));
368 CMD2_ANY ("choke_group.up.unchoked", std::bind(&torrent::choke_queue::size_unchoked, CHOKE_GROUP(&torrent::choke_group::up_queue)));
369 CMD2_ANY ("choke_group.up.heuristics", std::bind(&torrent::option_as_string, torrent::OPTION_CHOKE_HEURISTICS,
370 std::bind(&torrent::choke_queue::heuristics, CHOKE_GROUP(&torrent::choke_group::up_queue))));
371 CMD2_ANY_LIST ("choke_group.up.heuristics.set", std::bind(&apply_cg_heuristics_set, std::placeholders::_2, true));
372
373 CMD2_ANY ("choke_group.down.max.unlimited", std::bind(&torrent::choke_queue::is_unlimited, CHOKE_GROUP(&torrent::choke_group::down_queue)));
374 CMD2_ANY ("choke_group.down.max", std::bind(&torrent::choke_queue::max_unchoked_signed, CHOKE_GROUP(&torrent::choke_group::down_queue)));
375 CMD2_ANY_LIST ("choke_group.down.max.set", std::bind(&apply_cg_max_set, std::placeholders::_2, false));
376
377 CMD2_ANY ("choke_group.down.total", std::bind(&torrent::choke_queue::size_total, CHOKE_GROUP(&torrent::choke_group::down_queue)));
378 CMD2_ANY ("choke_group.down.queued", std::bind(&torrent::choke_queue::size_queued, CHOKE_GROUP(&torrent::choke_group::down_queue)));
379 CMD2_ANY ("choke_group.down.unchoked", std::bind(&torrent::choke_queue::size_unchoked, CHOKE_GROUP(&torrent::choke_group::down_queue)));
380 CMD2_ANY ("choke_group.down.heuristics", std::bind(&torrent::option_as_string, torrent::OPTION_CHOKE_HEURISTICS,
381 std::bind(&torrent::choke_queue::heuristics, CHOKE_GROUP(&torrent::choke_group::down_queue))));
382 CMD2_ANY_LIST ("choke_group.down.heuristics.set", std::bind(&apply_cg_heuristics_set, std::placeholders::_2, false));
383 }
384