IBR-DTNSuite  0.8
tools/src/dtnoutbox.cpp
Go to the documentation of this file.
00001 /*
00002  * dtnoutbox.cpp
00003  *
00004  *  Created on: 20.11.2009
00005  *      Author: morgenro
00006  */
00007 
00008 #include "config.h"
00009 #include "ibrdtn/api/Client.h"
00010 #include "ibrdtn/api/FileBundle.h"
00011 #include "ibrcommon/net/tcpclient.h"
00012 #include "ibrcommon/thread/Mutex.h"
00013 #include "ibrcommon/thread/MutexLock.h"
00014 #include "ibrdtn/data/PayloadBlock.h"
00015 #include "ibrdtn/api/BLOBBundle.h"
00016 #include "ibrcommon/data/BLOB.h"
00017 #include "ibrcommon/data/File.h"
00018 #include "ibrcommon/appstreambuf.h"
00019 
00020 #include <stdlib.h>
00021 #include <iostream>
00022 #include <map>
00023 #include <vector>
00024 #include <csignal>
00025 #include <sys/types.h>
00026 #include <unistd.h>
00027 
00028 using namespace ibrcommon;
00029 
00030 void print_help()
00031 {
00032         cout << "-- dtnoutbox (IBR-DTN) --" << endl;
00033         cout << "Syntax: dtnoutbox [options] <name> <outbox> <destination>"  << endl;
00034         cout << " <name>           the application name" << endl;
00035         cout << " <outbox>         directory with outgoing files" << endl;
00036         cout << " <destination>    the destination EID for all outgoing files" << endl;
00037         cout << "* optional parameters *" << endl;
00038         cout << " -h|--help        display this text" << endl;
00039         cout << " -w|--workdir     temporary work directory" << endl;
00040 }
00041 
00042 map<string,string> readconfiguration(int argc, char** argv)
00043 {
00044     // print help if not enough parameters are set
00045     if (argc < 4) { print_help(); exit(0); }
00046 
00047     map<string,string> ret;
00048 
00049     ret["name"] = argv[argc - 3];
00050     ret["outbox"] = argv[argc - 2];
00051     ret["destination"] = argv[argc - 1];
00052 
00053     for (int i = 0; i < (argc - 3); i++)
00054     {
00055         string arg = argv[i];
00056 
00057         // print help if requested
00058         if (arg == "-h" || arg == "--help")
00059         {
00060             print_help();
00061             exit(0);
00062         }
00063 
00064         if ((arg == "-w" || arg == "--workdir") && (argc > i))
00065         {
00066             ret["workdir"] = argv[i + 1];
00067         }
00068     }
00069 
00070     return ret;
00071 }
00072 
00073 // set this variable to false to stop the app
00074 bool _running = true;
00075 
00076 // global connection
00077 ibrcommon::tcpclient *_conn = NULL;
00078 
00079 void term(int signal)
00080 {
00081     if (signal >= 1)
00082     {
00083         _running = false;
00084         if (_conn != NULL) _conn->close();
00085     }
00086 }
00087 
00088 /*
00089  * main application method
00090  */
00091 int main(int argc, char** argv)
00092 {
00093     // catch process signals
00094     signal(SIGINT, term);
00095     signal(SIGTERM, term);
00096 
00097     // read the configuration
00098     map<string,string> conf = readconfiguration(argc, argv);
00099 
00100     // init working directory
00101     if (conf.find("workdir") != conf.end())
00102     {
00103         ibrcommon::File blob_path(conf["workdir"]);
00104 
00105         if (blob_path.exists())
00106         {
00107                 ibrcommon::BLOB::changeProvider(new ibrcommon::FileBLOBProvider(blob_path), true);
00108         }
00109     }
00110 
00111     // backoff for reconnect
00112     size_t backoff = 2;
00113 
00114     // check outbox for files
00115         File outbox(conf["outbox"]);
00116 
00117     // loop, if no stop if requested
00118     while (_running)
00119     {
00120         try {
00121                 // Create a stream to the server using TCP.
00122                 ibrcommon::tcpclient conn("127.0.0.1", 4550);
00123 
00124                 // enable nodelay option
00125                 conn.enableNoDelay();
00126 
00127                 // set the connection globally
00128                 _conn = &conn;
00129 
00130             // Initiate a client for synchronous receiving
00131             dtn::api::Client client(conf["name"], conn, dtn::api::Client::MODE_SENDONLY);
00132 
00133             // Connect to the server. Actually, this function initiate the
00134             // stream protocol by starting the thread and sending the contact header.
00135             client.connect();
00136 
00137             // reset backoff if connected
00138             backoff = 2;
00139 
00140             // check the connection
00141             while (_running)
00142             {
00143                 list<File> files;
00144                 outbox.getFiles(files);
00145 
00146                 // <= 2 because of "." and ".."
00147                 if (files.size() <= 2)
00148                 {
00149                     // wait some seconds
00150                     sleep(10);
00151 
00152                     continue;
00153                 }
00154 
00155                 stringstream file_list;
00156 
00157                 int prefix_length = outbox.getPath().length() + 1;
00158 
00159                 for (list<File>::iterator iter = files.begin(); iter != files.end(); iter++)
00160                 {
00161                         File &f = (*iter);
00162 
00163                         // skip system files ("." and "..")
00164                         if (f.isSystem()) continue;
00165 
00166                                         // remove the prefix of the outbox path
00167                         string rpath = f.getPath();
00168                         rpath = rpath.substr(prefix_length, rpath.length() - prefix_length);
00169 
00170                         file_list << rpath << " ";
00171                 }
00172 
00173                 // output of all files to send
00174                 cout << "files: " << file_list.str() << endl;
00175 
00176                 // "--remove-files" deletes files after adding
00177                 stringstream cmd; cmd << "tar --remove-files -cO -C " << outbox.getPath() << " " << file_list.str();
00178 
00179                 // make a tar archive
00180                 appstreambuf app(cmd.str(), appstreambuf::MODE_READ);
00181                 istream stream(&app);
00182 
00183                         // create a blob
00184                 ibrcommon::BLOB::Reference blob = ibrcommon::BLOB::create();
00185 
00186                         // stream the content of "tar" to the payload block
00187                         (*blob.iostream()) << stream.rdbuf();
00188 
00189                 // create a new bundle
00190                         dtn::data::EID destination = EID(conf["destination"]);
00191                         dtn::api::BLOBBundle b(destination, blob);
00192 
00193                 // send the bundle
00194                         client << b; client.flush();
00195 
00196                 if (_running)
00197                 {
00198                                         // wait some seconds
00199                                         sleep(10);
00200                 }
00201             }
00202 
00203             // close the client connection
00204             client.close();
00205 
00206             // close the connection
00207             conn.close();
00208 
00209             // set the global connection to NULL
00210             _conn = NULL;
00211         } catch (const ibrcommon::tcpclient::SocketException&) {
00212                 // set the global connection to NULL
00213                 _conn = NULL;
00214 
00215                 if (_running)
00216                 {
00217                                 cout << "Connection to bundle daemon failed. Retry in " << backoff << " seconds." << endl;
00218                                 sleep(backoff);
00219 
00220                                 // if backoff < 10 minutes
00221                                 if (backoff < 600)
00222                                 {
00223                                         // set a new backoff
00224                                         backoff = backoff * 2;
00225                                 }
00226                 }
00227         } catch (const ibrcommon::IOException&) {
00228                 // set the global connection to NULL
00229                 _conn = NULL;
00230 
00231                 if (_running)
00232                 {
00233                                 cout << "Connection to bundle daemon failed. Retry in " << backoff << " seconds." << endl;
00234                                 sleep(backoff);
00235 
00236                                 // if backoff < 10 minutes
00237                                 if (backoff < 600)
00238                                 {
00239                                         // set a new backoff
00240                                         backoff = backoff * 2;
00241                                 }
00242                 }
00243         } catch (const std::exception&) {
00244                 // set the global connection to NULL
00245                 _conn = NULL;
00246         }
00247     }
00248 
00249     return (EXIT_SUCCESS);
00250 }