12 Thrasher - top-level module for Thrasher Bird
16 In the thrasherbird.pl file:
19 Thrasher::start($backend, $protocol, $server_ip, $server_port,
24 The Thrasher module stores a few misc. functions that don't belong
25 anywhere else, and also has the C<start> function, which ties
26 together all of the pieces and actually starts the transport
29 To learn how to write a new Protocol for Thrasher, see
30 L<Thrasher::Protocol>.
32 To learn how to write a new Backend for Thrasher, see
38 use Thrasher
::Callbacks
qw(callbacks);
39 use Thrasher
::Component
;
40 use Thrasher
::Log
qw(logger debug log);
43 # Item -> discovery info for each item as [$identities, $features],
44 # created by the component after it finishes connecting
45 our $SERVER_INFO = {};
50 my $backend = shift() || 'Test';
51 my $backend_module = "Thrasher::Backend::$backend";
52 my $protocol = (shift() || 'Test');
53 my $protocol_module = "Thrasher::Protocol::$protocol";
54 my $server_ip = shift;
55 my $server_port = shift;
56 my $server_secret = shift;
57 my $backend_configuration = shift || {};
58 my $protocol_configuration = shift || {};
59 my $component_name_base = shift;
61 my $plugins = shift || [];
63 my $event_loop_module;
65 VALIDATE_PARAMETERS
: {
67 eval "use $backend_module;";
69 print "While trying to load the $backend backend, an error\n"
70 ."was encountered. The developers will want to know about\n"
71 ."the following:\n\n$@\n";
75 eval "use $protocol_module;";
77 print "While trying to load the $protocol protocol, an error\n"
78 ."was encountered. The developers will want to know about\n"
79 ."the following:\n\n$@\n";
83 if (!defined($server_ip)) {
84 print "Missing server specification (\$server_ip in the "
85 ."configuration file).\n";
89 if (!defined($server_port)) {
90 print "Missing server port specification (\$server_port "
91 ."in the configuration file).\n";
94 if ($server_port == 0 ||
96 $server_port > 65536 ||
97 int($server_port) ne $server_port) {
98 print "Invalid port specification: $server_port\n";
102 $component_name_base =~ s/^\.//;
103 if (!$component_name_base) {
104 print "Component name base (\$component_name_base) must not be empty.\n";
108 $event_loop_module = $protocol_module->event_loop;
109 eval "use $event_loop_module;";
111 print "While trying to load $event_loop_module, an error\n"
112 ."was encountered. The developers will want to know\n"
113 ."the following:\n\n$@";
117 for my $plugin_module (@
{$plugins}) {
118 eval("use ${plugin_module};");
121 "While trying to load the $plugin_module plugin, an error\n"
122 . " was encountered. The developers will want to know about\n"
123 . " the following:\n\n$@\n";
131 my $sequential_errors = 0;
132 logger
("Start connection process for PID $$");
136 # This sets up all the plumbing and starts the component
139 debug
("About to call $backend_module->new");
141 $backend_module->new($backend_configuration);
142 debug
("Got a backend back: $backend");
143 debug
("About to call $protocol_module->new");
145 $protocol_module->new($protocol_configuration,
147 debug
("Got a protocol: $protocol");
148 $backend->register_protocol($protocol);
149 my $component_name = $protocol->identifier;
150 if (!$component_name) {
151 die "Protocol $protocol_module does not give "
152 ."us an identifier to use with the component.";
154 $component_name .= '.' . $component_name_base;
156 $event_loop = $event_loop_module->new();
158 my $socket = new Thrasher
::Socket
($server_ip,
162 my $write_function = $socket->write_function;
164 my $component = new Thrasher
::Component
165 ($protocol, $write_function, $server_secret,
167 $component->{event_loop
} = $event_loop;
168 $component->{thrasher_socket
} = $socket;
169 $socket->{read_closure
} =
170 $component->socket_in_closure($socket);
171 $socket->establish_fd_watch;
173 $protocol->{component
} = $component;
174 $backend->{component
} = $component;
176 $component->output_initial_stream_tag;
178 my $kill_thrasher = sub {
179 logger
("SIGINT received, shutting down.");
180 $component->terminate;
183 my $signal_handler = sub {
184 $event_loop->execute_on_idle($kill_thrasher);
186 # If the user hits CTRL-C, nicely log people out,
187 # and allow nice log outs with a signal.
188 local $SIG{INT
} = $signal_handler;
189 local $SIG{TERM
} = $signal_handler;
191 callbacks
('main_loop', $component);
193 logger
("Beginning main event loop.");
196 $previous_error = '';
198 $sequential_errors = 0;
200 logger
("Main loop terminated.");
202 # Paranoia, really shouldn't fail
203 eval { $socket->close(); };
208 $previous_error = $@
;
210 # Suppress repeating log messages: suppress everything but
211 # the first five errors, and only print every ten minutes
212 # or so after that, to prove it's still trying
213 if ($previous_error eq $error) {
214 $sequential_errors++;
215 my $error_message = "Failed to start transport, retry $retry: $@";
216 if ($sequential_errors == 5) {
217 logger
($error_message);
218 # This will be true someday; right now we still
219 # want the debugging info.
220 logger
("Suppressing most errors now; attempts "
221 ."will continue until successful.");
222 } elsif ($sequential_errors % 60 == 0) {
223 logger
($error_message);
224 } elsif ($sequential_errors < 5) {
225 logger
($error_message);
228 logger
("Failed to connect to server, retry $retry\n");
229 logger
("Error was: $@\n\n");
230 $sequential_errors = 0;
239 # This currently has nowhere to live...
240 # If an error makes it back into the SWIG or libpurple layer,
241 # it is basically destroyed. This catches the error, outputs
242 # it in the log, and eats it (since the higher layer can't do
243 # anything useful with it).
245 my $callback_name = shift;
246 my $sub_to_wrap = shift;
253 # We know there's never any useful return
256 eval { @results = $sub_to_wrap->(@args); };
259 Thrasher
::Log
::log("Error in $callback_name: $@");
265 eval { $result = $sub_to_wrap->(@args); };
268 Thrasher
::Log
::log("Error in $callback_name: $@");