2 /// \file brawchannel.cc
3 /// Directs a named raw channel over STDIN/STDOUT or TCP
7 Copyright (C) 2010-2012, RealVNC Ltd.
9 Some parts are inspired from bjavaloader.cc
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20 See the GNU General Public License in the COPYING file at the
21 root directory of this project for more details.
25 #include <barry/barry.h>
35 #include <sys/types.h>
44 #include "barrygetopt.h"
45 #include "brawchannel.h"
48 using namespace Barry
;
50 static volatile bool signalReceived
= false;
52 static void signalHandler(int signum
)
54 signalReceived
= true;
57 class CallbackHandler
: public Barry::Mode::RawChannelDataCallback
60 OutputStream
& m_output
;
61 volatile bool *m_continuePtr
;
65 CallbackHandler(OutputStream
& output
, volatile bool &keepGoing
, bool verbose
)
67 , m_continuePtr(&keepGoing
)
73 public: // From RawChannelDataCallback
74 virtual void DataReceived(Data
&data
);
75 virtual void ChannelError(string msg
);
76 virtual void ChannelClose();
80 void CallbackHandler::DataReceived(Data
&data
)
83 cerr
<< _("From BB: ");
88 size_t toWrite
= data
.GetSize();
91 while( written
< toWrite
&& *m_continuePtr
) {
92 ssize_t writtenThisTime
= m_output
.write(&(data
.GetData()[written
]), toWrite
- written
);
94 cerr
.setf(ios::dec
, ios::basefield
);
95 cerr
<< string_vprintf(_("Written %ld bytes over stdout"), (long int)writtenThisTime
) << endl
;
98 if( writtenThisTime
< 0 ) {
102 written
+= writtenThisTime
;
107 void CallbackHandler::ChannelError(string msg
)
109 cerr
<< _("CallbackHandler: Received error: ") << msg
<< endl
;
113 void CallbackHandler::ChannelClose()
115 *m_continuePtr
= false;
120 int logical
, major
, minor
;
121 const char *Version
= Barry::Version(logical
, major
, minor
);
123 cerr
<< string_vprintf(
124 _("brawchannel - Command line USB Blackberry raw channel interface\n"
125 " Copyright 2010-2012, RealVNC Ltd.\n"
129 "brawchannel [options] <channel name>\n"
132 " -p pin PIN of device to talk with\n"
133 " If only one device is plugged in, this flag is optional\n"
134 " -P pass Simplistic method to specify device password\n"
135 " -l port Listen for a TCP connection on the provided port instead\n"
136 " of using STDIN and STDOUT for data\n"
137 " -a addr Address to bind the listening socket to, allowing listening\n"
138 " only on a specified interface\n"
139 " -v Dump protocol data during operation\n"
140 " This will cause libusb output to appear on STDOUT unless\n"
141 " the environment variable USB_DEBUG is set to 0,1 or 2.\n"),
146 // Helper class to restore signal handlers when shutdown is occuring
147 // This class isn't responsible for setting up the signal handlers
148 // as they need to be restored before the Barry::Socket starts closing.
153 sighandler_t m_handler
;
155 SignalRestorer(int signum
, sighandler_t handler
)
156 : m_signum(signum
), m_handler(handler
) {}
157 ~SignalRestorer() { signal(m_signum
, m_handler
); }
160 int main(int argc
, char *argv
[])
164 // Setup signal handling
165 sighandler_t oldSigHup
= signal(SIGHUP
, &signalHandler
);
166 sighandler_t oldSigTerm
= signal(SIGTERM
, &signalHandler
);
167 sighandler_t oldSigInt
= signal(SIGINT
, &signalHandler
);
168 sighandler_t oldSigQuit
= signal(SIGQUIT
, &signalHandler
);
170 cerr
.sync_with_stdio(true); // since libusb uses
171 // stdio for debug messages
173 // Buffer to hold data read in from STDIN before sending it
174 // to the BlackBerry.
175 unsigned char *buf
= NULL
;
178 bool data_dump
= false;
183 // process command line options
185 int cmd
= getopt(argc
, argv
, "hp:P:l:a:v");
192 case 'p': // Blackberry PIN
193 pin
= strtoul(optarg
, NULL
, 16);
196 case 'P': // Device password
200 case 'v': // data dump on
205 tcp_port
= strtol(optarg
, NULL
, 10);
223 cerr
<< _("Error: Missing raw channel name.") << endl
;
229 cerr
<< _("Error: Too many arguments.") << endl
;
234 // Fetch command from remaining arguments
235 string channelName
= argv
[0];
239 if( tcp_addr
.length() != 0 && tcp_port
== 0 ) {
240 cerr
<< _("Error: specified TCP listen address but no port.") << endl
;
245 // Warn if USB_DEBUG isn't set to 0, 1 or 2
246 // as that usually means libusb will write to STDOUT
247 char *val
= getenv("USB_DEBUG");
248 int parsedValue
= -1;
250 parsedValue
= atoi(val
);
252 if( parsedValue
!= 0 && parsedValue
!= 1 && parsedValue
!= 2 ) {
253 cerr
<< _("Warning: Protocol dump enabled without setting USB_DEBUG to 0, 1 or 2.\n"
254 " libusb might log to STDOUT and ruin data stream.") << endl
;
258 // Initialize the barry library. Must be called before
260 Barry::Init(data_dump
, &cerr
);
262 // Probe the USB bus for Blackberry devices.
263 // If user has specified a PIN, search for it in the
264 // available device list here as well
266 int activeDevice
= probe
.FindActive(pin
);
267 if( activeDevice
== -1 ) {
268 cerr
<< _("No device selected, or PIN not found")
273 // Now get setup to open the channel.
275 cerr
<< _("Connected to device, starting read/write\n");
278 volatile bool running
= true;
280 auto_ptr
<TcpStream
> tcpStreamPtr
;
281 auto_ptr
<InputStream
> inputPtr
;
282 auto_ptr
<OutputStream
> outputPtr
;
284 if( tcp_port
!= 0 ) {
285 /* Use TCP socket for channel data */
286 tcpStreamPtr
.reset(new TcpStream(tcp_addr
, tcp_port
));
287 if( !tcpStreamPtr
->accept() ) {
288 cerr
<< _("Failed to listen on requested port\n");
291 inputPtr
.reset(new TcpInStream(*tcpStreamPtr
));
292 outputPtr
.reset(new TcpOutStream(*tcpStreamPtr
));
294 /* Use STDIN and STDOUT for channel data */
295 inputPtr
.reset(new StdInStream());
296 outputPtr
.reset(new StdOutStream());
298 // Create the thing which will write onto stdout
299 // and perform other callback duties.
300 CallbackHandler
callbackHandler(*outputPtr
, running
, data_dump
);
302 // Start a thread to handle any data arriving from
304 auto_ptr
<SocketRoutingQueue
> router
;
305 router
.reset(new SocketRoutingQueue());
306 router
->SpinoffSimpleReadThread();
308 // Create our controller object
309 Barry::Controller
con(probe
.Get(activeDevice
), *router
);
311 Barry::Mode::RawChannel
rawChannel(con
, callbackHandler
);
313 // Try to open the requested channel now everything is setup
314 rawChannel
.Open(password
.c_str(), channelName
.c_str());
316 // We now have a thread running to read from the
317 // BB and write over stdout; in this thread we'll
318 // read from stdin and write to the BB.
319 const size_t bufSize
= rawChannel
.MaximumSendSize();
320 buf
= new unsigned char[bufSize
];
322 // Set up the signal restorers to restore signal
323 // handling (in their destructors) before the socket
324 // starts to be closed. This allows, for example,
325 // double control-c presses to stop graceful close
327 SignalRestorer
srh(SIGHUP
, oldSigHup
);
328 SignalRestorer
srt(SIGTERM
, oldSigTerm
);
329 SignalRestorer
sri(SIGINT
, oldSigInt
);
330 SignalRestorer
srq(SIGQUIT
, oldSigQuit
);
332 while( running
&& !signalReceived
) {
333 ssize_t haveRead
= inputPtr
->read(buf
, bufSize
, READ_TIMEOUT_SECONDS
);
335 Data
toWrite(buf
, haveRead
);
337 cerr
.setf(ios::dec
, ios::basefield
);
338 cerr
<< string_vprintf(_("Sending %ld bytes stdin->USB\n"), (long int)haveRead
);
339 cerr
<< _("To BB: ");
340 toWrite
.DumpHex(cerr
);
343 rawChannel
.Send(toWrite
);
345 cerr
.setf(ios::dec
, ios::basefield
);
346 cerr
<< string_vprintf(_("Sent %ld bytes stdin->USB\n"), (long int)haveRead
);
349 else if( haveRead
< 0 ) {
354 catch( const Usb::Error
&ue
) {
355 cerr
<< _("Usb::Error caught: ") << ue
.what() << endl
;
358 catch( const Barry::Error
&se
) {
359 cerr
<< _("Barry::Error caught: ") << se
.what() << endl
;
362 catch( const exception
&e
) {
363 cerr
<< _("exception caught: ") << e
.what() << endl
;
367 cerr
<< _("unknown exception caught") << endl
;