Fixing coding standards violations from Chris Frey's review. Also adding error detect...
[barry.git] / tools / brawchannel.cc
blob2c4e0e5ef9a1b6ca13ed84d3dba89d5e104ad882
1 ///
2 /// \file brawchannel.cc
3 /// Directs a named raw channel over STDIN/STDOUT
4 ///
6 /*
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>
26 #include <iostream>
27 #include <vector>
28 #include <string>
29 #include <cstring>
30 #include <cstdio>
31 #include <cstdlib>
32 #include <algorithm>
33 #include <getopt.h>
34 #include <fstream>
35 #include <string.h>
36 #include <unistd.h>
37 #include <sys/types.h>
38 #include <signal.h>
39 #include <errno.h>
40 #include <pthread.h>
42 #include "i18n.h"
44 using namespace std;
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 Condvar;
59 // Wrapper for pthread_mutex_t
60 class Mutex
62 private:
63 bool m_initialized;
64 pthread_mutex_t m_mutex;
65 public:
66 friend class Condvar;
67 Mutex();
68 ~Mutex();
69 void Lock();
70 void Unlock();
73 Mutex::Mutex()
74 : m_initialized(false)
76 int ret = pthread_mutex_init(&m_mutex, NULL);
77 if( ret != 0 ) {
78 throw Barry::Error("Mutex: failed to create mutex");
80 m_initialized = true;
83 Mutex::~Mutex()
85 if( m_initialized ) {
86 int ret = pthread_mutex_destroy(&m_mutex);
87 if( ret != 0 ) {
88 cerr << "Failed to destroy mutex with error: " << ret << endl;
93 void Mutex::Lock()
95 int ret = pthread_mutex_lock(&m_mutex);
96 if( ret != 0 ) {
97 throw Barry::Error("Mutex: failed to lock mutex");
101 void Mutex::Unlock()
103 int ret = pthread_mutex_unlock(&m_mutex);
104 if( ret != 0 ) {
105 throw Barry::Error("Mutex: failed to unlock mutex");
109 // RAII wrapper for locking Mutex class
110 class MutexLock
112 private:
113 bool m_locked;
114 Mutex& m_mutex;
115 public:
116 MutexLock(Mutex& mutex)
117 : m_locked(false)
118 , m_mutex(mutex)
119 { mutex.Lock(); m_locked = true; };
120 void Unlock()
122 if( m_locked ) {
123 m_mutex.Unlock();
124 m_locked = false;
127 ~MutexLock() { Unlock(); }
130 // Wrapper for pthread_cont_t
131 class Condvar
133 private:
134 bool m_initialized;
135 pthread_cond_t m_cv;
136 public:
137 Condvar();
138 ~Condvar();
139 void Wait(Mutex& mutex);
140 void Signal();
143 Condvar::Condvar()
144 : m_initialized(false)
146 int ret = pthread_cond_init(&m_cv, NULL);
147 if( ret != 0 ) {
148 throw Barry::Error("Condvar: failed to create condvar");
150 m_initialized = true;
153 Condvar::~Condvar()
155 if( m_initialized ) {
156 int ret = pthread_cond_destroy(&m_cv);
157 if (ret != 0)
158 cerr << "Failed to destroy condvar with error: " << ret << endl;
162 void Condvar::Wait(Mutex& mutex)
164 int ret = pthread_cond_wait(&m_cv, &mutex.m_mutex);
165 if( ret != 0 ) {
166 throw Barry::Error("Condvar: failed to wait on condvar");
170 void Condvar::Signal()
172 int ret = pthread_cond_signal(&m_cv);
173 if( ret != 0 ) {
174 throw Barry::Error("Condvar: failed to signal condvar");
178 // Semaphore class for signalling between threads
179 class Semaphore
181 private:
182 int m_value;
183 Mutex m_mutex;
184 Condvar m_cv;
185 public:
186 Semaphore(int initialVal = 0);
187 ~Semaphore();
188 void WaitForSignal();
189 void Signal();
192 Semaphore::Semaphore(int initialVal)
193 : m_value(initialVal)
194 , m_mutex()
195 , m_cv()
199 Semaphore::~Semaphore()
203 void Semaphore::WaitForSignal()
205 MutexLock lock(m_mutex);
206 while( m_value <= 0 ) {
207 m_cv.Wait(m_mutex);
209 --m_value;
210 lock.Unlock();
213 void Semaphore::Signal()
215 MutexLock lock(m_mutex);
216 ++m_value;
217 m_cv.Signal();
218 lock.Unlock();
221 class CallbackHandler : public Barry::Mode::RawChannelDataCallback
223 private:
224 volatile bool* m_continuePtr;
225 bool m_verbose;
226 Semaphore& m_semaphore;
228 public:
229 CallbackHandler(volatile bool& keepGoing, bool verbose, Semaphore& semaphore)
230 : m_continuePtr(&keepGoing)
231 , m_verbose(verbose)
232 , m_semaphore(semaphore)
237 public: // From RawChannelDataCallback
238 virtual void DataReceived(Data& data);
239 virtual void DataSendAck();
240 virtual void ChannelError(string msg);
241 virtual void ChannelClose();
245 void CallbackHandler::DataReceived(Data& data)
247 if( m_verbose ) {
248 cerr << "From BB: ";
249 data.DumpHex(cerr);
250 cerr << "\n";
253 size_t toWrite = data.GetSize();
254 size_t written = 0;
256 while( written < toWrite && *m_continuePtr ) {
257 ssize_t writtenThisTime = write(STDOUT_FILENO, &(data.GetData()[written]), toWrite - written);
258 if( m_verbose ) {
259 cerr.setf(ios::dec, ios::basefield);
260 cerr << "Written " << writtenThisTime << " bytes over stdout" << endl;
262 fflush(stdout);
263 if( writtenThisTime < 0 ) {
264 ChannelClose();
266 else {
267 written += writtenThisTime;
272 void CallbackHandler::DataSendAck()
274 m_semaphore.Signal();
277 void CallbackHandler::ChannelError(string msg)
279 cerr << "CallbackHandler: Received error: " << msg << endl;
280 ChannelClose();
283 void CallbackHandler::ChannelClose()
285 *m_continuePtr = false;
286 m_semaphore.Signal();
289 // Class which extends the functionality of SocketRoutingQueue to add
290 // error detection and setting of a continue boolean to false when an
291 // error is detected.
292 // This code is heavily based on the thread
293 // creation code of SocketRoutingQueue, which sadly has too many
294 // private variables to just sub-class.
295 class ErrorHandlingSocketRoutingQueue
297 protected:
298 static void* ReadThreadFunction(void* userPtr)
300 ErrorHandlingSocketRoutingQueue *q = (ErrorHandlingSocketRoutingQueue *)userPtr;
302 // read from USB and write to stdout until finished
303 string msg;
304 while( q->m_runningThread ) {
305 if( !q->m_socketRoutingQueue.DoRead(msg, READ_TIMEOUT_SECONDS * 1000) &&
306 // Only report the first failure, so check m_continuePtr
307 *q->m_continuePtr ) {
308 cerr << "Error in ReadThread: " << msg << endl;
309 *q->m_continuePtr = false;
310 q->m_semaphore.Signal();
313 return 0;
316 SocketRoutingQueue m_socketRoutingQueue;
317 volatile bool* m_continuePtr;
318 volatile bool m_runningThread;
319 pthread_t m_usb_read_thread;
320 Semaphore& m_semaphore;
321 public:
322 ErrorHandlingSocketRoutingQueue(volatile bool& continuePtr, Semaphore& semaphore)
323 : m_socketRoutingQueue()
324 , m_continuePtr(&continuePtr)
325 , m_runningThread(false)
326 , m_semaphore(semaphore)
328 // Nothing to do
331 ~ErrorHandlingSocketRoutingQueue()
333 // Is the read thread still running
334 if( m_runningThread ) {
335 m_runningThread = false;
336 pthread_join(m_usb_read_thread, NULL);
340 // Utility function to make it easier to create the
341 // USB pure-read thread.
342 // Throws Barry::ErrnoError on thread creation error.
343 void SpinoffReadThread()
345 // signal that it's ok to run inside the thread
346 if( m_runningThread )
347 return; // already running
348 m_runningThread = true;
350 // Start USB read thread, to handle all routing
351 int ret = pthread_create(&m_usb_read_thread, NULL, &ReadThreadFunction, this);
352 if( ret ) {
353 m_runningThread = false;
354 throw Barry::ErrnoError("SocketRoutingQueue: Error creating USB read thread.", ret);
358 SocketRoutingQueue* GetSocketRoutingQueue()
360 return &m_socketRoutingQueue;
364 void Usage()
366 int major, minor;
367 const char *Version = Barry::Version(major, minor);
369 cerr
370 << "brawchannel - Command line USB Blackberry raw channel interface\n"
371 << " Copyright 2010, RealVNC Ltd.\n"
372 << " Using: " << Version << "\n"
373 << "\n"
374 << "Usage:\n"
375 << "brawchannel [options] <channel name>\n"
376 << "\n"
377 << " -h This help\n"
378 << " -p pin PIN of device to talk with\n"
379 << " If only one device is plugged in, this flag is optional\n"
380 << " -P pass Simplistic method to specify device password\n"
381 << " -v Dump protocol data during operation\n"
382 << " This will cause libusb output to appear on STDOUT unless\n"
383 << " the environment variable USB_DEBUG is set to 0,1 or 2.\n"
384 << endl;
387 // Helper class to restore signal handlers when shutdown is occuring
388 // This class isn't responsible for setting up the signal handlers
389 // as they need to be restored before the Barry::Socket starts closing.
390 class SignalRestorer
392 private:
393 int m_signum;
394 sighandler_t m_handler;
395 public:
396 SignalRestorer(int signum, sighandler_t handler)
397 : m_signum(signum), m_handler(handler) {}
398 ~SignalRestorer() { signal(m_signum, m_handler); }
401 int main(int argc, char *argv[])
403 INIT_I18N(PACKAGE);
405 // Setup signal handling
406 sighandler_t oldSigHup = signal(SIGHUP, &signalHandler);
407 sighandler_t oldSigTerm = signal(SIGTERM, &signalHandler);
408 sighandler_t oldSigInt = signal(SIGINT, &signalHandler);
409 sighandler_t oldSigQuit = signal(SIGQUIT, &signalHandler);
411 cerr.sync_with_stdio(true); // since libusb uses
412 // stdio for debug messages
414 // Buffer to hold data read in from STDIN before sending it
415 // to the BlackBerry.
416 unsigned char* buf = NULL;
417 try {
418 uint32_t pin = 0;
419 bool data_dump = false;
420 string password;
422 // process command line options
423 for( ;; ) {
424 int cmd = getopt(argc, argv, "hp:P:v");
425 if( cmd == -1 ) {
426 break;
429 switch( cmd )
431 case 'p': // Blackberry PIN
432 pin = strtoul(optarg, NULL, 16);
433 break;
435 case 'P': // Device password
436 password = optarg;
437 break;
439 case 'v': // data dump on
440 data_dump = true;
441 break;
443 case 'h': // help
444 default:
445 Usage();
446 return 0;
450 argc -= optind;
451 argv += optind;
453 if( argc < 1 ) {
454 cerr << "Error: Missing raw channel name." << endl;
455 Usage();
456 return 1;
459 if( argc > 1 ) {
460 cerr << "Error: Too many arguments." << endl;
461 Usage();
462 return 1;
465 // Fetch command from remaining arguments
466 string channelName = argv[0];
467 argc --;
468 argv ++;
471 if( data_dump ) {
472 // Warn if USB_DEBUG isn't set to 0, 1 or 2
473 // as that usually means libusb will write to STDOUT
474 char* val = getenv("USB_DEBUG");
475 int parsedValue = -1;
476 if( val ) {
477 parsedValue = atoi(val);
479 if( parsedValue != 0 && parsedValue != 1 && parsedValue != 2 ) {
480 cerr << "Warning: Protocol dump enabled without setting USB_DEBUG to 0, 1 or 2.\n"
481 << " libusb might log to STDOUT and ruin data stream." << endl;
485 // Initialize the barry library. Must be called before
486 // anything else.
487 Barry::Init(data_dump, &cerr);
489 // Probe the USB bus for Blackberry devices.
490 // If user has specified a PIN, search for it in the
491 // available device list here as well
492 Barry::Probe probe;
493 int activeDevice = probe.FindActive(pin);
494 if( activeDevice == -1 ) {
495 cerr << "No device selected, or PIN not found" << endl;
496 return 1;
499 // Now get setup to open the channel.
500 if( data_dump ) {
501 cerr << "Connected to device, starting read/write\n";
504 volatile bool running = true;
506 Semaphore sem;
508 // Create the thing which will write onto stdout
509 // and perform other callback duties.
510 CallbackHandler callbackHandler(running, data_dump, sem);
512 // Start a thread to handle any data arriving from
513 // the BlackBerry.
514 auto_ptr<ErrorHandlingSocketRoutingQueue> router;
515 router.reset(new ErrorHandlingSocketRoutingQueue(running, sem));
516 router->SpinoffReadThread();
518 // Create our controller object
519 Barry::Controller con(probe.Get(activeDevice), *router->GetSocketRoutingQueue());
521 Barry::Mode::RawChannel rawChannel(con, callbackHandler);
523 // Try to open the requested channel now everything is setup
524 rawChannel.Open(password.c_str(), channelName.c_str());
526 // We now have a thread running to read from the
527 // BB and write over stdout; in this thread we'll
528 // read from stdin and write to the BB.
529 const size_t bufSize = rawChannel.MaximumSendSize();
530 buf = new unsigned char[bufSize];
531 fd_set rfds;
532 struct timeval tv;
533 FD_ZERO(&rfds);
535 // Set up the signal restorers to restore signal
536 // handling (in their destructors) before the socket
537 // starts to be closed. This allows, for example,
538 // double control-c presses to stop graceful close
539 // down.
540 SignalRestorer srh(SIGHUP, oldSigHup);
541 SignalRestorer srt(SIGTERM, oldSigTerm);
542 SignalRestorer sri(SIGINT, oldSigInt);
543 SignalRestorer srq(SIGQUIT, oldSigQuit);
545 while( running && !signalReceived ) {
546 FD_SET(STDIN_FILENO, &rfds);
547 tv.tv_sec = READ_TIMEOUT_SECONDS;
548 tv.tv_usec = 0;
550 int ret = select(1, &rfds, NULL, NULL, &tv);
551 if( ret < 0 ) {
552 cerr << "Select failed with errno: " << errno << endl;
553 running = false;
555 else if ( ret && FD_ISSET(STDIN_FILENO, &rfds) ) {
556 ssize_t haveRead = read(STDIN_FILENO, buf, bufSize);
557 if( haveRead > 0 ) {
558 Data toWrite(buf, haveRead);
559 if( data_dump ) {
560 cerr.setf(ios::dec, ios::basefield);
561 cerr << "Sending " << haveRead << " bytes stdin->USB\n";
562 cerr << "To BB: ";
563 toWrite.DumpHex(cerr);
564 cerr << "\n";
566 rawChannel.Send(toWrite);
567 if( data_dump ) {
568 cerr.setf(ios::dec, ios::basefield);
569 cerr << "Sent " << ios::dec << haveRead << " bytes stdin->USB\n";
571 // Wait for the write to be completed before reading
572 // the next data to send.
573 sem.WaitForSignal();
575 else if( haveRead < 0 ) {
576 running = false;
581 catch( Usb::Error &ue ) {
582 cerr << "Usb::Error caught: " << ue.what() << endl;
583 return 1;
585 catch( Barry::Error &se ) {
586 cerr << "Barry::Error caught: " << se.what() << endl;
587 return 1;
589 catch( exception &e ) {
590 cerr << "exception caught: " << e.what() << endl;
591 return 1;
594 delete[] buf;
596 return 0;