Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


nbd server updates
[palacios.git] / misc / network_servers / v3_nbd / v3_nbd.cc
index 4a54726..c0b3923 100644 (file)
  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
  */
 
-#include <string>
+
 #include <iostream>
 #include <fstream>
 #include <stdio.h>
 #include <sstream>
+#include <list>
 
 #ifdef linux 
 #include <errno.h>
 
 #endif
 
-#include "vtl.h"
+
+#include "v3_disk.h"
+#include "raw.h"
+#include "iso.h"
+
+#define NBD_KEY "V3_NBD_1"
+
+
+#define NBD_READ_CMD 0x1
+#define NBD_WRITE_CMD 0x2
+#define NBD_CAPACITY_CMD 0x3
+
+#define NBD_STATUS_OK 0x00
+#define NBD_STATUS_ERR 0xff
 
 
 #define DEFAULT_LOG_FILE "./status.log"
@@ -54,18 +68,6 @@ using namespace std;
 //using namespace __gnu_cxx;
 
 
-typedef enum {INVALID, ISO, RAW} disk_type_t;
-
-struct disk_info {
-    string filename;
-    string tag;
-    disk_type_t type;
-
-    FILE * disk_file;
-};
-
-
-
 struct eqsock {
     bool operator()(const SOCK sock1, const SOCK sock2) const {
        return sock1 == sock2;
@@ -78,11 +80,11 @@ int server_port;
 
 // List of disks being served 
 // eqstr from vtl (config.h)
-map<const string, struct disk_info *, eqstr> disks;
-
+map<const string, v3_disk *, eqstr> disks;
 
 // List of open connections
-map<SOCK, struct disk_info *, eqsock> conns;
+map<const SOCK, v3_disk *, eqsock> conns;
+
 
 // Enable Debugging
 static const int enable_debug = 1;
@@ -93,19 +95,15 @@ int config_nbd(string conf_file_name);
 int serv_loop(int serv_sock);
 void setup_disk(string disk_tag, config_t &config_map);
 
+int handle_new_connection(SOCK new_conn);
+int handle_disk_request(SOCK conn, v3_disk * disk);
 
-int __main (int argc, char ** argv);
+int handle_capacity_request(SOCK conn, v3_disk * disk);
+int handle_read_request(SOCK conn, v3_disk * disk);
+int handle_write_request(SOCK conn, v3_disk * disk);
 
-disk_type_t get_disk_type(const string type_str) {
-
-    if (type_str == "ISO") {
-       return ISO;
-    } else if (type_str == "RAW") {
-       return RAW;
-    } 
+int __main (int argc, char ** argv);
 
-    return INVALID;
-}
 
 
 #ifdef linux
@@ -125,11 +123,19 @@ void main() {
 int __main (int argc, char ** argv) {
   string config_file;
   SOCK serv_sock;
+
+
+
   if (argc > 2) {
     usage();
     exit(0);
   }
 
+  // init global maps
+  disks.clear();
+  conns.clear();
+
+
   if (argc == 2) {
     config_file = string(argv[1]);
   } else {
@@ -174,6 +180,9 @@ int serv_loop(int serv_sock) {
     int max_fd = -1;
     RawEthernetPacket pkt;
 
+
+    list<SOCK> pending_cons;
+
     FD_ZERO(&all_set);
     FD_SET(serv_sock, &all_set);
     max_fd = serv_sock;
@@ -189,6 +198,7 @@ int serv_loop(int serv_sock) {
                continue;
            } else {
                vtl_debug("Select returned error\n");
+               perror("Select returned error: ");
                exit(-1);
            }
        }
@@ -201,6 +211,8 @@ int serv_loop(int serv_sock) {
            // new connection
            conn_socket = accept(serv_sock, (struct sockaddr *)&rem_addr, &addr_len);
 
+           vtl_debug("New Connection...\n");
+
            if (conn_socket < 0) {
                if (errno == EINTR) {
                    continue;
@@ -210,9 +222,74 @@ int serv_loop(int serv_sock) {
                }
            }
 
-           // configure socket
+           pending_cons.push_front(conn_socket);
+
+           FD_SET(conn_socket, &all_set);
+
+           if (conn_socket > max_fd) {
+               max_fd = conn_socket;
+           }
 
+           if (--nready <= 0) continue;
        }
+
+       
+       // handle open connections
+       for (map<SOCK, v3_disk *, eqsock>::iterator con_iter = conns.begin();
+            con_iter != conns.end(); ) {
+           SOCK tmp_sock = con_iter->first;
+           v3_disk * tmp_disk = con_iter->second;
+
+           if (FD_ISSET(con_iter->first, &read_set)) {
+
+               if (handle_disk_request(tmp_sock, tmp_disk) == -1) {
+                   vtl_debug("Error: Could not complete disk request\n");
+
+                   map<SOCK, v3_disk *, eqsock>::iterator tmp_iter = con_iter;
+                   con_iter++;
+
+                   tmp_disk->detach();
+
+
+                   FD_CLR(tmp_sock, &all_set);
+                   close(tmp_sock);
+
+                   conns.erase(tmp_iter);
+               } else {
+                   con_iter++;
+               }
+
+               if (--nready <= 0) break;
+           }
+       }
+
+       if (nready <= 0) continue;
+
+       // check pending connections
+       for (list<SOCK>::iterator pending_iter = pending_cons.begin();
+            pending_iter != pending_cons.end();) {
+           
+           if (FD_ISSET(*pending_iter, &read_set)) {
+               if (handle_new_connection(*pending_iter) == -1) {
+                   // error
+                   vtl_debug("Error: Could not connect to disk\n");
+                   FD_CLR(*pending_iter, &all_set);
+               }
+               list<SOCK>::iterator tmp_iter = pending_iter;
+               pending_iter++;
+
+               pending_cons.erase(tmp_iter);
+               
+               if (--nready <= 0) break;
+           } else {
+               pending_iter++;
+           }
+       }
+       
+       if (nready <= 0) continue;
+       
+
+
     }
 
     return 0;
@@ -274,6 +351,188 @@ int serv_loop(iface_t * iface, SOCK vnet_sock, struct vnet_config * vnet_info) {
 #endif
 
 
+// byte 1: command (read = 1, write = 2, capacity = 3)
+// byte 2 - 4: zero
+int handle_disk_request(SOCK conn, v3_disk * disk) {
+    char buf[4];
+
+    int read_len = Receive(conn, buf, 4, true);
+    
+    if (read_len == 0) {
+       vtl_debug("Detaching from disk (conn=%d)\n", conn);
+       return -1;
+    }
+
+    if (read_len == -1) {
+       vtl_debug("Could not read command\n");
+       return -1;
+    }
+
+    if ((buf[1] != 0) || (buf[2] != 0) || (buf[3] != 0)) {
+       // error
+       vtl_debug("Invalid command padding\n");
+       return -1;
+    }
+
+    switch (buf[0]) {
+       case NBD_CAPACITY_CMD:
+           return handle_capacity_request(conn, disk);
+       case NBD_READ_CMD:
+           return handle_read_request(conn, disk);
+       case NBD_WRITE_CMD:
+           return handle_write_request(conn, disk);
+       default:
+           vtl_debug("Invalid Disk Command %d\n", buf[0]);
+           return -1;
+    }
+
+    return 0;
+}
+
+
+// send: 
+//    8 bytes : capacity
+int handle_capacity_request(SOCK conn, v3_disk * disk) {
+    off_t capacity = disk->get_capacity();
+
+    vtl_debug("Returing capacity %d\n", capacity);
+
+    return Send(conn, (char *)&capacity, 8, true);
+}
+
+// receive:
+//    8 bytes : offset
+//    4 bytes : length
+// send:
+//    1 byte  : status
+//    4 bytes : return length
+//    x bytes : data
+int handle_read_request(SOCK conn, v3_disk * disk) {
+    off_t offset = 0;
+    unsigned int length = 0;
+    unsigned char * buf = NULL;
+    unsigned int ret_len = 0;
+    unsigned char status = NBD_STATUS_OK;
+
+    vtl_debug("Read Request\n");
+
+    if (Receive(conn, (char *)&offset, 8, true) <= 0) {
+       vtl_debug("Error receiving read offset\n");
+       return -1;
+    }
+
+    vtl_debug("Read Offset %d\n", offset);
+
+    if (Receive(conn, (char *)&length, 4, true) <= 0) {
+       vtl_debug("Error receiving read length\n");
+       return -1;
+    }
+
+    vtl_debug("Read length: %d\n", length);
+
+    buf = new unsigned char[length];
+    
+    ret_len = disk->read(buf, offset, length);
+
+    vtl_debug("Read %d bytes from source disk\n", ret_len);
+
+    if (ret_len == 0) {
+       vtl_debug("Read Error\n");
+       status = NBD_STATUS_ERR;
+    }
+
+    vtl_debug("Sending Status byte (%d)\n", status);
+
+    if (Send(conn, (char *)&status, 1, true) <= 0) {
+       vtl_debug("Error Sending Read Status\n");
+       return -1;
+    }
+
+    vtl_debug("Sending Ret Len: %d\n", ret_len);
+
+    if (Send(conn, (char *)&ret_len, 4, true) <= 0) {
+       vtl_debug("Error Sending Read Length\n");
+       return -1;
+    }
+
+
+
+    if (ret_len > 0) {
+       vtl_debug("Sending Data\n");
+       if (Send(conn, (char *)buf, ret_len, true)  <= 0) {
+           vtl_debug("Error sending Read Data\n");
+           return -1;
+       }
+    }
+
+    vtl_debug("Read Complete\n");
+
+    delete buf;
+
+    return 0;
+}
+
+// receive: 
+//    8 bytes : offset
+//    4 bytes : length
+//    x bytes : data
+// send : 
+//    1 bytes : status
+int handle_write_request(SOCK conn, v3_disk * disk) {
+    return -1;
+}
+
+
+/* Negotiation:
+ * <NBD_KEY> <Disk Tag>\n
+ */
+
+int handle_new_connection(SOCK new_conn) {
+    string input;
+    string key_str;
+    string tag_str;
+    v3_disk * disk = NULL;
+
+    GetLine(new_conn, input);
+
+    vtl_debug("New Connection: %s\n", input.c_str());
+
+    {
+       istringstream is(input, istringstream::in);
+       is >> key_str >> tag_str;
+    }
+
+    if (key_str != NBD_KEY) {
+       vtl_debug("Error: Invalid NBD key string (%s)\n", key_str.c_str());
+       return -1;
+    }
+
+    if (disks.count(tag_str) == 0) {
+       vtl_debug("Error: Requesting disk that does not exist (%s)\n", tag_str.c_str());
+       return -1;
+    }
+
+    disk = disks[tag_str];
+
+    if (!disk) {
+       vtl_debug("Disk (%s) Does not exist\n", tag_str.c_str());
+       return -1;
+    }
+
+    if (disk->locked == 1) {
+       vtl_debug("Attempting to attach to a device already in use\n");
+       return -1;
+    }
+
+    conns[new_conn] = disk;
+
+    disk->attach();
+
+    vtl_debug("Connected to disk %s\n", tag_str.c_str());
+
+    return 0;
+}
+
 
 int config_nbd(string conf_file_name) {
     config_t config_map;
@@ -283,6 +542,13 @@ int config_nbd(string conf_file_name) {
        return -1;
     }
 
+    if (config_map.count(LOGFILE_TAG) == 0) {
+       config_map[LOGFILE_TAG] = DEFAULT_LOG_FILE;
+    }
+
+    vtl_debug_init(config_map[LOGFILE_TAG], enable_debug);
+
+
     if (config_map.count(PORT_TAG) > 0) {
        server_port = atoi(config_map[PORT_TAG].c_str());
     } else {
@@ -309,11 +575,6 @@ int config_nbd(string conf_file_name) {
        return -1;
     }
     
-    if (config_map.count(LOGFILE_TAG) == 0) {
-       config_map[LOGFILE_TAG] = DEFAULT_LOG_FILE;
-    }
-    
-    vtl_debug_init(config_map[LOGFILE_TAG], enable_debug);
 
     return 0;
 }
@@ -321,7 +582,10 @@ int config_nbd(string conf_file_name) {
 void setup_disk(string disk_tag, config_t &config_map) {
     string file_tag = disk_tag +  ".file";
     string type_tag = disk_tag + ".type";
-    struct disk_info * disk = (struct disk_info *)malloc(sizeof(struct disk_info));
+    
+    v3_disk * disk;
+    string type;
+
 
     cout << "Setting up " << disk_tag.c_str() << endl;
 
@@ -330,17 +594,16 @@ void setup_disk(string disk_tag, config_t &config_map) {
        cerr << "Missing Disk configuration directive for " << disk_tag << endl;
     }
 
-    disk->tag = disk_tag;
-    disk->filename = config_map[file_tag];
-    disk->type = get_disk_type(config_map[type_tag]);
+    type = config_map[type_tag];  
 
-    if (disk->type == RAW) {
-       disk->disk_file = fopen(disk->filename.c_str(), "w+");
-    } else if (disk->type == ISO) {
-       disk->disk_file = fopen(disk->filename.c_str(), "r");
+    if (type == "RAW") {
+       disk = new raw_disk(config_map[file_tag]);
+    } else if (type == "ISO") {
+       vtl_debug("Setting up ISO\n");
+       disk = new iso_image(config_map[file_tag]);
     }
 
-    disks[disk->tag] = disk;
+    disks[disk_tag] = disk;
 
     return;
 }