pppob.cc

Go to the documentation of this file.
00001 ///
00002 /// \file       pppob.cc
00003 ///             In the same vein as pppoe, used with pppd to create a
00004 ///             pty tunnel and GPRS modem link.
00005 ///
00006 
00007 /*
00008     Copyright (C) 2007-2012, Net Direct Inc. (http://www.netdirect.ca/)
00009 
00010     This program is free software; you can redistribute it and/or modify
00011     it under the terms of the GNU General Public License as published by
00012     the Free Software Foundation; either version 2 of the License, or
00013     (at your option) any later version.
00014 
00015     This program is distributed in the hope that it will be useful,
00016     but WITHOUT ANY WARRANTY; without even the implied warranty of
00017     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00018 
00019     See the GNU General Public License in the COPYING file at the
00020     root directory of this project for more details.
00021 */
00022 
00023 #include <barry/barry.h>
00024 #include <iomanip>
00025 #include <iostream>
00026 #include <fstream>
00027 #include <vector>
00028 #include <string>
00029 #include <memory>
00030 #include <stdlib.h>
00031 #include <sys/select.h>
00032 #include <sys/time.h>
00033 #include <sys/types.h>
00034 #include <unistd.h>
00035 #include <signal.h>
00036 #include <errno.h>
00037 #include "i18n.h"
00038 
00039 #include "barrygetopt.h"
00040 
00041 using namespace std;
00042 using namespace Barry;
00043 
00044 bool data_dump = false;
00045 volatile bool signal_end = false;
00046 
00047 void Usage()
00048 {
00049    int logical, major, minor;
00050    const char *Version = Barry::Version(logical, major, minor);
00051 
00052    cerr
00053    << "pppob - PPP over Barry\n"
00054    << "        Copyright 2007-2012, Net Direct Inc. (http://www.netdirect.ca/)\n"
00055    << "        Using: " << Version << "\n"
00056    << "\n"
00057    << "   -l file   Direct pppob log output to file (useful with -v)\n"
00058    << "   -p pin    PIN of device to talk with\n"
00059    << "             If only one device plugged in, this flag is optional\n"
00060    << "   -P pass   Simplistic method to specify device password\n"
00061    << "   -s        Use Serial mode instead of IpModem\n"
00062    << "   -v        Dump protocol data during operation (debugging only!)\n"
00063    << endl;
00064 }
00065 
00066 void signal_handler(int signum)
00067 {
00068         signal_end = true;
00069 }
00070 
00071 void SerialDataCallback(void *context, const unsigned char *data, int len)
00072 {
00073         if( len && data_dump )
00074                 barryverbose("ReadThread:\n" << Data(data, len));
00075 
00076         while( len ) {
00077                 int written = write(1, data, len);
00078                 if( written > 0 ) {
00079                         len -= written;
00080                         data += written;
00081                 }
00082                 else {
00083                         barryverbose("Error in write()");
00084                 }
00085         }
00086 }
00087 
00088 void ProcessStdin(Modem &modem)
00089 {
00090         // Read from stdin and write to USB, until
00091         // stdin is closed
00092         Data data;
00093         int bytes_read;
00094         fd_set rfds;
00095         struct timeval tv;
00096         int ret;
00097 
00098         // Handle interrupt signals from pppd
00099         signal_end = false;
00100         signal(SIGINT, &signal_handler);
00101         signal(SIGHUP, &signal_handler);
00102         signal(SIGTERM, &signal_handler);
00103 
00104         FD_ZERO(&rfds);
00105         while( signal_end == false ) {
00106                 // Need to use select() here, so that pppd doesn't
00107                 // hang when it tries to set the line discipline
00108                 // on our stdin.
00109 
00110                 FD_SET(0, &rfds);
00111                 tv.tv_sec = 30;
00112                 tv.tv_usec = 0;
00113 
00114                 ret = select(1, &rfds, NULL, NULL, &tv);
00115                 if( ret == -1 ) {
00116                         perror("select()");
00117                 }
00118                 else if( ret && FD_ISSET(0, &rfds) ) {
00119                         bytes_read = read(0, data.GetBuffer(), data.GetBufSize());
00120                         if( bytes_read == 0 )
00121                                 break;  // end of file
00122                         else if( bytes_read > 0 ) {
00123                                 data.ReleaseBuffer(bytes_read);
00124                                 modem.Write(data);
00125                         }
00126                         else {
00127                                 // read error
00128                                 barryverbose("Read error in ProcessStdin: " << strerror(errno));
00129                                 break;
00130                         }
00131                 }
00132         }
00133 }
00134 
00135 int main(int argc, char *argv[])
00136 {
00137         INIT_I18N(PACKAGE);
00138 
00139         cout.sync_with_stdio(true);     // leave this on, since libusb uses
00140                                         // stdio for debug messages
00141 
00142         try {
00143 
00144                 uint32_t pin = 0;
00145                 bool force_serial = false;
00146                 std::string logfile;
00147                 std::string password;
00148 
00149                 // check for options via the fifo first, so the command
00150                 // line args can override them
00151                 FifoClient fifo;
00152                 if( fifo.Fetch(4) ) {
00153                         const FifoArgs &args = fifo.GetArgs();
00154                         pin = args.m_pin.Value();
00155                         force_serial = args.m_use_serial_mode;
00156                         logfile = args.m_log_filename;
00157                         password = args.m_password;
00158                         data_dump = args.m_verbose;
00159                 }
00160 
00161                 // process command line options
00162                 for(;;) {
00163                         int cmd = getopt(argc, argv, "l:p:P:sv");
00164                         if( cmd == -1 )
00165                                 break;
00166 
00167                         switch( cmd )
00168                         {
00169                         case 'l':       // Verbose log file
00170                                 logfile = optarg;
00171                                 break;
00172 
00173                         case 'p':       // Blackberry PIN
00174                                 pin = strtoul(optarg, NULL, 16);
00175                                 break;
00176 
00177                         case 'P':       // Device password
00178                                 password = optarg;
00179                                 break;
00180 
00181                         case 's':       // Use Serial mode
00182                                 force_serial = true;
00183                                 break;
00184 
00185                         case 'v':       // data dump on
00186                                 data_dump = true;
00187                                 break;
00188 
00189                         case 'h':       // help
00190                         default:
00191                                 Usage();
00192                                 return 0;
00193                         }
00194                 }
00195 
00196                 // Initialize the barry library.  Must be called before
00197                 // anything else.
00198                 // Log to stderr, since stdout is for data in this program.
00199                 std::auto_ptr<std::ofstream> log;
00200                 if( logfile.size() ) {
00201                         log.reset( new std::ofstream(logfile.c_str(), ios::app) );
00202                         Barry::Init(data_dump, log.get());
00203                 }
00204                 else {
00205                         Barry::Init(data_dump, &std::cerr);
00206                 }
00207 
00208                 // Display version if in data_dump mode
00209                 if( data_dump ) {
00210                         int logical, major, minor;
00211                         const char *Version = Barry::Version(logical, major, minor);
00212                         barryverbose(Version);
00213                 }
00214 
00215                 // Probe the USB bus for Blackberry devices and display.
00216                 // If user has specified a PIN, search for it in the
00217                 // available device list here as well
00218                 Barry::Probe probe;
00219                 int activeDevice = probe.FindActive(pin);
00220                 if( activeDevice == -1 ) {
00221                         if( pin )
00222                                 cerr << "PIN " << setbase(16) << pin
00223                                         << " not found" << endl;
00224                         cerr << "No device selected" << endl;
00225                         return 1;
00226                 }
00227 
00228                 const ProbeResult &device = probe.Get(activeDevice);
00229 
00230                 if( !force_serial && device.HasIpModem() ) {
00231                         barryverbose("Using IpModem mode...");
00232 
00233                         // Create our controller object using our threaded router.
00234                         Controller con(probe.Get(activeDevice));
00235 
00236                         // Open serial mode... the callback handles reading from
00237                         // USB and writing to stdout
00238                         Mode::IpModem modem(con, SerialDataCallback, 0);
00239                         modem.Open(password.c_str());
00240 
00241                         ProcessStdin(modem);
00242                         modem.Close();  // graceful close so we can restart without unplugging
00243                 }
00244                 else {
00245                         if( force_serial ) {
00246                                 barryverbose("Using Serial mode per command line...");
00247                         }
00248                         else {
00249                                 barryverbose("No IpModem mode available, using Serial mode...");
00250                         }
00251 
00252                         // Create our socket router and start thread to handle
00253                         // the USB reading, instead of creating our own thread.
00254                         SocketRoutingQueue router;
00255                         router.SpinoffSimpleReadThread();
00256 
00257                         // Create our controller object using our threaded router.
00258                         Controller con(probe.Get(activeDevice), router);
00259 
00260                         // Open desktop mode... this handles the password side
00261                         // of things
00262                         Mode::Desktop desktop(con);
00263                         desktop.Open(password.c_str());
00264 
00265                         // Open serial connection
00266                         Mode::Serial modem(con, SerialDataCallback, 0);
00267                         modem.Open(password.c_str());
00268 
00269                         ProcessStdin(modem);
00270                 }
00271 
00272                 barryverbose("Exiting");
00273 
00274         }
00275         catch( std::exception &e ) {
00276                 cerr << "exception caught in main(): " << e.what() << endl;
00277                 return 1;
00278         }
00279 
00280         return 0;
00281 }
00282