summaryrefslogblamecommitdiffstats
path: root/src/server/net.c
blob: cd93c0d5ef2fbb756abefbec5baa0ee3e449adec (plain) (tree)




















                                                               
                   



                    




                         
                      
                        

                   
                  
 
                                            
 
                                                                         

                            
 


                   
                                
                                     
 
                              
              
 


                         




                             

                  
                                                                                  
     
                                
                       

                                                                     
                                             
                            
         
                             


                                                              
 

                                                                                                  
                                                             
                                                      
                                                                          
 
                                     
             

                                                                                    
             

                                                                        
                  

                          
                                                              
                      
                           
 

                                   
                                          
                                                                          
 
                                                                      


                                                                        



                                                              
                  


                               
                           
 
                                                                             
                                      
                                                                          
 





                                                                                                    
                                                                                     


                      































                                                                                                     
 

                                        
             

                                                                                             

                                                               














                                                            

             
                                  

                                                                                                 
 
                                                                                 
                  


                                               
                  
 
         
 
                                               







                                                                          
     
                        
                      
                       
                                  
                                                            
                                    
                                                 
                 




























                                                                   
                                






                                          
/*
 * This file is part of the Distributed Network Block Device 3
 *
 * Copyright(c) 2011-2012 Johann Latocha <johann@latocha.de>
 *
 * This file may be licensed under the terms of of the
 * GNU General Public License Version 2 (the ``GPL'').
 *
 * Software distributed under the License is distributed
 * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
 * express or implied. See the GPL for the specific language
 * governing rights and limitations.
 *
 * You should have received a copy of the GPL along with this
 * program. If not, go to http://www.gnu.org/licenses/gpl.html
 * or write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/sendfile.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>

#include "server.h"
#include "utils.h"

void *dnbd3_handle_query(void *dnbd3_client)
{
    dnbd3_client_t *client = (dnbd3_client_t *) (uintptr_t) dnbd3_client;
    dnbd3_request_t request;
    dnbd3_reply_t reply;

    int cork = 1;
    int uncork = 0;

    dnbd3_image_t *image = NULL;
    int image_file, image_cache = -1;

    struct in_addr alt_server;
    int i = 0;

    uint64_t map_y;
    char map_x, bit_mask;

    uint64_t todo_size = 0;
    uint64_t todo_offset = 0;
    uint64_t cur_offset = 0;
    uint64_t last_offset = 0;

    int dirty = 0;

    while (recv(client->sock, &request, sizeof(dnbd3_request_t), MSG_WAITALL) > 0)
    {
        reply.cmd = request.cmd;
        reply.size = 0;
        memcpy(reply.handle, request.handle, sizeof(request.handle));

        pthread_spin_lock(&client->spinlock);
        switch (request.cmd)
        {
        case CMD_GET_SERVERS:
            image = dnbd3_get_image(request.vid, request.rid);
            if(!image)
                goto error;

            int num = (image->num_servers < NUMBER_SERVERS) ? image->num_servers : NUMBER_SERVERS;
            reply.vid = image->vid;
            reply.rid = dnbd3_get_image(request.vid, 0)->rid;
            reply.size = num * sizeof(struct in_addr);
            send(client->sock, (char *) &reply, sizeof(dnbd3_reply_t), 0);

            for (i = 0; i < num; i++)
            {
                inet_aton(image->servers[i], &alt_server);
                send(client->sock, (char *) &alt_server, sizeof(struct in_addr), 0);
            }
            client->image = image;
            image->atime = time(NULL); // TODO: check if mutex is needed
            break;

        case CMD_GET_SIZE:
            image = dnbd3_get_image(request.vid, request.rid);
            if(!image)
                goto error;

            reply.vid = image->vid;
            reply.rid = image->rid;
            reply.size = sizeof(uint64_t);
            send(client->sock, (char *) &reply, sizeof(dnbd3_reply_t), 0);

            send(client->sock, &image->filesize, sizeof(uint64_t), 0);
            image_file = open(image->file, O_RDONLY);
            client->image = image;
            image->atime = time(NULL); // TODO: check if mutex is needed

            if (image->cache_file)
                image_cache = open(image->cache_file, O_RDWR);

            break;

        case CMD_GET_BLOCK:
            if (image_file < 0)
                goto error;

            setsockopt(client->sock, SOL_TCP, TCP_CORK, &cork, sizeof(cork));
            reply.size = request.size;
            send(client->sock, (char *) &reply, sizeof(dnbd3_reply_t), 0);

            // caching is off
            if (!image->cache_file)
            {
                if (sendfile(client->sock, image_file, (off_t *) &request.offset, request.size) < 0)
                    printf("ERROR: Sendfile failed (sock)\n");

                setsockopt(client->sock, SOL_TCP, TCP_CORK, &uncork, sizeof(uncork));
                break;
            }

            // caching is on
            dirty = 0;
            todo_size = 0;
            todo_offset = request.offset;
            cur_offset = request.offset;
            last_offset = request.offset + request.size;

            while(cur_offset < last_offset)
            {
                map_y = cur_offset >> 15;
                map_x = (cur_offset >> 12) & 7; // mod 8
                bit_mask = 0b00000001 << (map_x);

                cur_offset += 4096;

                if ((image->cache_map[map_y] & bit_mask) != 0) // cache hit
                {
                    if (todo_size != 0) // fetch missing chunks
                    {
                        lseek(image_cache, todo_offset, SEEK_SET);
                        if (sendfile(image_cache, image_file, (off_t *) &todo_offset, todo_size) < 0)
                            printf("ERROR: Sendfile failed (cache)\n");
                        todo_size = 0;
                        dirty = 1;
                    }
                    todo_offset = cur_offset;
                }
                else
                {
                    todo_size += 4096;
                }
            }

            // whole request was missing
            if (todo_size != 0)
            {
                lseek(image_cache, todo_offset, SEEK_SET);
                if (sendfile(image_cache, image_file, (off_t *) &todo_offset, todo_size) < 0)
                    printf("ERROR: Sendfile failed (cache)\n");

                dirty = 1;
            }

            if (dirty)
            {
                // set 1 in cache map for whole request
                cur_offset = request.offset;
                while(cur_offset < last_offset)
                {
                    map_y = cur_offset >> 15;
                    map_x = (cur_offset >> 12) & 7; // mod 8
                    bit_mask = 0b00000001 << (map_x);
                    image->cache_map[map_y] |= bit_mask;
                    cur_offset += 4096;
                }
            }

            // send data to client
            if (sendfile(client->sock, image_cache, (off_t *) &request.offset, request.size) < 0)
                printf("ERROR: Sendfile failed (net)\n");

            setsockopt(client->sock, SOL_TCP, TCP_CORK, &uncork, sizeof(uncork));
            break;

        default:
            printf("ERROR: Unknown command\n");
            break;

        }

        pthread_spin_unlock(&client->spinlock);
        continue;

        error:
            printf("ERROR: Client requested an unknown image id.\n");
            send(client->sock, (char *) &reply, sizeof(dnbd3_reply_t), 0);
            pthread_spin_unlock(&client->spinlock);
            continue;

    }
    close(client->sock);
    close(image_file);
    close(image_cache);
    pthread_spin_lock(&_spinlock);
    _dnbd3_clients = g_slist_remove(_dnbd3_clients, client);
    pthread_spin_unlock(&_spinlock);
    printf("INFO: Client %s exit\n", client->ip);
    free(client);
    pthread_exit((void *) 0);
}

int dnbd3_setup_socket()
{
    int sock;
    struct sockaddr_in server;

    // Create socket
    sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sock < 0)
    {
        printf("ERROR: Socket failure\n");
        return -1;
    }

    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET; // IPv4
    server.sin_addr.s_addr = htonl(INADDR_ANY); // Take all IPs
    server.sin_port = htons(PORT); // set port number

    // Bind to socket
    if (bind(sock, (struct sockaddr*) &server, sizeof(server)) < 0)
    {
        printf("ERROR: Bind failure\n");
        return -1;
    }

    // Listen on socket
    if (listen(sock, 100) == -1)
    {
        printf("ERROR: Listen failure\n");
        return -1;
    }

    return sock;
}