# 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 <getopt.h>
    00034 #include <fstream>
    00035 #include <string.h>
    00036 #include <sys/types.h>
    00037 #include <sys/time.h>
    00038 #include <unistd.h>
    00039 #include <signal.h>
    00040 #include <errno.h>
    00041 #include <pthread.h>
    00042
    00043 #include "i18n.h"
    00044 #include "platform.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 major, minor;
    00121         const char *Version = Barry::Version(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