Add back the clarified logging from commit c7685942140b123bf11
[barry.git] / tools / brawchannel.cc
blobf4a2b8804aea0271804a024e571010e6449239ac
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"
43 #include "platform.h"
45 using namespace std;
46 using namespace Barry;
48 // How long to wait between reads before checking if should shutdown
49 #define READ_TIMEOUT_SECONDS 1
51 static volatile bool signalReceived = false;
53 static void signalHandler(int signum)
55 signalReceived = true;
58 class CallbackHandler : public Barry::Mode::RawChannelDataCallback
60 private:
61 volatile bool *m_continuePtr;
62 bool m_verbose;
64 public:
65 CallbackHandler(volatile bool &keepGoing, bool verbose)
66 : m_continuePtr(&keepGoing)
67 , m_verbose(verbose)
72 public: // From RawChannelDataCallback
73 virtual void DataReceived(Data &data);
74 virtual void ChannelError(string msg);
75 virtual void ChannelClose();
79 void CallbackHandler::DataReceived(Data &data)
81 if( m_verbose ) {
82 cerr << "From BB: ";
83 data.DumpHex(cerr);
84 cerr << "\n";
87 size_t toWrite = data.GetSize();
88 size_t written = 0;
90 while( written < toWrite && *m_continuePtr ) {
91 ssize_t writtenThisTime = write(STDOUT_FILENO, &(data.GetData()[written]), toWrite - written);
92 if( m_verbose ) {
93 cerr.setf(ios::dec, ios::basefield);
94 cerr << "Written " << writtenThisTime << " bytes over stdout" << endl;
96 fflush(stdout);
97 if( writtenThisTime < 0 ) {
98 ChannelClose();
100 else {
101 written += writtenThisTime;
106 void CallbackHandler::ChannelError(string msg)
108 cerr << "CallbackHandler: Received error: " << msg << endl;
109 ChannelClose();
112 void CallbackHandler::ChannelClose()
114 *m_continuePtr = false;
117 void Usage()
119 int major, minor;
120 const char *Version = Barry::Version(major, minor);
122 cerr
123 << "brawchannel - Command line USB Blackberry raw channel interface\n"
124 << " Copyright 2010, RealVNC Ltd.\n"
125 << " Using: " << Version << "\n"
126 << "\n"
127 << "Usage:\n"
128 << "brawchannel [options] <channel name>\n"
129 << "\n"
130 << " -h This help\n"
131 << " -p pin PIN of device to talk with\n"
132 << " If only one device is plugged in, this flag is optional\n"
133 << " -P pass Simplistic method to specify device password\n"
134 << " -v Dump protocol data during operation\n"
135 << " This will cause libusb output to appear on STDOUT unless\n"
136 << " the environment variable USB_DEBUG is set to 0,1 or 2.\n"
137 << endl;
140 // Helper class to restore signal handlers when shutdown is occuring
141 // This class isn't responsible for setting up the signal handlers
142 // as they need to be restored before the Barry::Socket starts closing.
143 class SignalRestorer
145 private:
146 int m_signum;
147 sighandler_t m_handler;
148 public:
149 SignalRestorer(int signum, sighandler_t handler)
150 : m_signum(signum), m_handler(handler) {}
151 ~SignalRestorer() { signal(m_signum, m_handler); }
154 int main(int argc, char *argv[])
156 INIT_I18N(PACKAGE);
158 // Setup signal handling
159 sighandler_t oldSigHup = signal(SIGHUP, &signalHandler);
160 sighandler_t oldSigTerm = signal(SIGTERM, &signalHandler);
161 sighandler_t oldSigInt = signal(SIGINT, &signalHandler);
162 sighandler_t oldSigQuit = signal(SIGQUIT, &signalHandler);
164 cerr.sync_with_stdio(true); // since libusb uses
165 // stdio for debug messages
167 // Buffer to hold data read in from STDIN before sending it
168 // to the BlackBerry.
169 unsigned char *buf = NULL;
170 try {
171 uint32_t pin = 0;
172 bool data_dump = false;
173 string password;
175 // process command line options
176 for( ;; ) {
177 int cmd = getopt(argc, argv, "hp:P:v");
178 if( cmd == -1 ) {
179 break;
182 switch( cmd )
184 case 'p': // Blackberry PIN
185 pin = strtoul(optarg, NULL, 16);
186 break;
188 case 'P': // Device password
189 password = optarg;
190 break;
192 case 'v': // data dump on
193 data_dump = true;
194 break;
196 case 'h': // help
197 default:
198 Usage();
199 return 0;
203 argc -= optind;
204 argv += optind;
206 if( argc < 1 ) {
207 cerr << "Error: Missing raw channel name." << endl;
208 Usage();
209 return 1;
212 if( argc > 1 ) {
213 cerr << "Error: Too many arguments." << endl;
214 Usage();
215 return 1;
218 // Fetch command from remaining arguments
219 string channelName = argv[0];
220 argc --;
221 argv ++;
224 if( data_dump ) {
225 // Warn if USB_DEBUG isn't set to 0, 1 or 2
226 // as that usually means libusb will write to STDOUT
227 char *val = getenv("USB_DEBUG");
228 int parsedValue = -1;
229 if( val ) {
230 parsedValue = atoi(val);
232 if( parsedValue != 0 && parsedValue != 1 && parsedValue != 2 ) {
233 cerr << "Warning: Protocol dump enabled without setting USB_DEBUG to 0, 1 or 2.\n"
234 << " libusb might log to STDOUT and ruin data stream." << endl;
238 // Initialize the barry library. Must be called before
239 // anything else.
240 Barry::Init(data_dump, &cerr);
242 // Probe the USB bus for Blackberry devices.
243 // If user has specified a PIN, search for it in the
244 // available device list here as well
245 Barry::Probe probe;
246 int activeDevice = probe.FindActive(pin);
247 if( activeDevice == -1 ) {
248 cerr << "No device selected, or PIN not found" << endl;
249 return 1;
252 // Now get setup to open the channel.
253 if( data_dump ) {
254 cerr << "Connected to device, starting read/write\n";
257 volatile bool running = true;
259 // Create the thing which will write onto stdout
260 // and perform other callback duties.
261 CallbackHandler callbackHandler(running, data_dump);
263 // Start a thread to handle any data arriving from
264 // the BlackBerry.
265 auto_ptr<SocketRoutingQueue> router;
266 router.reset(new SocketRoutingQueue());
267 router->SpinoffSimpleReadThread();
269 // Create our controller object
270 Barry::Controller con(probe.Get(activeDevice), *router);
272 Barry::Mode::RawChannel rawChannel(con, callbackHandler);
274 // Try to open the requested channel now everything is setup
275 rawChannel.Open(password.c_str(), channelName.c_str());
277 // We now have a thread running to read from the
278 // BB and write over stdout; in this thread we'll
279 // read from stdin and write to the BB.
280 const size_t bufSize = rawChannel.MaximumSendSize();
281 buf = new unsigned char[bufSize];
282 fd_set rfds;
283 struct timeval tv;
284 FD_ZERO(&rfds);
286 // Set up the signal restorers to restore signal
287 // handling (in their destructors) before the socket
288 // starts to be closed. This allows, for example,
289 // double control-c presses to stop graceful close
290 // down.
291 SignalRestorer srh(SIGHUP, oldSigHup);
292 SignalRestorer srt(SIGTERM, oldSigTerm);
293 SignalRestorer sri(SIGINT, oldSigInt);
294 SignalRestorer srq(SIGQUIT, oldSigQuit);
296 while( running && !signalReceived ) {
297 FD_SET(STDIN_FILENO, &rfds);
298 tv.tv_sec = READ_TIMEOUT_SECONDS;
299 tv.tv_usec = 0;
301 int ret = select(STDIN_FILENO + 1, &rfds, NULL, NULL, &tv);
302 if( ret < 0 ) {
303 cerr << "Select failed with errno: " << errno << endl;
304 running = false;
306 else if ( ret && FD_ISSET(STDIN_FILENO, &rfds) ) {
307 ssize_t haveRead = read(STDIN_FILENO, buf, bufSize);
308 if( haveRead > 0 ) {
309 Data toWrite(buf, haveRead);
310 if( data_dump ) {
311 cerr.setf(ios::dec, ios::basefield);
312 cerr << "Sending " << haveRead << " bytes stdin->USB\n";
313 cerr << "To BB: ";
314 toWrite.DumpHex(cerr);
315 cerr << "\n";
317 rawChannel.Send(toWrite);
318 if( data_dump ) {
319 cerr.setf(ios::dec, ios::basefield);
320 cerr << "Sent " << haveRead << " bytes stdin->USB\n";
323 else if( haveRead < 0 ) {
324 running = false;
329 catch( Usb::Error &ue ) {
330 cerr << "Usb::Error caught: " << ue.what() << endl;
331 return 1;
333 catch( Barry::Error &se ) {
334 cerr << "Barry::Error caught: " << se.what() << endl;
335 return 1;
337 catch( exception &e ) {
338 cerr << "exception caught: " << e.what() << endl;
339 return 1;
342 delete[] buf;
344 return 0;