Esempio n. 1
0
/*------------------------------------------------------------------------
 * int ttp_open_transfer(ttp_session_t *session);
 *
 * Tries to create a new TTP file request object for the given session
 * by reading the name of a requested file from the client.  If we are
 * able to negotiate the transfer successfully, we return 0.  If we
 * can't negotiate the transfer because of I/O or file errors, we
 * return a negative vlaue.
 *
 * The client is sent a result byte of 0 if the request is accepted
 * (because the file can be read) and a non-zero result byte otherwise.
 *------------------------------------------------------------------------*/
int ttp_open_transfer(ttp_session_t *session)
{
    char             filename[MAX_FILENAME_LENGTH];  /* the name of the file to transfer     */
    u_int64_t        file_size;                      /* network-order version of file size   */
    u_int32_t        block_size;                     /* network-order version of block size  */
    u_int32_t        block_count;                    /* network-order version of block count */
    time_t           epoch;
    int              status;
    ttp_transfer_t  *xfer  = &session->transfer;
    ttp_parameter_t *param =  session->parameter;

    #ifdef VSIB_REALTIME
    /* VLBI/VSIB-related variables */
    struct evn_filename *ef;
    double starttime;
    struct timeval d;
    #endif

    char       size[10];
    char       file_no[10];
    char       message[20];
    u_int16_t  i;
    struct     timeval ping_s, ping_e;

    /* clear out the transfer data */
    memset(xfer, 0, sizeof(*xfer));

    /* read in the requested filename */
    status = read_line(session->client_fd, filename, MAX_FILENAME_LENGTH);
    if (status < 0)
        error("Could not read filename from client");
    filename[MAX_FILENAME_LENGTH - 1] = '\0';

    if(!strcmp(filename, TS_DIRLIST_HACK_CMD)) {

       /* The client requested listing of files and their sizes (dir command)
        * Send strings:   NNN \0   name1 \0 len1 \0     nameN \0 lenN \0
        */
        snprintf(file_no, sizeof(file_no), "%u", param->total_files);
        full_write(session->client_fd, file_no, strlen(file_no)+1);
        for(i=0; i<param->total_files; i++) {
            full_write(session->client_fd, param->file_names[i], strlen(param->file_names[i])+1);
            snprintf(message, sizeof(message), "%Lu", (ull_t)(param->file_sizes[i]));
            full_write(session->client_fd, message, strlen(message)+1);
        }
        full_read(session->client_fd, message, 1);
        return warn("File list sent!");

    } else if(!strcmp(filename,"*")) {
      
        if(param->allhook != 0)
        {
           /* execute the provided program on server side to see what files 
            * should be gotten
            */
			const int MaxFileListLength = 32768;
			char fileList[MaxFileListLength];
			const char *fl;
			int nFile = 0;
			int length = 0;
			int l;
            FILE *p;

            fprintf(stderr, "Using allhook program: %s\n", param->allhook);
            p = popen((char *)(param->allhook), "r");
            if(p)
            {

                memset(fileList, 0, MaxFileListLength);

                while(1)
                {
                    if(fgets(message, sizeof(message), p) == 0)
                        break;

                    /* get lenght of string and strip non printable chars */
                    for(l = 0; message[l] >= ' '; ++l) {}
                    message[l] = 0;

                    fprintf(stdout, "  '%s'\n", message);

                    if(l + length >= MaxFileListLength)
                        break;

                    strncpy(fileList + length, message, l);
                    length += l + 1;
                    ++nFile;
                }
            }
            pclose(p);

            memset(size, 0, sizeof(size));
            snprintf(size, sizeof(size), "%u", length);
            full_write(session->client_fd, size, 10);

            memset(file_no, 0, sizeof(file_no));
            snprintf(file_no, sizeof(file_no), "%u", nFile);
            full_write(session->client_fd, file_no, 10);

            printf("\nSent multi-GET filename count and array size to client\n");
            memset(message, 0, sizeof(message));
            full_read(session->client_fd, message, 8);
            printf("Client response: %s\n", message);

            fl = fileList;
            if(nFile > 0)
            {
                for(i=0; i<nFile; i++)
                {
                    l = strlen(fl);
                    full_write(session->client_fd, fl, l+1);
                    fl += l+1;
                }

                memset(message, 0, sizeof(message));
                full_read(session->client_fd, message, 8);
                printf("Sent file list, client response: %s\n", message);

                status = read_line(session->client_fd, filename, MAX_FILENAME_LENGTH);

                if (status < 0)
                    error("Could not read filename from client");
            }

        } else {

           /* A multiple file request - sent the file names first, 
            * and next the client requests a download of each in turn (get * command)
            */
            memset(size, 0, sizeof(size));
            snprintf(size, sizeof(size), "%u", param->file_name_size);
            full_write(session->client_fd, size, 10);

            memset(file_no, 0, sizeof(file_no));
            snprintf(file_no, sizeof(file_no), "%u", param->total_files);
            full_write(session->client_fd, file_no, 10);

            printf("\nSent multi-GET filename count and array size to client\n");
            memset(message, 0, sizeof(message));
            full_read(session->client_fd, message, 8);
            printf("Client response: %s\n", message);

            for(i=0; i<param->total_files; i++)
                full_write(session->client_fd, param->file_names[i], strlen(param->file_names[i])+1);

            memset(message, 0, sizeof(message));
            full_read(session->client_fd, message, 8);
            printf("Sent file list, client response: %s\n", message);

            status = read_line(session->client_fd, filename, MAX_FILENAME_LENGTH);

            if (status < 0)
                error("Could not read filename from client");
        }
    }

    /* store the filename in the transfer object */
    xfer->filename = strdup(filename);
    if (xfer->filename == NULL)
        return warn("Memory allocation error");

    /* make a note of the request */
    if (param->verbose_yn)
        printf("Request for file: '%s'\n", filename);

    #ifndef VSIB_REALTIME

    /* try to open the file for reading */
    xfer->file = fopen(filename, "r");
    if (xfer->file == NULL) {
        sprintf(g_error, "File '%s' does not exist or cannot be read", filename);
        /* signal failure to the client */
        status = full_write(session->client_fd, "\x008", 1);
        if (status < 0)
            warn("Could not signal request failure to client");
        return warn(g_error);
    }

    #else

    /* get starting time (UTC) and detect whether local disk copy is wanted */
    if (strrchr(filename,'/') == NULL) {
        ef = parse_evn_filename(filename);          /* attempt to parse */
        param->fileout = 0;
    } else {
        ef = parse_evn_filename(strrchr(filename, '/')+1);       /* attempt to parse */
        param->fileout = 1;
    }
    if (!ef->valid) {
        fprintf(stderr, "Warning: EVN filename parsing failed, '%s' not following EVN File Naming Convention?\n", filename);
    }

    /* get time multiplexing info from EVN filename (currently these are all unused) */
    if (get_aux_entry("sl",ef->auxinfo, ef->nr_auxinfo) == 0)
      param->totalslots= 1;          /* default to 1 */
    else 
      sscanf(get_aux_entry("sl",ef->auxinfo, ef->nr_auxinfo), "%d", &(param->totalslots));

    if (get_aux_entry("sn",ef->auxinfo, ef->nr_auxinfo) == 0)
      param->slotnumber= 1;          /* default to 1 */
    else 
      sscanf(get_aux_entry("sn",ef->auxinfo, ef->nr_auxinfo), "%d", &param->slotnumber);

    if (get_aux_entry("sr",ef->auxinfo, ef->nr_auxinfo) == 0)
      param->samplerate= 512;          /* default to 512 Msamples/s */
    else 
      sscanf(get_aux_entry("sr",ef->auxinfo, ef->nr_auxinfo), "%d", &param->samplerate);

    /* try to open the vsib for reading */
    xfer->vsib = fopen("/dev/vsib", "r");
    if (xfer->vsib == NULL) {
        sprintf(g_error, "VSIB board does not exist in /dev/vsib or it cannot be read");
        status = full_write(session->client_fd, "\002", 1);
        if (status < 0) {
            warn("Could not signal request failure to client");
        }
        return warn(g_error);
    }

    /* try to open the local disk copy file for writing */
    if (param->fileout) {
        xfer->file = fopen(filename, "wb");
        if (xfer->file == NULL) {
            sprintf(g_error, "Could not open local file '%s' for writing", filename);
            status = full_write(session->client_fd, "\x010", 1);
            if (status < 0) {
                warn("Could not signal request failure to client");
            }
            fclose(xfer->vsib);
            return warn(g_error);
        }
    }

    /* Start half a second before full UTC seconds change. If EVN filename date/time parse failed, start immediately. */
    if (!(NULL == ef->data_start_time_ascii || ef->data_start_time <= 1.0)) {
        u_int64_t timedelta_usec;
        starttime = ef->data_start_time - 0.5;

        assert( gettimeofday(&d, NULL) == 0 );
        timedelta_usec = (unsigned long)((starttime - (double)d.tv_sec)* 1000000.0) - (double)d.tv_usec;
        fprintf(stderr, "Sleeping until specified time (%s) for %Lu usec...\n", ef->data_start_time_ascii, (ull_t)timedelta_usec);
        usleep_that_works(timedelta_usec);
    }

    /* Check if the client is still connected after the long(?) wait */
    //if(recv(session->client_fd, &status, 1, MSG_PEEK)<0) {
    //    // connection has terminated, exit
    //    fclose(xfer->vsib);
    //    return warn("The client disconnected while server was sleeping.");
    //}

    /* start at next 1PPS pulse */
    start_vsib(session);

    #endif // end of VSIB_REALTIME section

    /* begin round trip time estimation */
    gettimeofday(&ping_s,NULL);

    /* try to signal success to the client */
    status = full_write(session->client_fd, "\000", 1);
    if (status < 0)
        return warn("Could not signal request approval to client");

    /* read in the block size, target bitrate, and error rate */
    if (full_read(session->client_fd, &param->block_size,  4) < 0) return warn("Could not read block size");            param->block_size  = ntohl(param->block_size);
    if (full_read(session->client_fd, &param->target_rate, 4) < 0) return warn("Could not read target bitrate");        param->target_rate = ntohl(param->target_rate);
    if (full_read(session->client_fd, &param->error_rate,  4) < 0) return warn("Could not read error rate");            param->error_rate  = ntohl(param->error_rate);

    /* end round trip time estimation */
    gettimeofday(&ping_e,NULL);

    /* read in the slowdown and speedup factors */
    if (full_read(session->client_fd, &param->slower_num,  2) < 0) return warn("Could not read slowdown numerator");    param->slower_num  = ntohs(param->slower_num);
    if (full_read(session->client_fd, &param->slower_den,  2) < 0) return warn("Could not read slowdown denominator");  param->slower_den  = ntohs(param->slower_den);
    if (full_read(session->client_fd, &param->faster_num,  2) < 0) return warn("Could not read speedup numerator");     param->faster_num  = ntohs(param->faster_num);
    if (full_read(session->client_fd, &param->faster_den,  2) < 0) return warn("Could not read speedup denominator");   param->faster_den  = ntohs(param->faster_den);

    #ifndef VSIB_REALTIME
    /* try to find the file statistics */
    fseeko(xfer->file, 0, SEEK_END);
    param->file_size   = ftello(xfer->file);
    fseeko(xfer->file, 0, SEEK_SET);
    #else
    /* get length of recording in bytes from filename */
    if (get_aux_entry("flen", ef->auxinfo, ef->nr_auxinfo) != 0) {
        sscanf(get_aux_entry("flen", ef->auxinfo, ef->nr_auxinfo), "%" SCNu64, (u_int64_t*) &(param->file_size));
    } else if (get_aux_entry("dl", ef->auxinfo, ef->nr_auxinfo) != 0) {
        sscanf(get_aux_entry("dl", ef->auxinfo, ef->nr_auxinfo), "%" SCNu64, (u_int64_t*) &(param->file_size));
    } else {
        param->file_size = 60LL * 512000000LL * 4LL / 8; /* default to amount of bytes equivalent to 4 minutes at 512Mbps */
    }
    fprintf(stderr, "Realtime file length in bytes: %Lu\n", (ull_t)param->file_size);
    #endif

    param->block_count = (param->file_size / param->block_size) + ((param->file_size % param->block_size) != 0);
    param->epoch       = time(NULL);

    /* reply with the length, block size, number of blocks, and run epoch */
    file_size   = tsunami_htonll(param->file_size);  if (full_write(session->client_fd, &file_size,   8) < 0) return warn("Could not submit file size");
    block_size  = htonl (param->block_size);         if (full_write(session->client_fd, &block_size,  4) < 0) return warn("Could not submit block size");
    block_count = htonl (param->block_count);        if (full_write(session->client_fd, &block_count, 4) < 0) return warn("Could not submit block count");
    epoch       = htonl (param->epoch);              if (full_write(session->client_fd, &epoch,       4) < 0) return warn("Could not submit run epoch");

    /*calculate and convert RTT to u_sec*/
    session->parameter->wait_u_sec=(ping_e.tv_sec - ping_s.tv_sec)*1000000+(ping_e.tv_usec-ping_s.tv_usec);
    /*add a 10% safety margin*/
    session->parameter->wait_u_sec = session->parameter->wait_u_sec + ((int)(session->parameter->wait_u_sec* 0.1));  

    /* and store the inter-packet delay */
    param->ipd_time   = (u_int32_t) ((1000000LL * 8 * param->block_size) / param->target_rate);
    xfer->ipd_current = param->ipd_time * 3;

    /* if we're doing a transcript */
    if (param->transcript_yn)
        xscript_open(session);

    /* we succeeded! */
    return 0;
}
Esempio n. 2
0
/*------------------------------------------------------------------------
 * void client_handler(ttp_session_t *session);
 *
 * This routine is run by the client processes that are created in
 * response to incoming connections.
 *------------------------------------------------------------------------*/
void client_handler(ttp_session_t *session)
{
    retransmission_t  retransmission;                /* the retransmission data object                 */
    struct timeval    start, stop;                   /* the start and stop times for the transfer      */
    struct timeval    prevpacketT;                   /* the send time of the previous packet           */
    struct timeval    currpacketT;                   /* the interpacket delay value                    */
    struct timeval    lastfeedback;                  /* the time since last client feedback            */
    struct timeval    lasthblostreport;              /* the time since last 'heartbeat lost' report    */
    u_int32_t         deadconnection_counter;        /* the counter for checking dead conn timeout     */
    int               retransmitlen;                 /* number of bytes read from retransmission queue */
    u_char            datagram[MAX_BLOCK_SIZE + 6];  /* the datagram containing the file block         */
    int64_t           ipd_time;                      /* the time to delay/sleep after packet, signed   */
    int64_t           ipd_usleep_diff;               /* the time correction to ipd_time, signed        */
    int64_t           ipd_time_max;
    int               status;
    ttp_transfer_t   *xfer  = &session->transfer;
    ttp_parameter_t  *param =  session->parameter;
    u_int64_t         delta;
    u_char            block_type;

    /* negotiate the connection parameters */
    status = ttp_negotiate(session);
    if (status < 0)
        error("Protocol revision number mismatch");

    /* have the client try to authenticate to us */
    status = ttp_authenticate(session, session->parameter->secret);
    if (status < 0)
        error("Client authentication failure");

    if (1==param->verbose_yn) {
        fprintf(stderr,"Client authenticated. Negotiated parameters are:\n");
        fprintf(stderr,"Block size: %d\n", param->block_size);
        fprintf(stderr,"Buffer size: %d\n", param->udp_buffer); 
        fprintf(stderr,"Port: %d\n", param->tcp_port);    
    }

    /* while we haven't been told to stop */
    while (1) {

    /* make the client descriptor blocking */
    status = fcntl(session->client_fd, F_SETFL, 0);
    if (status < 0)
        error("Could not make client socket blocking");

    /* negotiate another transfer */
    status = ttp_open_transfer(session);
    if (status < 0) {
        warn("Invalid file request");
        continue;
    }

    /* negotiate a data transfer port */
    status = ttp_open_port(session);
    if (status < 0) {
        warn("UDP socket creation failed");
        continue;
    }

    /* make the client descriptor non-blocking again */
    status = fcntl(session->client_fd, F_SETFL, O_NONBLOCK);
    if (status < 0)
        error("Could not make client socket non-blocking");

    /*---------------------------
     * START TIMING
     *---------------------------*/
    gettimeofday(&start, NULL);
    if (param->transcript_yn)
        xscript_data_start(session, &start);

    lasthblostreport       = start;
    lastfeedback           = start;
    prevpacketT            = start;
    deadconnection_counter = 0;
    ipd_time               = 0;
    ipd_time_max           = 0;
    ipd_usleep_diff        = 0;
    retransmitlen          = 0;

    /* start by blasting out every block */
    xfer->block = 0;
    while (xfer->block <= param->block_count) {

        /* default: flag as retransmitted block */
        block_type = TS_BLOCK_RETRANSMISSION;

        /* precalculate time to wait after sending the next packet */
        gettimeofday(&currpacketT, NULL);
        ipd_usleep_diff = xfer->ipd_current + tv_diff_usec(prevpacketT, currpacketT);
        prevpacketT = currpacketT;
        if (ipd_usleep_diff > 0 || ipd_time > 0) {
            ipd_time += ipd_usleep_diff;
        }
        ipd_time_max = (ipd_time > ipd_time_max) ? ipd_time : ipd_time_max;

        /* see if transmit requests are available */
        status = read(session->client_fd, ((char*)&retransmission)+retransmitlen, sizeof(retransmission)-retransmitlen);
        #ifndef VSIB_REALTIME
        if ((status <= 0) && (errno != EAGAIN))
            error("Retransmission read failed");
        #else
        if ((status <= 0) && (errno != EAGAIN) && (!session->parameter->fileout))
            error("Retransmission read failed and not writing local backup file");
        #endif
        if (status > 0)
            retransmitlen += status;

        /* if we have a retransmission */
        if (retransmitlen == sizeof(retransmission_t)) {

            /* store current time */
            lastfeedback           = currpacketT;
            lasthblostreport       = currpacketT;
            deadconnection_counter = 0;

            /* if it's a stop request, go back to waiting for a filename */
            if (ntohs(retransmission.request_type) == REQUEST_STOP) {
                fprintf(stderr, "Transmission complete.\n");
                break;
            }

            /* otherwise, handle the retransmission */
            status = ttp_accept_retransmit(session, &retransmission, datagram);
            if (status < 0)
                warn("Retransmission error");
            retransmitlen = 0;

        /* if we have no retransmission */
        } else if (retransmitlen < sizeof(retransmission_t)) {

            /* build the block */
            xfer->block = min(xfer->block + 1, param->block_count);
            block_type = (xfer->block == param->block_count) ? TS_BLOCK_TERMINATE : TS_BLOCK_ORIGINAL;
            status = build_datagram(session, xfer->block, block_type, datagram);
            if (status < 0) {
                sprintf(g_error, "Could not read block #%u", xfer->block);
                error(g_error);
            }

            /* transmit the block */
            status = sendto(xfer->udp_fd, datagram, 6 + param->block_size, 0, xfer->udp_address, xfer->udp_length);
            if (status < 0) {
                sprintf(g_error, "Could not transmit block #%u", xfer->block);
                warn(g_error);
                continue;
            }

        /* if we have too long retransmission message */
        } else if (retransmitlen > sizeof(retransmission_t)) {

            fprintf(stderr, "warn: retransmitlen > %d\n", (int)sizeof(retransmission_t));
            retransmitlen = 0;

        }

        /* monitor client heartbeat and disconnect dead client */
        if ((deadconnection_counter++) > 2048) {
            char stats_line[160];

            deadconnection_counter = 0;

            /* limit 'heartbeat lost' reports to 500ms intervals */
            if (get_usec_since(&lasthblostreport) < 500000.0) continue;
            gettimeofday(&lasthblostreport, NULL);

            /* throttle IPD with fake 100% loss report */
            #ifndef VSIB_REALTIME
            retransmission.request_type = htons(REQUEST_ERROR_RATE);
            retransmission.error_rate   = htonl(100000);
            retransmission.block = 0;
            ttp_accept_retransmit(session, &retransmission, datagram);
            #endif

            delta = get_usec_since(&lastfeedback);

            /* show an (additional) statistics line */
            snprintf(stats_line, sizeof(stats_line)-1,
                                "   n/a     n/a     n/a %7u %6.2f %3u -- no heartbeat since %3.2fs\n",
                                xfer->block, 100.0 * xfer->block / param->block_count, session->session_id,
                                1e-6*delta);
            if (param->transcript_yn)
               xscript_data_log(session, stats_line);
            fprintf(stderr, "%s", stats_line);

            /* handle timeout for normal file transfers */
            #ifndef VSIB_REALTIME
            if ((1e-6 * delta) > param->hb_timeout) {
                fprintf(stderr, "Heartbeat timeout of %d seconds reached, terminating transfer.\n", param->hb_timeout);
                break;
            }
            #else
            /* handle timeout condition for : realtime with local backup, simple realtime */
            if ((1e-6 * delta) > param->hb_timeout) {
                if ((session->parameter->fileout) && (block_type == TS_BLOCK_TERMINATE)) {
                    fprintf(stderr, "Reached the Terminate block and timed out, terminating transfer.\n");
                    break;
                } else if(!session->parameter->fileout) {
                    fprintf(stderr, "Heartbeat timeout of %d seconds reached and not doing local backup, terminating transfer now.\n", param->hb_timeout);
                    break;
                } else {
                    lastfeedback = currpacketT;
                }
            }
            #endif
        }

         /* wait before handling the next packet */
         if (block_type == TS_BLOCK_TERMINATE) {
             usleep_that_works(10*ipd_time_max);
         }
         if (ipd_time > 0) {
             usleep_that_works(ipd_time);
         }

    }

    /*---------------------------
     * STOP TIMING
     *---------------------------*/
    gettimeofday(&stop, NULL);
    if (param->transcript_yn)
        xscript_data_stop(session, &stop);
    delta = 1000000LL * (stop.tv_sec - start.tv_sec) + stop.tv_usec - start.tv_usec;

    /* report on the transfer */
    if (param->verbose_yn)
        fprintf(stderr, "Server %d transferred %llu bytes in %0.2f seconds (%0.1f Mbps)\n",
                session->session_id, (ull_t)param->file_size, delta / 1000000.0, 
                8.0 * param->file_size / (delta * 1e-6 * 1024*1024) );

    /* close the transcript */
    if (param->transcript_yn)
        xscript_close(session, delta);

    #ifndef VSIB_REALTIME

    /* close the file */
    fclose(xfer->file);

    #else

    /* VSIB local disk copy: close file only if file output was requested */
    if (param->fileout) {
        fclose(xfer->file);
    }

    /* stop the VSIB */
    stop_vsib(session);
    fclose(xfer->vsib);

    #endif

    /* close the UDP socket */
    close(xfer->udp_fd);
    memset(xfer, 0, sizeof(*xfer));

    } //while(1)

}