add .gitignore
[swift-upb.git] / swift.cpp
index ee9f13c..6ea86ff 100644 (file)
--- a/swift.cpp
+++ b/swift.cpp
 /*
  *  swift.cpp
- *  serp++
+ *  swift the multiparty transport protocol
  *
- *  Created by Victor Grishchenko on 3/6/09.
- *  Copyright 2009 Delft University of Technology. All rights reserved.
+ *  Created by Victor Grishchenko on 2/15/10.
+ *  Copyright 2010 Delft University of Technology. All rights reserved.
  *
  */
-
+#include <stdio.h>
 #include <stdlib.h>
-#include <fcntl.h>
-#ifndef _WIN32
-#include <sys/select.h>
-#include <sys/time.h>
-#include <sys/mman.h>
-#include <arpa/inet.h>
-#include <unistd.h>
-#endif
-#include <sys/stat.h>
-#include <string.h>
-
-//#include <glog/logging.h>
+#include "compat.h"
 #include "swift.h"
-#include "datagram.h"
 
-using namespace std;
 using namespace swift;
 
-swift::tint Channel::last_tick = 0;
-int Channel::MAX_REORDERING = 4;
-bool Channel::SELF_CONN_OK = false;
-swift::tint Channel::TIMEOUT = TINT_SEC*60;
-std::vector<Channel*> Channel::channels(1);
-SOCKET Channel::sockets[8] = {0,0,0,0,0,0,0,0};
-int Channel::socket_count = 0;
-Address Channel::tracker;
-tbheap Channel::send_queue;
-#include "ext/simple_selector.cpp"
-PeerSelector* Channel::peer_selector = new SimpleSelector();
+#define quit(...) {fprintf(stderr,__VA_ARGS__); exit(1); }
+SOCKET InstallHTTPGateway (Address addr);
+
+
+int main (int argc, char** argv) {
+    
+    static struct option long_options[] =
+    {
+        {"hash",    required_argument, 0, 'h'},
+        {"file",    required_argument, 0, 'f'},
+        {"daemon",  no_argument, 0, 'd'},
+        {"listen",  required_argument, 0, 'l'},
+        {"tracker", required_argument, 0, 't'},
+        {"debug",   no_argument, 0, 'D'},
+        {"progress",no_argument, 0, 'p'},
+        {"http",    optional_argument, 0, 'g'},
+        {"wait",    optional_argument, 0, 'w'},
+        {0, 0, 0, 0}
+    };
+
+    Sha1Hash root_hash;
+    char* filename = 0;
+    bool daemonize = false, report_progress = false;
+    Address bindaddr;
+    Address tracker;
+    Address http_gw;
+    tint wait_time = 0;
+    
+    LibraryInit();
+    
+    int c;
+    while ( -1 != (c = getopt_long (argc, argv, ":h:f:dl:t:Dpg::w::", long_options, 0)) ) {
+        
+        switch (c) {
+            case 'h':
+                if (strlen(optarg)!=40)
+                    quit("SHA1 hash must be 40 hex symbols\n");
+                root_hash = Sha1Hash(true,optarg); // FIXME ambiguity
+                if (root_hash==Sha1Hash::ZERO)
+                    quit("SHA1 hash must be 40 hex symbols\n");
+                break;
+            case 'f':
+                filename = strdup(optarg);
+                break;
+            case 'd':
+                daemonize = true;
+                break;
+            case 'l':
+                bindaddr = Address(optarg);
+                if (bindaddr==Address())
+                    quit("address must be hostname:port, ip:port or just port\n");
+                wait_time = TINT_NEVER;
+                break;
+            case 't':
+                tracker = Address(optarg);
+                if (tracker==Address())
+                    quit("address must be hostname:port, ip:port or just port\n");
+                SetTracker(tracker);
+                break;
+            case 'D':
+                Channel::debug_file = optarg ? fopen(optarg,"a") : stdout;
+                break;
+            case 'p':
+                report_progress = true;
+                break;
+            case 'g':
+                http_gw = optarg ? Address(optarg) : Address(Address::LOCALHOST,8080);
+                if (wait_time==-1)
+                    wait_time = TINT_NEVER; // seed
+                break;
+            case 'w':
+                if (optarg) {
+                    char unit = 'u';
+                    if (sscanf(optarg,"%lli%c",&wait_time,&unit)!=2)
+                        quit("time format: 1234[umsMHD], e.g. 1M = one minute\n");
+                    switch (unit) {
+                        case 'D': wait_time *= 24;
+                        case 'H': wait_time *= 60;
+                        case 'M': wait_time *= 60;
+                        case 's': wait_time *= 1000;
+                        case 'm': wait_time *= 1000;
+                        case 'u': break;
+                        default:  quit("time format: 1234[umsMHD], e.g. 1D = one day\n");
+                    }
+                } else
+                    wait_time = TINT_NEVER;
+                break;
+        }
 
-Channel::Channel    (FileTransfer* transfer, int socket, Address peer_addr) :
-    transfer_(transfer), peer_(peer_addr), peer_channel_id_(0), pex_out_(0),
-    socket_(socket==-1?sockets[0]:socket), // FIXME
-    data_out_cap_(bin64_t::ALL), last_data_out_time_(0), last_data_in_time_(0),
-    own_id_mentioned_(false), next_send_time_(0), last_send_time_(0),
-    last_recv_time_(0), rtt_avg_(TINT_SEC), dev_avg_(0), dip_avg_(TINT_SEC),
-    data_in_dbl_(bin64_t::NONE), hint_out_size_(0),
-    cwnd_(1), send_interval_(TINT_SEC), send_control_(PING_PONG_CONTROL),
-    sent_since_recv_(0), ack_rcvd_recent_(0), ack_not_rcvd_recent_(0),
-    last_loss_time_(0), owd_min_bin_(0), owd_min_bin_start_(NOW), 
-    owd_cur_bin_(0), dgrams_sent_(0), dgrams_rcvd_(0), 
-    data_in_(TINT_NEVER,bin64_t::NONE)
-{
-    if (peer_==Address())
-        peer_ = tracker;
-    this->id_ = channels.size();
-    channels.push_back(this);
-    transfer_->hs_in_.push_back(id_);
-    for(int i=0; i<4; i++) {
-        owd_min_bins_[i] = TINT_NEVER;
-        owd_current_[i] = TINT_NEVER;
+    }   // arguments parsed
+    
+
+    if (bindaddr!=Address()) { // seeding
+        if (Listen(bindaddr)<=0)
+            quit("cant listen to %s\n",bindaddr.str())
+    } else if (tracker!=Address() || http_gw!=Address()) { // leeching
+        for (int i=0; i<=10; i++) {
+            bindaddr = Address((uint32_t)INADDR_ANY,0);
+            if (Listen(bindaddr)>0)
+                break;
+            if (i==10)
+                quit("cant listen on %s\n",bindaddr.str());
+        }
+    }
+    
+    if (tracker!=Address())
+        SetTracker(tracker);
+
+    if (http_gw!=Address())
+        InstallHTTPGateway(http_gw);
+
+    if (root_hash!=Sha1Hash::ZERO && !filename)
+        filename = strdup(root_hash.hex().c_str());
+
+    int file = -1;
+    if (filename) {
+        file = Open(filename,root_hash);
+        if (file<=0)
+            quit("cannot open file %s",filename);
+        printf("Root hash: %s\n", RootMerkleHash(file).hex().c_str());
     }
-    Reschedule();
-    dprintf("%s #%u init %s\n",tintstr(),id_,peer_.str());
-}
-
-
-Channel::~Channel () {
-    channels[id_] = NULL;
-}
-
-
-void     swift::SetTracker(const Address& tracker) {
-    Channel::tracker = tracker;
-}
-
-
-int Channel::DecodeID(int scrambled) {
-    return scrambled ^ (int)Datagram::start;
-}
-int Channel::EncodeID(int unscrambled) {
-    return unscrambled ^ (int)Datagram::start;
-}
-
-
-int     swift::Listen (Address addr) {
-    int sock = Datagram::Bind(addr);
-    if (sock!=INVALID_SOCKET)
-        Channel::sockets[Channel::socket_count++] = sock;
-    return sock;
-}
 
+    if (bindaddr==Address() && file==-1 && http_gw==Address()) {
+        fprintf(stderr,"Usage:\n");
+        fprintf(stderr,"  -h, --hash\troot Merkle hash for the transmission\n");
+        fprintf(stderr,"  -f, --file\tname of file to use (root hash by default)\n");
+        fprintf(stderr,"  -l, --listen\t[ip:|host:]port to listen to (default: random)\n");
+        fprintf(stderr,"  -t, --tracker\t[ip:|host:]port of the tracker (default: none)\n");
+        fprintf(stderr,"  -D, --debug\tfile name for debugging logs (default: stdout)\n");
+        fprintf(stderr,"  -p, --progress\treport transfer progress\n");
+        fprintf(stderr,"  -g, --http\t[ip:|host:]port to bind HTTP gateway to (default localhost:8080)\n");
+        fprintf(stderr,"  -w, --wait\tlimit running time, e.g. 1[DHMs] (default: infinite with -l, -g)\n");
+        return 1;
+    }
 
-void    swift::Shutdown (int sock_des) {
-    for(int i=0; i<Channel::socket_count; i++)
-        if (sock_des==-1 || Channel::sockets[i]==sock_des) {
-            Datagram::Close(Channel::sockets[i]);
-            Channel::sockets[i] = Channel::sockets[--Channel::socket_count];
+    tint start_time = NOW;
+    
+    while ( (file>=0 && !IsComplete(file)) ||
+            (start_time+wait_time>NOW)   ) {
+        swift::Loop(TINT_SEC);
+        if (report_progress && file>=0) {
+            fprintf(stderr,
+                    "%s %lli of %lli (seq %lli) %lli dgram %lli bytes up, "\
+                    "%lli dgram %lli bytes down\n",
+                IsComplete(file) ? "DONE" : "done",
+                Complete(file), Size(file), SeqComplete(file),
+                Datagram::dgrams_up, Datagram::bytes_up,
+                Datagram::dgrams_down, Datagram::bytes_down );
         }
-}
-
-
-void    swift::Loop (tint till) {
-    Channel::Loop(till);
-}
-
-
-int      swift::Open (const char* filename, const Sha1Hash& hash) {
-    FileTransfer* ft = new FileTransfer(filename, hash);
-    if (ft && ft->file().file_descriptor()) {
-
-        /*if (FileTransfer::files.size()<fdes)  // FIXME duplication
-            FileTransfer::files.resize(fdes);
-        FileTransfer::files[fdes] = ft;*/
-
-        // initiate tracker connections
-        if (Channel::tracker!=Address())
-            new Channel(ft);
-
-        return ft->file().file_descriptor();
-    } else {
-        if (ft)
-            delete ft;
-        return -1;
     }
+    
+    if (file!=-1)
+        Close(file);
+    
+    if (Channel::debug_file)
+        fclose(Channel::debug_file);
+    
+    swift::Shutdown();
+    
+    return 0;
+    
 }
 
-
-void    swift::Close (int fd) {
-    // FIXME delete all channels
-    if (fd>FileTransfer::files.size() && FileTransfer::files[fd])
-        delete FileTransfer::files[fd];
-}
-
-
-void    swift::AddPeer (Address address, const Sha1Hash& root) {
-    Channel::peer_selector->AddPeer(address,root);
-}
-
-
-uint64_t  swift::Size (int fdes) {
-    if (FileTransfer::files.size()>fdes && FileTransfer::files[fdes])
-        return FileTransfer::files[fdes]->file().size();
-    else
-        return 0;
-}
-
-
-bool  swift::IsComplete (int fdes) {
-    if (FileTransfer::files.size()>fdes && FileTransfer::files[fdes])
-        return FileTransfer::files[fdes]->file().is_complete();
-    else
-        return 0;
-}
-
-
-uint64_t  swift::Complete (int fdes) {
-    if (FileTransfer::files.size()>fdes && FileTransfer::files[fdes])
-        return FileTransfer::files[fdes]->file().complete();
-    else
-        return 0;
-}
-
-
-uint64_t  swift::SeqComplete (int fdes) {
-    if (FileTransfer::files.size()>fdes && FileTransfer::files[fdes])
-        return FileTransfer::files[fdes]->file().seq_complete();
-    else
-        return 0;
-}
-
-
-const Sha1Hash& swift::RootMerkleHash (int file) {
-    FileTransfer* trans = FileTransfer::file(file);
-    if (!trans)
-        return Sha1Hash::ZERO;
-    return trans->file().root_hash();
-}
-
-
-/**    <h2> swift handshake </h2>
- Basic rules:
- <ul>
- <li>    to send a datagram, a channel must be created
- (channels are cheap and easily recycled)
- <li>    a datagram must contain either the receiving
- channel id (scrambled) or the root hash
- </ul>
- <b>Note:</b>
- */