2 /// \file brawchannel.cc
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;
59 // Wrapper for pthread_mutex_t
70 pthread_mutex_t m_mutex
;
74 : m_initialized(false)
76 int ret
= pthread_mutex_init(&m_mutex
, NULL
);
78 throw Barry::Error("Mutex: failed to create mutex");
86 int ret
= pthread_mutex_destroy(&m_mutex
);
88 cerr
<< "Failed to destroy mutex with error: " << ret
<< endl
;
94 int ret
= pthread_mutex_lock(&m_mutex
);
96 throw Barry::Error("Mutex: failed to lock mutex");
101 int ret
= pthread_mutex_unlock(&m_mutex
);
103 throw Barry::Error("Mutex: failed to unlock mutex");
106 // RIAA wrapper for locking Mutex class
110 MutexLock(Mutex
& mutex
)
113 { mutex
.Lock(); m_locked
= true; };
122 ~MutexLock() { Unlock(); }
128 // Wrapper for pthread_cont_t
134 void Wait(Mutex
& mutex
);
142 : m_initialized(false)
144 int ret
= pthread_cond_init(&m_cv
, NULL
);
146 throw Barry::Error("Condvar: failed to create condvar");
147 m_initialized
= true;
154 int ret
= pthread_cond_destroy(&m_cv
);
156 cerr
<< "Failed to destroy condvar with error: " << ret
<< endl
;
160 void Condvar::Wait(Mutex
& mutex
)
162 int ret
= pthread_cond_wait(&m_cv
, &mutex
.m_mutex
);
164 throw Barry::Error("Condvar: failed to wait on condvar");
167 void Condvar::Signal()
169 int ret
= pthread_cond_signal(&m_cv
);
171 throw Barry::Error("Condvar: failed to signal condvar");
174 // Semaphore class for signalling between threads
178 Semaphore(int initialVal
= 0);
180 void WaitForSignal();
188 Semaphore::Semaphore(int initialVal
)
189 : m_value(initialVal
),
195 Semaphore::~Semaphore()
199 void Semaphore::WaitForSignal()
201 MutexLock
lock(m_mutex
);
202 while (m_value
<= 0) {
209 void Semaphore::Signal()
211 MutexLock
lock(m_mutex
);
217 class StdoutWriter
: public Barry::Mode::RawChannelDataCallback
220 StdoutWriter(volatile bool& keepGoing
, bool verbose
, Semaphore
& semaphore
)
221 : m_continuePtr(&keepGoing
),
223 m_semaphore(semaphore
)
228 public: // From RawChannelDataCallback
229 virtual void DataReceived(Data
& data
);
230 virtual void DataSendAck();
231 virtual void ChannelError(string msg
);
232 virtual void ChannelClose();
235 volatile bool* m_continuePtr
;
237 Semaphore
& m_semaphore
;
241 void StdoutWriter::DataReceived(Data
& data
)
249 size_t toWrite
= data
.GetSize();
252 while (written
< toWrite
&& *m_continuePtr
) {
253 ssize_t writtenThisTime
= write(STDOUT_FILENO
, &(data
.GetData()[written
]), toWrite
- written
);
255 cerr
.setf(ios::dec
, ios::basefield
);
256 cerr
<< "Written " << writtenThisTime
<< " bytes over stdout" << endl
;
259 if (writtenThisTime
< 0)
265 written
+= writtenThisTime
;
270 void StdoutWriter::DataSendAck()
272 m_semaphore
.Signal();
275 void StdoutWriter::ChannelError(string msg
)
277 cerr
<< "StdoutWriter: Received error: " << msg
<< endl
;
281 void StdoutWriter::ChannelClose()
283 *m_continuePtr
= false;
284 m_semaphore
.Signal();
287 // Class which adds error detection and setting of a continue boolean
288 // to false when an error is detected to SocketRoutingQueue.
289 // This code is heavily based on the thread creation code of
290 // SocketRoutingQueue, which sadly has too many private variables
291 // to just sub-class.
292 class ErrorHandlingSocketRoutingQueue
295 ErrorHandlingSocketRoutingQueue(volatile bool& continuePtr
, Semaphore
& semaphore
)
296 : m_socketRoutingQueue(),
297 m_continuePtr(&continuePtr
),
298 m_runningThread(false),
299 m_semaphore(semaphore
)
304 ~ErrorHandlingSocketRoutingQueue() {
305 // Is the read thread still running
306 if( m_runningThread
) {
307 m_runningThread
= false;
308 pthread_join(m_usb_read_thread
, NULL
);
312 // Utility function to make it easier to create the
313 // USB pure-read thread.
314 // Throws Barry::ErrnoError on thread creation error.
315 void SpinoffReadThread() {
316 // signal that it's ok to run inside the thread
317 if( m_runningThread
)
318 return; // already running
319 m_runningThread
= true;
321 // Start USB read thread, to handle all routing
322 int ret
= pthread_create(&m_usb_read_thread
, NULL
, &ReadThreadFunction
, this);
324 m_runningThread
= false;
325 throw Barry::ErrnoError("SocketRoutingQueue: Error creating USB read thread.", ret
);
329 SocketRoutingQueue
* GetSocketRoutingQueue() {
330 return &m_socketRoutingQueue
;
334 static void* ReadThreadFunction(void* userPtr
) {
335 ErrorHandlingSocketRoutingQueue
*q
= (ErrorHandlingSocketRoutingQueue
*)userPtr
;
337 // read from USB and write to stdout until finished
339 while (q
->m_runningThread
) {
340 if( !q
->m_socketRoutingQueue
.DoRead(msg
, READ_TIMEOUT_SECONDS
* 1000) &&
341 // Only report the first failure, so check m_continuePtr
343 cerr
<< "Error in ReadThread: " << msg
<< endl
;
344 *q
->m_continuePtr
= false;
345 q
->m_semaphore
.Signal();
352 SocketRoutingQueue m_socketRoutingQueue
;
353 volatile bool* m_continuePtr
;
354 volatile bool m_runningThread
;
355 pthread_t m_usb_read_thread
;
356 Semaphore
& m_semaphore
;
362 const char *Version
= Barry::Version(major
, minor
);
365 << "brawchannel - Command line USB Blackberry raw channel interface\n"
366 << " Copyright 2010, RealVNC Ltd.\n"
367 << " Using: " << Version
<< "\n"
370 << "brawchannel [options] <channel name>\n"
373 << " -p pin PIN of device to talk with\n"
374 << " If only one device is plugged in, this flag is optional\n"
375 << " -P pass Simplistic method to specify device password\n"
376 << " -v Dump protocol data during operation\n"
377 << " This will cause libusb output to appear on STDOUT unless\n"
378 << " the environment variable USB_DEBUG is set to 0,1 or 2.\n"
382 // Helper class to restore signal handlers when shutdown is occuring
383 // This class isn't responsible for setting up the signal handlers
384 // as they need to be restored before the Barry::Socket starts closing.
388 SignalRestorer(int signum
, sighandler_t handler
)
389 : m_signum(signum
), m_handler(handler
) {}
390 ~SignalRestorer() { signal(m_signum
, m_handler
); }
393 sighandler_t m_handler
;
396 int main(int argc
, char *argv
[])
400 // Setup signal handling
401 sighandler_t oldSigHup
= signal(SIGHUP
, &signalHandler
);
402 sighandler_t oldSigTerm
= signal(SIGTERM
, &signalHandler
);
403 sighandler_t oldSigInt
= signal(SIGINT
, &signalHandler
);
404 sighandler_t oldSigQuit
= signal(SIGQUIT
, &signalHandler
);
406 cerr
.sync_with_stdio(true); // since libusb uses
407 // stdio for debug messages
408 unsigned char* buf
= NULL
;
411 bool data_dump
= false;
413 vector
<string
> params
;
417 Usb::EndpointPair epOverride
;
419 // process command line options
421 int cmd
= getopt(argc
, argv
, "hp:P:v");
427 case 'p': // Blackberry PIN
428 pin
= strtoul(optarg
, NULL
, 16);
431 case 'P': // Device password
435 case 'v': // data dump on
450 cerr
<< "Error: Missing raw channel name." << endl
;
455 // Fetch command from remaining arguments
456 string channelName
= argv
[0];
460 // Put the remaining arguments into an array
461 for (; argc
> 0; argc
--, argv
++) {
462 params
.push_back(string(argv
[0]));
467 // Warn if USB_DEBUG isn't set to 0, 1 or 2
468 // as that usually means libusb will write to STDOUT
469 char* val
= getenv("USB_DEBUG");
470 int parsedValue
= -1;
473 parsedValue
= atoi(val
);
475 if(parsedValue
!= 0 && parsedValue
!= 1 && parsedValue
!= 2)
477 cerr
<< "Warning: Protocol dump enabled without setting USB_DEBUG to 0, 1 or 2.\n"
478 << " libusb might log to STDOUT and ruin data stream." << endl
;
482 // Initialize the barry library. Must be called before
484 Barry::Init(data_dump
, &cerr
);
486 // Probe the USB bus for Blackberry devices and display.
487 // If user has specified a PIN, search for it in the
488 // available device list here as well
490 int activeDevice
= probe
.FindActive(pin
);
491 if( activeDevice
== -1 ) {
492 cerr
<< "No device selected, or PIN not found" << endl
;
496 // Now start to read from stdin and get ready to write
497 // to the BlackBerry.
499 cerr
<< "Connected to device, starting read/write\n";
501 volatile bool running
= true;
505 // Create the thing which will write onto stdout
506 StdoutWriter
stdoutWriter(running
, data_dump
, sem
);
508 // Set up the BlackBerry gubbins
509 // Start a thread to handle any data arriving from
511 auto_ptr
<ErrorHandlingSocketRoutingQueue
> router
;
512 router
.reset(new ErrorHandlingSocketRoutingQueue(running
, sem
));
513 router
->SpinoffReadThread();
515 // Create our controller object
516 Barry::Controller
con(probe
.Get(activeDevice
), *router
->GetSocketRoutingQueue());
518 Barry::Mode::RawChannel
rawChannel(con
, stdoutWriter
);
521 // execute each mode that was turned on
523 rawChannel
.Open(password
.c_str(), channelName
.c_str());
525 // We now have a thread running to read from the
526 // BB and write over stdout; in this thread we'll
527 // read from stdin and write to the BB.
528 const size_t bufSize
= rawChannel
.MaximumSendSize();
529 buf
= new unsigned char[bufSize
];
534 SignalRestorer
srh(SIGHUP
, oldSigHup
);
535 SignalRestorer
srt(SIGTERM
, oldSigTerm
);
536 SignalRestorer
sri(SIGINT
, oldSigInt
);
537 SignalRestorer
srq(SIGQUIT
, oldSigQuit
);
539 while (running
&& !signalReceived
) {
540 FD_SET(STDIN_FILENO
, &rfds
);
541 tv
.tv_sec
= READ_TIMEOUT_SECONDS
;
544 int ret
= select(1, &rfds
, NULL
, NULL
, &tv
);
546 cerr
<< "Select failed with errno: " << errno
<< endl
;
549 else if (ret
&& FD_ISSET(STDIN_FILENO
, &rfds
)) {
550 ssize_t haveRead
= read(STDIN_FILENO
, buf
, bufSize
);
552 Data
toWrite(buf
, haveRead
);
554 cerr
.setf(ios::dec
, ios::basefield
);
555 cerr
<< "Sending " << haveRead
<< " bytes stdin->USB\n";
557 toWrite
.DumpHex(cerr
);
560 rawChannel
.Send(toWrite
);
562 cerr
.setf(ios::dec
, ios::basefield
);
563 cerr
<< "Sent " << ios::dec
<< haveRead
<< " bytes stdin->USB\n";
567 else if (haveRead
< 0) {
573 catch( Usb::Error
&ue
) {
574 cerr
<< "Usb::Error caught: " << ue
.what() << endl
;
577 catch( Barry::Error
&se
) {
578 cerr
<< "Barry::Error caught: " << se
.what() << endl
;
581 catch( exception
&e
) {
582 cerr
<< "exception caught: " << e
.what() << endl
;