/*------------------------------------------------------------------------ * 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", ¶m->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", ¶m->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, ¶m->block_size, 4) < 0) return warn("Could not read block size"); param->block_size = ntohl(param->block_size); if (full_read(session->client_fd, ¶m->target_rate, 4) < 0) return warn("Could not read target bitrate"); param->target_rate = ntohl(param->target_rate); if (full_read(session->client_fd, ¶m->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, ¶m->slower_num, 2) < 0) return warn("Could not read slowdown numerator"); param->slower_num = ntohs(param->slower_num); if (full_read(session->client_fd, ¶m->slower_den, 2) < 0) return warn("Could not read slowdown denominator"); param->slower_den = ntohs(param->slower_den); if (full_read(session->client_fd, ¶m->faster_num, 2) < 0) return warn("Could not read speedup numerator"); param->faster_num = ntohs(param->faster_num); if (full_read(session->client_fd, ¶m->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; }
/*------------------------------------------------------------------------ * 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) }