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
::Component
;
39 use Thrasher
::Log
qw(logger debug log);
42 # Item -> discovery info for each item as [$identities, $features],
43 # created by the component after it finishes connecting
44 our $SERVER_INFO = {};
49 my $backend = shift() || 'Test';
50 my $backend_module = "Thrasher::Backend::$backend";
51 my $protocol = (shift() || 'Test');
52 my $protocol_module = "Thrasher::Protocol::$protocol";
53 my $server_ip = shift;
54 my $server_port = shift;
55 my $server_secret = shift;
56 my $backend_configuration = shift || {};
57 my $protocol_configuration = shift || {};
58 my $component_name_base = shift;
60 my $plugins = shift || [];
62 my $event_loop_module;
64 VALIDATE_PARAMETERS
: {
66 eval "use $backend_module;";
68 print "While trying to load the $backend backend, an error\n"
69 ."was encountered. The developers will want to know about\n"
70 ."the following:\n\n$@\n";
74 eval "use $protocol_module;";
76 print "While trying to load the $protocol protocol, an error\n"
77 ."was encountered. The developers will want to know about\n"
78 ."the following:\n\n$@\n";
82 if (!defined($server_ip)) {
83 print "Missing server specification (\$server_ip in the "
84 ."configuration file).\n";
88 if (!defined($server_port)) {
89 print "Missing server port specification (\$server_port "
90 ."in the configuration file).\n";
93 if ($server_port == 0 ||
95 $server_port > 65536 ||
96 int($server_port) ne $server_port) {
97 print "Invalid port specification: $server_port\n";
101 $component_name_base =~ s/^\.//;
102 if (!$component_name_base) {
103 print "Component name base (\$component_name_base) must not be empty.\n";
107 $event_loop_module = $protocol_module->event_loop;
108 eval "use $event_loop_module;";
110 print "While trying to load $event_loop_module, an error\n"
111 ."was encountered. The developers will want to know\n"
112 ."the following:\n\n$@";
116 for my $plugin_module (@
{$plugins}) {
117 eval("use ${plugin_module};");
120 "While trying to load the $plugin_module plugin, an error\n"
121 . " was encountered. The developers will want to know about\n"
122 . " the following:\n\n$@\n";
130 my $sequential_errors = 0;
131 logger
("Start connection process for PID $$");
135 # This sets up all the plumbing and starts the component
138 debug
("About to call $backend_module->new");
140 $backend_module->new($backend_configuration);
141 debug
("Got a backend back: $backend");
142 debug
("About to call $protocol_module->new");
144 $protocol_module->new($protocol_configuration,
146 debug
("Got a protocol: $protocol");
147 $backend->register_protocol($protocol);
148 my $component_name = $protocol->identifier;
149 if (!$component_name) {
150 die "Protocol $protocol_module does not give "
151 ."us an identifier to use with the component.";
153 $component_name .= '.' . $component_name_base;
155 $event_loop = $event_loop_module->new();
157 my $socket = new Thrasher
::Socket
($server_ip,
161 my $write_function = $socket->write_function;
163 my $component = new Thrasher
::Component
164 ($protocol, $write_function, $server_secret,
166 $component->{event_loop
} = $event_loop;
167 $component->{thrasher_socket
} = $socket;
168 $socket->{read_closure
} =
169 $component->socket_in_closure($socket);
170 $socket->establish_fd_watch;
172 $protocol->{component
} = $component;
173 $backend->{component
} = $component;
175 $component->output_initial_stream_tag;
177 my $kill_thrasher = sub {
178 logger
("SIGINT received, shutting down.");
179 $component->terminate;
182 my $signal_handler = sub {
183 $event_loop->execute_on_idle($kill_thrasher);
185 # If the user hits CTRL-C, nicely log people out,
186 # and allow nice log outs with a signal.
187 local $SIG{INT
} = $signal_handler;
188 local $SIG{TERM
} = $signal_handler;
190 logger
("Beginning main event loop.");
193 $previous_error = '';
195 $sequential_errors = 0;
197 logger
("Main loop terminated.");
199 # Paranoia, really shouldn't fail
200 eval { $socket->close(); };
205 $previous_error = $@
;
207 # Suppress repeating log messages: suppress everything but
208 # the first five errors, and only print every ten minutes
209 # or so after that, to prove it's still trying
210 if ($previous_error eq $error) {
211 $sequential_errors++;
212 my $error_message = "Failed to start transport, retry $retry: $@";
213 if ($sequential_errors == 5) {
214 logger
($error_message);
215 # This will be true someday; right now we still
216 # want the debugging info.
217 logger
("Suppressing most errors now; attempts "
218 ."will continue until successful.");
219 } elsif ($sequential_errors % 60 == 0) {
220 logger
($error_message);
221 } elsif ($sequential_errors < 5) {
222 logger
($error_message);
225 logger
("Failed to connect to server, retry $retry\n");
226 logger
("Error was: $@\n\n");
227 $sequential_errors = 0;
236 # This currently has nowhere to live...
237 # If an error makes it back into the SWIG or libpurple layer,
238 # it is basically destroyed. This catches the error, outputs
239 # it in the log, and eats it (since the higher layer can't do
240 # anything useful with it).
242 my $callback_name = shift;
243 my $sub_to_wrap = shift;
250 # We know there's never any useful return
253 eval { @results = $sub_to_wrap->(@args); };
256 Thrasher
::Log
::log("Error in $callback_name: $@");
262 eval { $result = $sub_to_wrap->(@args); };
265 Thrasher
::Log
::log("Error in $callback_name: $@");