brawchannel.cc

Go to the documentation of this file.
00001 ///
00002 /// \file       brawchannel.cc
00003 ///             Directs a named raw channel over STDIN/STDOUT
00004 ///
00005 
00006 /*
00007     Copyright (C) 2010, RealVNC Ltd.
00008 
00009         Some parts are inspired from bjavaloader.cc
00010 
00011     This program is free software; you can redistribute it and/or modify
00012     it under the terms of the GNU General Public License as published by
00013     the Free Software Foundation; either version 2 of the License, or
00014     (at your option) any later version.
00015 
00016     This program is distributed in the hope that it will be useful,
00017     but WITHOUT ANY WARRANTY; without even the implied warranty of
00018     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00019 
00020     See the GNU General Public License in the COPYING file at the
00021     root directory of this project for more details.
00022 */
00023 
00024 
00025 #include <barry/barry.h>
00026 #include <iostream>
00027 #include <vector>
00028 #include <string>
00029 #include <cstring>
00030 #include <cstdio>
00031 #include <cstdlib>
00032 #include <algorithm>
00033 #include <fstream>
00034 #include <string.h>
00035 #include <sys/types.h>
00036 #include <sys/time.h>
00037 #include <unistd.h>
00038 #include <signal.h>
00039 #include <errno.h>
00040 #include <pthread.h>
00041 
00042 #include "i18n.h"
00043 #include "platform.h"
00044 #include "barrygetopt.h"
00045 
00046 using namespace std;
00047 using namespace Barry;
00048 
00049 // How long to wait between reads before checking if should shutdown
00050 #define READ_TIMEOUT_SECONDS 1
00051 
00052 static volatile bool signalReceived = false;
00053 
00054 static void signalHandler(int signum)
00055 {
00056         signalReceived = true;
00057 }
00058 
00059 class CallbackHandler : public Barry::Mode::RawChannelDataCallback
00060 {
00061 private:
00062         volatile bool *m_continuePtr;
00063         bool m_verbose;
00064 
00065 public:
00066         CallbackHandler(volatile bool &keepGoing, bool verbose)
00067                 : m_continuePtr(&keepGoing)
00068                 , m_verbose(verbose)
00069                 {
00070                 }
00071 
00072 
00073 public: // From RawChannelDataCallback
00074         virtual void DataReceived(Data &data);
00075         virtual void ChannelError(string msg);
00076         virtual void ChannelClose();
00077 };
00078 
00079 
00080 void CallbackHandler::DataReceived(Data &data)
00081 {
00082         if( m_verbose ) {
00083                 cerr << "From BB: ";
00084                 data.DumpHex(cerr);
00085                 cerr << "\n";
00086         }
00087 
00088         size_t toWrite = data.GetSize();
00089         size_t written = 0;
00090 
00091         while( written < toWrite && *m_continuePtr ) {
00092                 ssize_t writtenThisTime = write(STDOUT_FILENO, &(data.GetData()[written]), toWrite - written);
00093                 if( m_verbose ) {
00094                         cerr.setf(ios::dec, ios::basefield);
00095                         cerr << "Written " << writtenThisTime << " bytes over stdout" << endl;
00096                 }
00097                 fflush(stdout);
00098                 if( writtenThisTime < 0 ) {
00099                         ChannelClose();
00100                 }
00101                 else {
00102                         written += writtenThisTime;
00103                 }
00104         }
00105 }
00106 
00107 void CallbackHandler::ChannelError(string msg)
00108 {
00109         cerr << "CallbackHandler: Received error: " << msg << endl;
00110         ChannelClose();
00111 }
00112 
00113 void CallbackHandler::ChannelClose()
00114 {
00115         *m_continuePtr = false;
00116 }
00117 
00118 void Usage()
00119 {
00120         int logical, major, minor;
00121         const char *Version = Barry::Version(logical, major, minor);
00122 
00123         cerr
00124                 << "brawchannel - Command line USB Blackberry raw channel interface\n"
00125                 << "        Copyright 2010, RealVNC Ltd.\n"
00126                 << "        Using: " << Version << "\n"
00127                 << "\n"
00128                 << "Usage:\n"
00129                 << "brawchannel [options] <channel name>\n"
00130                 << "\n"
00131                 << "   -h        This help\n"
00132                 << "   -p pin    PIN of device to talk with\n"
00133                 << "             If only one device is plugged in, this flag is optional\n"
00134                 << "   -P pass   Simplistic method to specify device password\n"
00135                 << "   -v        Dump protocol data during operation\n"
00136                 << "             This will cause libusb output to appear on STDOUT unless\n"
00137                 << "             the environment variable USB_DEBUG is set to 0,1 or 2.\n"
00138                 << endl;
00139 }
00140 
00141 // Helper class to restore signal handlers when shutdown is occuring
00142 // This class isn't responsible for setting up the signal handlers
00143 // as they need to be restored before the Barry::Socket starts closing.
00144 class SignalRestorer
00145 {
00146 private:
00147         int m_signum;
00148         sighandler_t m_handler;
00149 public:
00150         SignalRestorer(int signum, sighandler_t handler)
00151                 : m_signum(signum), m_handler(handler) {}
00152         ~SignalRestorer() { signal(m_signum, m_handler); }
00153 };
00154 
00155 int main(int argc, char *argv[])
00156 {
00157         INIT_I18N(PACKAGE);
00158 
00159         // Setup signal handling
00160         sighandler_t oldSigHup = signal(SIGHUP, &signalHandler);
00161         sighandler_t oldSigTerm = signal(SIGTERM, &signalHandler);
00162         sighandler_t oldSigInt = signal(SIGINT, &signalHandler);
00163         sighandler_t oldSigQuit = signal(SIGQUIT, &signalHandler);
00164 
00165         cerr.sync_with_stdio(true);     // since libusb uses
00166                                         // stdio for debug messages
00167 
00168         // Buffer to hold data read in from STDIN before sending it
00169         // to the BlackBerry.
00170         unsigned char *buf = NULL;
00171         try {
00172                 uint32_t pin = 0;
00173                 bool data_dump = false;
00174                 string password;
00175 
00176                 // process command line options
00177                 for( ;; ) {
00178                         int cmd = getopt(argc, argv, "hp:P:v");
00179                         if( cmd == -1 ) {
00180                                 break;
00181                         }
00182 
00183                         switch( cmd )
00184                         {
00185                         case 'p':       // Blackberry PIN
00186                                 pin = strtoul(optarg, NULL, 16);
00187                                 break;
00188 
00189                         case 'P':       // Device password
00190                                 password = optarg;
00191                                 break;
00192 
00193                         case 'v':       // data dump on
00194                                 data_dump = true;
00195                                 break;
00196 
00197                         case 'h':       // help
00198                         default:
00199                                 Usage();
00200                         return 0;
00201                         }
00202                 }
00203 
00204                 argc -= optind;
00205                 argv += optind;
00206 
00207                 if( argc < 1 ) {
00208                         cerr << "Error: Missing raw channel name." << endl;
00209                         Usage();
00210                         return 1;
00211                 }
00212 
00213                 if( argc > 1 ) {
00214                         cerr << "Error: Too many arguments." << endl;
00215                         Usage();
00216                         return 1;
00217                 }
00218 
00219                 // Fetch command from remaining arguments
00220                 string channelName = argv[0];
00221                 argc --;
00222                 argv ++;
00223 
00224 
00225                 if( data_dump ) {
00226                         // Warn if USB_DEBUG isn't set to 0, 1 or 2
00227                         // as that usually means libusb will write to STDOUT
00228                         char *val = getenv("USB_DEBUG");
00229                         int parsedValue = -1;
00230                         if( val ) {
00231                                 parsedValue = atoi(val);
00232                         }
00233                         if( parsedValue != 0 && parsedValue != 1 && parsedValue != 2 ) {
00234                                 cerr << "Warning: Protocol dump enabled without setting USB_DEBUG to 0, 1 or 2.\n"
00235                                      << "         libusb might log to STDOUT and ruin data stream." << endl;
00236                         }
00237                 }
00238 
00239                 // Initialize the barry library.  Must be called before
00240                 // anything else.
00241                 Barry::Init(data_dump, &cerr);
00242 
00243                 // Probe the USB bus for Blackberry devices.
00244                 // If user has specified a PIN, search for it in the
00245                 // available device list here as well
00246                 Barry::Probe probe;
00247                 int activeDevice = probe.FindActive(pin);
00248                 if( activeDevice == -1 ) {
00249                         cerr << "No device selected, or PIN not found" << endl;
00250                         return 1;
00251                 }
00252 
00253                 // Now get setup to open the channel.
00254                 if( data_dump ) {
00255                         cerr << "Connected to device, starting read/write\n";
00256                 }
00257 
00258                 volatile bool running = true;
00259 
00260                 // Create the thing which will write onto stdout
00261                 // and perform other callback duties.
00262                 CallbackHandler callbackHandler(running, data_dump);
00263 
00264                 // Start a thread to handle any data arriving from
00265                 // the BlackBerry.
00266                 auto_ptr<SocketRoutingQueue> router;
00267                 router.reset(new SocketRoutingQueue());
00268                 router->SpinoffSimpleReadThread();
00269 
00270                 // Create our controller object
00271                 Barry::Controller con(probe.Get(activeDevice), *router);
00272 
00273                 Barry::Mode::RawChannel rawChannel(con, callbackHandler);
00274 
00275                 // Try to open the requested channel now everything is setup
00276                 rawChannel.Open(password.c_str(), channelName.c_str());
00277 
00278                 // We now have a thread running to read from the
00279                 // BB and write over stdout; in this thread we'll
00280                 // read from stdin and write to the BB.
00281                 const size_t bufSize = rawChannel.MaximumSendSize();
00282                 buf = new unsigned char[bufSize];
00283                 fd_set rfds;
00284                 struct timeval tv;
00285                 FD_ZERO(&rfds);
00286 
00287                 // Set up the signal restorers to restore signal
00288                 // handling (in their destructors) before the socket
00289                 // starts to be closed. This allows, for example,
00290                 // double control-c presses to stop graceful close
00291                 // down.
00292                 SignalRestorer srh(SIGHUP, oldSigHup);
00293                 SignalRestorer srt(SIGTERM, oldSigTerm);
00294                 SignalRestorer sri(SIGINT, oldSigInt);
00295                 SignalRestorer srq(SIGQUIT, oldSigQuit);
00296 
00297                 while( running && !signalReceived ) {
00298                         FD_SET(STDIN_FILENO, &rfds);
00299                         tv.tv_sec = READ_TIMEOUT_SECONDS;
00300                         tv.tv_usec = 0;
00301 
00302                         int ret = select(STDIN_FILENO + 1, &rfds, NULL, NULL, &tv);
00303                         if( ret < 0 ) {
00304                                 cerr << "Select failed with errno: " << errno << endl;
00305                                 running = false;
00306                         }
00307                         else if ( ret && FD_ISSET(STDIN_FILENO, &rfds) ) {
00308                                 ssize_t haveRead = read(STDIN_FILENO, buf, bufSize);
00309                                 if( haveRead > 0 ) {
00310                                         Data toWrite(buf, haveRead);
00311                                         if( data_dump ) {
00312                                                 cerr.setf(ios::dec, ios::basefield);
00313                                                 cerr << "Sending " << haveRead << " bytes stdin->USB\n";
00314                                                 cerr << "To BB: ";
00315                                                 toWrite.DumpHex(cerr);
00316                                                 cerr << "\n";
00317                                         }
00318                                         rawChannel.Send(toWrite);
00319                                         if( data_dump ) {
00320                                                 cerr.setf(ios::dec, ios::basefield);
00321                                                 cerr << "Sent " << haveRead << " bytes stdin->USB\n";
00322                                         }
00323                                 }
00324                                 else if( haveRead < 0 ) {
00325                                         running = false;
00326                                 }
00327                         }
00328                 }
00329         }
00330         catch( Usb::Error &ue ) {
00331                 cerr << "Usb::Error caught: " << ue.what() << endl;
00332                 return 1;
00333         }
00334         catch( Barry::Error &se ) {
00335                 cerr << "Barry::Error caught: " << se.what() << endl;
00336                 return 1;
00337         }
00338         catch( exception &e ) {
00339                 cerr << "exception caught: " << e.what() << endl;
00340                 return 1;
00341         }
00342 
00343         delete[] buf;
00344 
00345         return 0;
00346 }
00347