2 /// \file brawchannel.cc
3 /// Directs a named raw channel over STDIN/STDOUT
7 Copyright (C) 2010, 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>
37 #include <sys/types.h>
45 using namespace Barry
;
47 // How long to wait between reads before checking if should shutdown
48 #define READ_TIMEOUT_SECONDS 1
50 static volatile bool signalReceived
= false;
52 static void signalHandler(int signum
)
54 signalReceived
= true;
57 class CallbackHandler
: public Barry::Mode::RawChannelDataCallback
60 volatile bool* m_continuePtr
;
64 CallbackHandler(volatile bool& keepGoing
, bool verbose
)
65 : m_continuePtr(&keepGoing
)
71 public: // From RawChannelDataCallback
72 virtual void DataReceived(Data
& data
);
73 virtual void ChannelError(string msg
);
74 virtual void ChannelClose();
78 void CallbackHandler::DataReceived(Data
& data
)
86 size_t toWrite
= data
.GetSize();
89 while( written
< toWrite
&& *m_continuePtr
) {
90 ssize_t writtenThisTime
= write(STDOUT_FILENO
, &(data
.GetData()[written
]), toWrite
- written
);
92 cerr
.setf(ios::dec
, ios::basefield
);
93 cerr
<< "Written " << writtenThisTime
<< " bytes over stdout" << endl
;
96 if( writtenThisTime
< 0 ) {
100 written
+= writtenThisTime
;
105 void CallbackHandler::ChannelError(string msg
)
107 cerr
<< "CallbackHandler: Received error: " << msg
<< endl
;
111 void CallbackHandler::ChannelClose()
113 *m_continuePtr
= false;
116 // Class which extends the functionality of SocketRoutingQueue to add
117 // error detection and setting of a continue boolean to false when an
118 // error is detected.
119 // This code is heavily based on the thread
120 // creation code of SocketRoutingQueue, which sadly has too many
121 // private variables to just sub-class.
122 class ErrorHandlingSocketRoutingQueue
125 static void* ReadThreadFunction(void* userPtr
)
127 ErrorHandlingSocketRoutingQueue
*q
= (ErrorHandlingSocketRoutingQueue
*)userPtr
;
129 // read from USB and write to stdout until finished
131 while( q
->m_runningThread
) {
132 if( !q
->m_socketRoutingQueue
.DoRead(msg
, READ_TIMEOUT_SECONDS
* 1000) &&
133 // Only report the first failure, so check m_continuePtr
134 *q
->m_continuePtr
) {
135 cerr
<< "Error in ReadThread: " << msg
<< endl
;
136 *q
->m_continuePtr
= false;
142 SocketRoutingQueue m_socketRoutingQueue
;
143 volatile bool* m_continuePtr
;
144 volatile bool m_runningThread
;
145 pthread_t m_usb_read_thread
;
147 ErrorHandlingSocketRoutingQueue(volatile bool& continuePtr
)
148 : m_socketRoutingQueue()
149 , m_continuePtr(&continuePtr
)
150 , m_runningThread(false)
155 ~ErrorHandlingSocketRoutingQueue()
157 // Is the read thread still running
158 if( m_runningThread
) {
159 m_runningThread
= false;
160 pthread_join(m_usb_read_thread
, NULL
);
164 // Utility function to make it easier to create the
165 // USB pure-read thread.
166 // Throws Barry::ErrnoError on thread creation error.
167 void SpinoffReadThread()
169 // signal that it's ok to run inside the thread
170 if( m_runningThread
)
171 return; // already running
172 m_runningThread
= true;
174 // Start USB read thread, to handle all routing
175 int ret
= pthread_create(&m_usb_read_thread
, NULL
, &ReadThreadFunction
, this);
177 m_runningThread
= false;
178 throw Barry::ErrnoError("SocketRoutingQueue: Error creating USB read thread.", ret
);
182 SocketRoutingQueue
* GetSocketRoutingQueue()
184 return &m_socketRoutingQueue
;
191 const char *Version
= Barry::Version(major
, minor
);
194 << "brawchannel - Command line USB Blackberry raw channel interface\n"
195 << " Copyright 2010, RealVNC Ltd.\n"
196 << " Using: " << Version
<< "\n"
199 << "brawchannel [options] <channel name>\n"
202 << " -p pin PIN of device to talk with\n"
203 << " If only one device is plugged in, this flag is optional\n"
204 << " -P pass Simplistic method to specify device password\n"
205 << " -v Dump protocol data during operation\n"
206 << " This will cause libusb output to appear on STDOUT unless\n"
207 << " the environment variable USB_DEBUG is set to 0,1 or 2.\n"
211 // Helper class to restore signal handlers when shutdown is occuring
212 // This class isn't responsible for setting up the signal handlers
213 // as they need to be restored before the Barry::Socket starts closing.
218 sighandler_t m_handler
;
220 SignalRestorer(int signum
, sighandler_t handler
)
221 : m_signum(signum
), m_handler(handler
) {}
222 ~SignalRestorer() { signal(m_signum
, m_handler
); }
225 int main(int argc
, char *argv
[])
229 // Setup signal handling
230 sighandler_t oldSigHup
= signal(SIGHUP
, &signalHandler
);
231 sighandler_t oldSigTerm
= signal(SIGTERM
, &signalHandler
);
232 sighandler_t oldSigInt
= signal(SIGINT
, &signalHandler
);
233 sighandler_t oldSigQuit
= signal(SIGQUIT
, &signalHandler
);
235 cerr
.sync_with_stdio(true); // since libusb uses
236 // stdio for debug messages
238 // Buffer to hold data read in from STDIN before sending it
239 // to the BlackBerry.
240 unsigned char* buf
= NULL
;
243 bool data_dump
= false;
246 // process command line options
248 int cmd
= getopt(argc
, argv
, "hp:P:v");
255 case 'p': // Blackberry PIN
256 pin
= strtoul(optarg
, NULL
, 16);
259 case 'P': // Device password
263 case 'v': // data dump on
278 cerr
<< "Error: Missing raw channel name." << endl
;
284 cerr
<< "Error: Too many arguments." << endl
;
289 // Fetch command from remaining arguments
290 string channelName
= argv
[0];
296 // Warn if USB_DEBUG isn't set to 0, 1 or 2
297 // as that usually means libusb will write to STDOUT
298 char* val
= getenv("USB_DEBUG");
299 int parsedValue
= -1;
301 parsedValue
= atoi(val
);
303 if( parsedValue
!= 0 && parsedValue
!= 1 && parsedValue
!= 2 ) {
304 cerr
<< "Warning: Protocol dump enabled without setting USB_DEBUG to 0, 1 or 2.\n"
305 << " libusb might log to STDOUT and ruin data stream." << endl
;
309 // Initialize the barry library. Must be called before
311 Barry::Init(data_dump
, &cerr
);
313 // Probe the USB bus for Blackberry devices.
314 // If user has specified a PIN, search for it in the
315 // available device list here as well
317 int activeDevice
= probe
.FindActive(pin
);
318 if( activeDevice
== -1 ) {
319 cerr
<< "No device selected, or PIN not found" << endl
;
323 // Now get setup to open the channel.
325 cerr
<< "Connected to device, starting read/write\n";
328 volatile bool running
= true;
330 // Create the thing which will write onto stdout
331 // and perform other callback duties.
332 CallbackHandler
callbackHandler(running
, data_dump
);
334 // Start a thread to handle any data arriving from
336 auto_ptr
<ErrorHandlingSocketRoutingQueue
> router
;
337 router
.reset(new ErrorHandlingSocketRoutingQueue(running
));
338 router
->SpinoffReadThread();
340 // Create our controller object
341 Barry::Controller
con(probe
.Get(activeDevice
), *router
->GetSocketRoutingQueue());
343 Barry::Mode::RawChannel
rawChannel(con
, callbackHandler
);
345 // Try to open the requested channel now everything is setup
346 rawChannel
.Open(password
.c_str(), channelName
.c_str());
348 // We now have a thread running to read from the
349 // BB and write over stdout; in this thread we'll
350 // read from stdin and write to the BB.
351 const size_t bufSize
= rawChannel
.MaximumSendSize();
352 buf
= new unsigned char[bufSize
];
357 // Set up the signal restorers to restore signal
358 // handling (in their destructors) before the socket
359 // starts to be closed. This allows, for example,
360 // double control-c presses to stop graceful close
362 SignalRestorer
srh(SIGHUP
, oldSigHup
);
363 SignalRestorer
srt(SIGTERM
, oldSigTerm
);
364 SignalRestorer
sri(SIGINT
, oldSigInt
);
365 SignalRestorer
srq(SIGQUIT
, oldSigQuit
);
367 while( running
&& !signalReceived
) {
368 FD_SET(STDIN_FILENO
, &rfds
);
369 tv
.tv_sec
= READ_TIMEOUT_SECONDS
;
372 int ret
= select(1, &rfds
, NULL
, NULL
, &tv
);
374 cerr
<< "Select failed with errno: " << errno
<< endl
;
377 else if ( ret
&& FD_ISSET(STDIN_FILENO
, &rfds
) ) {
378 ssize_t haveRead
= read(STDIN_FILENO
, buf
, bufSize
);
380 Data
toWrite(buf
, haveRead
);
382 cerr
.setf(ios::dec
, ios::basefield
);
383 cerr
<< "Sending " << haveRead
<< " bytes stdin->USB\n";
385 toWrite
.DumpHex(cerr
);
388 rawChannel
.Send(toWrite
);
390 cerr
.setf(ios::dec
, ios::basefield
);
391 cerr
<< "Sent " << ios::dec
<< haveRead
<< " bytes stdin->USB\n";
394 else if( haveRead
< 0 ) {
400 catch( Usb::Error
&ue
) {
401 cerr
<< "Usb::Error caught: " << ue
.what() << endl
;
404 catch( Barry::Error
&se
) {
405 cerr
<< "Barry::Error caught: " << se
.what() << endl
;
408 catch( exception
&e
) {
409 cerr
<< "exception caught: " << e
.what() << endl
;