Updated .gitignore for the barry18 -> barry19 symlink
[barry.git] / tools / brawchannel.cc
blob32a4b9c64a9d854a1660ea6bfeaf6b1018ff6c3a
1 ///
2 /// \file brawchannel.cc
3 /// Directs a named raw channel over STDIN/STDOUT or TCP
4 ///
6 /*
7 Copyright (C) 2010-2012, 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 <fstream>
34 #include <string.h>
35 #include <sys/types.h>
36 #include <sys/time.h>
37 #include <unistd.h>
38 #include <signal.h>
39 #include <errno.h>
40 #include <pthread.h>
42 #include "i18n.h"
43 #include "platform.h"
44 #include "barrygetopt.h"
45 #include "brawchannel.h"
47 using namespace std;
48 using namespace Barry;
50 static volatile bool signalReceived = false;
52 static void signalHandler(int signum)
54 signalReceived = true;
57 class CallbackHandler : public Barry::Mode::RawChannelDataCallback
59 private:
60 OutputStream& m_output;
61 volatile bool *m_continuePtr;
62 bool m_verbose;
64 public:
65 CallbackHandler(OutputStream& output, volatile bool &keepGoing, bool verbose)
66 : m_output(output)
67 , m_continuePtr(&keepGoing)
68 , m_verbose(verbose)
73 public: // From RawChannelDataCallback
74 virtual void DataReceived(Data &data);
75 virtual void ChannelError(string msg);
76 virtual void ChannelClose();
80 void CallbackHandler::DataReceived(Data &data)
82 if( m_verbose ) {
83 cerr << _("From BB: ");
84 data.DumpHex(cerr);
85 cerr << "\n";
88 size_t toWrite = data.GetSize();
89 size_t written = 0;
91 while( written < toWrite && *m_continuePtr ) {
92 ssize_t writtenThisTime = m_output.write(&(data.GetData()[written]), toWrite - written);
93 if( m_verbose ) {
94 cerr.setf(ios::dec, ios::basefield);
95 cerr << string_vprintf(_("Written %ld bytes over stdout"), (long int)writtenThisTime) << endl;
97 fflush(stdout);
98 if( writtenThisTime < 0 ) {
99 ChannelClose();
101 else {
102 written += writtenThisTime;
107 void CallbackHandler::ChannelError(string msg)
109 cerr << _("CallbackHandler: Received error: ") << msg << endl;
110 ChannelClose();
113 void CallbackHandler::ChannelClose()
115 *m_continuePtr = false;
118 void Usage()
120 int logical, major, minor;
121 const char *Version = Barry::Version(logical, major, minor);
123 cerr << string_vprintf(
124 _("brawchannel - Command line USB Blackberry raw channel interface\n"
125 " Copyright 2010-2012, RealVNC Ltd.\n"
126 " Using: %s\n"
127 "\n"
128 "Usage:\n"
129 "brawchannel [options] <channel name>\n"
130 "\n"
131 " -h This help\n"
132 " -p pin PIN of device to talk with\n"
133 " If only one device is plugged in, this flag is optional\n"
134 " -P pass Simplistic method to specify device password\n"
135 " -l port Listen for a TCP connection on the provided port instead\n"
136 " of using STDIN and STDOUT for data\n"
137 " -a addr Address to bind the listening socket to, allowing listening\n"
138 " only on a specified interface\n"
139 " -v Dump protocol data during operation\n"
140 " This will cause libusb output to appear on STDOUT unless\n"
141 " the environment variable USB_DEBUG is set to 0,1 or 2.\n"),
142 Version)
143 << endl;
146 // Helper class to restore signal handlers when shutdown is occuring
147 // This class isn't responsible for setting up the signal handlers
148 // as they need to be restored before the Barry::Socket starts closing.
149 class SignalRestorer
151 private:
152 int m_signum;
153 sighandler_t m_handler;
154 public:
155 SignalRestorer(int signum, sighandler_t handler)
156 : m_signum(signum), m_handler(handler) {}
157 ~SignalRestorer() { signal(m_signum, m_handler); }
160 int main(int argc, char *argv[])
162 INIT_I18N(PACKAGE);
164 // Setup signal handling
165 sighandler_t oldSigHup = signal(SIGHUP, &signalHandler);
166 sighandler_t oldSigTerm = signal(SIGTERM, &signalHandler);
167 sighandler_t oldSigInt = signal(SIGINT, &signalHandler);
168 sighandler_t oldSigQuit = signal(SIGQUIT, &signalHandler);
170 cerr.sync_with_stdio(true); // since libusb uses
171 // stdio for debug messages
173 // Buffer to hold data read in from STDIN before sending it
174 // to the BlackBerry.
175 unsigned char *buf = NULL;
176 try {
177 uint32_t pin = 0;
178 bool data_dump = false;
179 string password;
180 char * tcp_addr = NULL;
181 long tcp_port = 0;
183 // process command line options
184 for( ;; ) {
185 int cmd = getopt(argc, argv, "hp:P:l:a:v");
186 if( cmd == -1 ) {
187 break;
190 switch( cmd )
192 case 'p': // Blackberry PIN
193 pin = strtoul(optarg, NULL, 16);
194 break;
196 case 'P': // Device password
197 password = optarg;
198 break;
200 case 'v': // data dump on
201 data_dump = true;
202 break;
204 case 'l':
205 tcp_port = strtol(optarg, NULL, 10);
206 break;
208 case 'a':
209 tcp_addr = optarg;
210 break;
212 case 'h': // help
213 default:
214 Usage();
215 return 0;
219 argc -= optind;
220 argv += optind;
222 if( argc < 1 ) {
223 cerr << _("Error: Missing raw channel name.") << endl;
224 Usage();
225 return 1;
228 if( argc > 1 ) {
229 cerr << _("Error: Too many arguments.") << endl;
230 Usage();
231 return 1;
234 // Fetch command from remaining arguments
235 string channelName = argv[0];
236 argc --;
237 argv ++;
239 if( tcp_addr != NULL && tcp_port == 0 ) {
240 cerr << _("Error: specified TCP listen address but no port.") << endl;
241 return 1;
244 if( data_dump ) {
245 // Warn if USB_DEBUG isn't set to 0, 1 or 2
246 // as that usually means libusb will write to STDOUT
247 char *val = getenv("USB_DEBUG");
248 int parsedValue = -1;
249 if( val ) {
250 parsedValue = atoi(val);
252 if( parsedValue != 0 && parsedValue != 1 && parsedValue != 2 ) {
253 cerr << _("Warning: Protocol dump enabled without setting USB_DEBUG to 0, 1 or 2.\n"
254 " libusb might log to STDOUT and ruin data stream.") << endl;
258 // Initialize the barry library. Must be called before
259 // anything else.
260 Barry::Init(data_dump, &cerr);
262 // Probe the USB bus for Blackberry devices.
263 // If user has specified a PIN, search for it in the
264 // available device list here as well
265 Barry::Probe probe;
266 int activeDevice = probe.FindActive(pin);
267 if( activeDevice == -1 ) {
268 cerr << _("No device selected, or PIN not found")
269 << endl;
270 return 1;
273 // Now get setup to open the channel.
274 if( data_dump ) {
275 cerr << _("Connected to device, starting read/write\n");
278 volatile bool running = true;
280 auto_ptr<TcpStream> tcpStreamPtr;
281 auto_ptr<InputStream> inputPtr;
282 auto_ptr<OutputStream> outputPtr;
284 if( tcp_port != 0 ) {
285 /* Use TCP socket for channel data */
286 tcpStreamPtr.reset(new TcpStream(tcp_addr, tcp_port));
287 if( !tcpStreamPtr->accept() )
288 return 1;
289 inputPtr.reset(new TcpInStream(*tcpStreamPtr));
290 outputPtr.reset(new TcpOutStream(*tcpStreamPtr));
291 } else {
292 /* Use STDIN and STDOUT for channel data */
293 inputPtr.reset(new StdInStream());
294 outputPtr.reset(new StdOutStream());
296 // Create the thing which will write onto stdout
297 // and perform other callback duties.
298 CallbackHandler callbackHandler(*outputPtr, running, data_dump);
300 // Start a thread to handle any data arriving from
301 // the BlackBerry.
302 auto_ptr<SocketRoutingQueue> router;
303 router.reset(new SocketRoutingQueue());
304 router->SpinoffSimpleReadThread();
306 // Create our controller object
307 Barry::Controller con(probe.Get(activeDevice), *router);
309 Barry::Mode::RawChannel rawChannel(con, callbackHandler);
311 // Try to open the requested channel now everything is setup
312 rawChannel.Open(password.c_str(), channelName.c_str());
314 // We now have a thread running to read from the
315 // BB and write over stdout; in this thread we'll
316 // read from stdin and write to the BB.
317 const size_t bufSize = rawChannel.MaximumSendSize();
318 buf = new unsigned char[bufSize];
320 // Set up the signal restorers to restore signal
321 // handling (in their destructors) before the socket
322 // starts to be closed. This allows, for example,
323 // double control-c presses to stop graceful close
324 // down.
325 SignalRestorer srh(SIGHUP, oldSigHup);
326 SignalRestorer srt(SIGTERM, oldSigTerm);
327 SignalRestorer sri(SIGINT, oldSigInt);
328 SignalRestorer srq(SIGQUIT, oldSigQuit);
330 while( running && !signalReceived ) {
331 ssize_t haveRead = inputPtr->read(buf, bufSize, READ_TIMEOUT_SECONDS);
332 if( haveRead > 0 ) {
333 Data toWrite(buf, haveRead);
334 if( data_dump ) {
335 cerr.setf(ios::dec, ios::basefield);
336 cerr << string_vprintf(_("Sending %ld bytes stdin->USB\n"), (long int)haveRead);
337 cerr << _("To BB: ");
338 toWrite.DumpHex(cerr);
339 cerr << "\n";
341 rawChannel.Send(toWrite);
342 if( data_dump ) {
343 cerr.setf(ios::dec, ios::basefield);
344 cerr << string_vprintf(_("Sent %ld bytes stdin->USB\n"), (long int)haveRead);
347 else if( haveRead < 0 ) {
348 running = false;
352 catch( const Usb::Error &ue ) {
353 cerr << _("Usb::Error caught: ") << ue.what() << endl;
354 return 1;
356 catch( const Barry::Error &se ) {
357 cerr << _("Barry::Error caught: ") << se.what() << endl;
358 return 1;
360 catch( const exception &e ) {
361 cerr << _("exception caught: ") << e.what() << endl;
362 return 1;
364 catch( ... ) {
365 cerr << _("unknown exception caught") << endl;
366 return 1;
369 delete[] buf;
371 return 0;