| /* |
| * Copyright (c) 2011 Intel Corporation. All rights reserved. |
| * |
| * This software is available to you under the OpenIB.org BSD license |
| * below: |
| * |
| * Redistribution and use in source and binary forms, with or |
| * without modification, are permitted provided that the following |
| * conditions are met: |
| * |
| * - Redistributions of source code must retain the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer. |
| * |
| * - Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer in the documentation and/or other materials |
| * provided with the distribution. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AWV |
| * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
| * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
| * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| * SOFTWARE. |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <strings.h> |
| #include <errno.h> |
| #include <getopt.h> |
| #include <arpa/inet.h> |
| #include <sys/mman.h> |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <sys/time.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <netdb.h> |
| #include <unistd.h> |
| |
| #include <rdma/rsocket.h> |
| |
| #include "common.h" |
| |
| union rsocket_address { |
| struct sockaddr sa; |
| struct sockaddr_in sin; |
| struct sockaddr_in6 sin6; |
| struct sockaddr_storage storage; |
| }; |
| |
| static const char *port = "7427"; |
| static char *dst_addr; |
| static char *dst_file; |
| static char *src_file; |
| static struct timeval start, end; |
| //static void buf[1024 * 1024]; |
| static uint64_t bytes; |
| static int fd; |
| static void *file_addr; |
| |
| enum { |
| CMD_NOOP, |
| CMD_OPEN, |
| CMD_CLOSE, |
| CMD_WRITE, |
| CMD_RESP = 0x80, |
| }; |
| |
| /* TODO: handle byte swapping */ |
| struct msg_hdr { |
| uint8_t version; |
| uint8_t command; |
| uint16_t len; |
| uint32_t data; |
| uint64_t id; |
| }; |
| |
| struct msg_open { |
| struct msg_hdr hdr; |
| char path[0]; |
| }; |
| |
| struct msg_write { |
| struct msg_hdr hdr; |
| uint64_t size; |
| }; |
| |
| static void show_perf(void) |
| { |
| float usec; |
| |
| usec = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec); |
| |
| printf("%lld bytes in %.2f seconds = %.2f Gb/sec\n", |
| (long long) bytes, usec / 1000000., (bytes * 8) / (1000. * usec)); |
| } |
| |
| static char *_ntop(union rsocket_address *rsa) |
| { |
| static char addr[32]; |
| |
| switch (rsa->sa.sa_family) { |
| case AF_INET: |
| inet_ntop(AF_INET, &rsa->sin.sin_addr, addr, sizeof addr); |
| break; |
| case AF_INET6: |
| inet_ntop(AF_INET6, &rsa->sin6.sin6_addr, addr, sizeof addr); |
| break; |
| default: |
| addr[0] = '\0'; |
| break; |
| } |
| |
| return addr; |
| } |
| |
| static size_t _recv(int rs, char *msg, size_t len) |
| { |
| size_t ret, offset; |
| |
| for (offset = 0; offset < len; offset += ret) { |
| ret = rrecv(rs, msg + offset, len - offset, 0); |
| if (ret <= 0) |
| return ret; |
| } |
| |
| return len; |
| } |
| |
| static int msg_recv_hdr(int rs, struct msg_hdr *hdr) |
| { |
| int ret; |
| |
| ret = _recv(rs, (char *) hdr, sizeof *hdr); |
| if (ret != sizeof *hdr) |
| return -1; |
| |
| if (hdr->version || hdr->len < sizeof *hdr) { |
| printf("invalid version %d or length %d\n", |
| hdr->version, hdr->len); |
| return -1; |
| } |
| |
| return sizeof *hdr; |
| } |
| |
| static int msg_get_resp(int rs, struct msg_hdr *msg, uint8_t cmd) |
| { |
| int ret; |
| |
| ret = msg_recv_hdr(rs, msg); |
| if (ret != sizeof *msg) |
| return ret; |
| |
| if ((msg->len != sizeof *msg) || (msg->command != (cmd | CMD_RESP))) { |
| printf("invalid length %d or bad command response %x:%x\n", |
| msg->len, msg->command, cmd | CMD_RESP); |
| return -1; |
| } |
| |
| return msg->data; |
| } |
| |
| static void msg_send_resp(int rs, struct msg_hdr *msg, uint32_t status) |
| { |
| struct msg_hdr resp; |
| |
| resp.version = 0; |
| resp.command = msg->command | CMD_RESP; |
| resp.len = sizeof resp; |
| resp.data = status; |
| resp.id = msg->id; |
| rsend(rs, (char *) &resp, sizeof resp, 0); |
| } |
| |
| static int server_listen(void) |
| { |
| struct addrinfo hints, *res; |
| int ret, rs; |
| |
| memset(&hints, 0, sizeof hints); |
| hints.ai_flags = RAI_PASSIVE; |
| ret = getaddrinfo(NULL, port, &hints, &res); |
| if (ret) { |
| printf("getaddrinfo failed: %s\n", gai_strerror(ret)); |
| return ret; |
| } |
| |
| rs = rs_socket(res->ai_family, res->ai_socktype, res->ai_protocol); |
| if (rs < 0) { |
| ret = rs; |
| goto free; |
| } |
| |
| ret = 1; |
| ret = rsetsockopt(rs, SOL_SOCKET, SO_REUSEADDR, &ret, sizeof ret); |
| if (ret) { |
| perror("rsetsockopt failed"); |
| goto close; |
| } |
| |
| ret = rbind(rs, res->ai_addr, res->ai_addrlen); |
| if (ret) { |
| perror("rbind failed"); |
| goto close; |
| } |
| |
| ret = rlisten(rs, 1); |
| if (ret) { |
| perror("rlisten failed"); |
| goto close; |
| } |
| |
| ret = rs; |
| goto free; |
| |
| close: |
| rclose(rs); |
| free: |
| freeaddrinfo(res); |
| return ret; |
| } |
| |
| static int server_open(int rs, struct msg_hdr *msg) |
| { |
| char *path = NULL; |
| int ret, len; |
| |
| printf("opening: "); |
| fflush(NULL); |
| if (file_addr || fd > 0) { |
| printf("cannot open another file\n"); |
| ret = EBUSY; |
| goto out; |
| } |
| |
| len = msg->len - sizeof *msg; |
| path = malloc(len); |
| if (!path) { |
| printf("cannot allocate path name\n"); |
| ret = ENOMEM; |
| goto out; |
| } |
| |
| ret = _recv(rs, path, len); |
| if (ret != len) { |
| printf("error receiving path\n"); |
| goto out; |
| } |
| |
| printf("%s, ", path); |
| fflush(NULL); |
| fd = open(path, O_RDWR | O_CREAT | O_TRUNC, msg->data); |
| if (fd < 0) { |
| printf("unable to open destination file\n"); |
| ret = errno; |
| goto out; |
| } |
| |
| ret = 0; |
| out: |
| if (path) |
| free(path); |
| |
| msg_send_resp(rs, msg, ret); |
| return ret; |
| } |
| |
| static void server_close(int rs, struct msg_hdr *msg) |
| { |
| printf("closing..."); |
| fflush(NULL); |
| msg_send_resp(rs, msg, 0); |
| |
| if (file_addr) { |
| munmap(file_addr, bytes); |
| file_addr = NULL; |
| } |
| |
| if (fd > 0) { |
| close(fd); |
| fd = 0; |
| } |
| printf("done\n"); |
| } |
| |
| static int server_write(int rs, struct msg_hdr *msg) |
| { |
| size_t len; |
| int ret; |
| |
| printf("transferring"); |
| fflush(NULL); |
| if (fd <= 0) { |
| printf("...file not opened\n"); |
| ret = EINVAL; |
| goto out; |
| } |
| |
| if (msg->len != sizeof(struct msg_write)) { |
| printf("...invalid message length %d\n", msg->len); |
| ret = EINVAL; |
| goto out; |
| } |
| |
| ret = _recv(rs, (char *) &bytes, sizeof bytes); |
| if (ret != sizeof bytes) |
| goto out; |
| |
| ret = ftruncate(fd, bytes); |
| if (ret) |
| goto out; |
| |
| file_addr = mmap(NULL, bytes, PROT_WRITE, MAP_SHARED, fd, 0); |
| if (file_addr == (void *) -1) { |
| printf("...error mapping file\n"); |
| ret = errno; |
| goto out; |
| } |
| |
| printf("...%lld bytes...", (long long) bytes); |
| fflush(NULL); |
| len = _recv(rs, file_addr, bytes); |
| if (len != bytes) { |
| printf("...error receiving data\n"); |
| ret = (int) len; |
| } |
| out: |
| msg_send_resp(rs, msg, ret); |
| return ret; |
| } |
| |
| static void server_process(int rs) |
| { |
| struct msg_hdr msg; |
| int ret; |
| |
| do { |
| ret = msg_recv_hdr(rs, &msg); |
| if (ret != sizeof msg) |
| break; |
| |
| switch (msg.command) { |
| case CMD_OPEN: |
| ret = server_open(rs, &msg); |
| break; |
| case CMD_CLOSE: |
| server_close(rs, &msg); |
| ret = 0; |
| break; |
| case CMD_WRITE: |
| ret = server_write(rs, &msg); |
| break; |
| default: |
| msg_send_resp(rs, &msg, EINVAL); |
| ret = -1; |
| break; |
| } |
| |
| } while (!ret); |
| } |
| |
| static int server_run(void) |
| { |
| int lrs, rs; |
| union rsocket_address rsa; |
| socklen_t len; |
| |
| lrs = server_listen(); |
| if (lrs < 0) |
| return lrs; |
| |
| while (1) { |
| len = sizeof rsa; |
| printf("waiting for connection..."); |
| fflush(NULL); |
| rs = raccept(lrs, &rsa.sa, &len); |
| |
| printf("client: %s\n", _ntop(&rsa)); |
| server_process(rs); |
| |
| rshutdown(rs, SHUT_RDWR); |
| rclose(rs); |
| } |
| return 0; |
| } |
| |
| static int client_connect(void) |
| { |
| struct addrinfo *res; |
| int ret, rs; |
| |
| ret = getaddrinfo(dst_addr, port, NULL, &res); |
| if (ret) { |
| printf("getaddrinfo failed: %s\n", gai_strerror(ret)); |
| return ret; |
| } |
| |
| rs = rs_socket(res->ai_family, res->ai_socktype, res->ai_protocol); |
| if (rs < 0) { |
| goto free; |
| } |
| |
| ret = rconnect(rs, res->ai_addr, res->ai_addrlen); |
| if (ret) { |
| perror("rconnect failed\n"); |
| rclose(rs); |
| rs = ret; |
| } |
| |
| free: |
| freeaddrinfo(res); |
| return rs; |
| } |
| |
| static int client_open(int rs) |
| { |
| struct msg_open *msg; |
| struct stat stats; |
| uint32_t len; |
| int ret; |
| |
| printf("opening..."); |
| fflush(NULL); |
| fd = open(src_file, O_RDONLY); |
| if (fd < 0) |
| return fd; |
| |
| ret = fstat(fd, &stats); |
| if (ret < 0) |
| goto err1; |
| |
| bytes = (uint64_t) stats.st_size; |
| file_addr = mmap(NULL, bytes, PROT_READ, MAP_SHARED, fd, 0); |
| if (file_addr == (void *) -1) { |
| ret = errno; |
| goto err1; |
| } |
| |
| len = (((uint32_t) strlen(dst_file)) + 8) & 0xFFFFFFF8; |
| msg = calloc(1, sizeof(*msg) + len); |
| if (!msg) { |
| ret = -1; |
| goto err2; |
| } |
| |
| msg->hdr.command = CMD_OPEN; |
| msg->hdr.len = sizeof(*msg) + len; |
| msg->hdr.data = (uint32_t) stats.st_mode; |
| strcpy(msg->path, dst_file); |
| ret = rsend(rs, msg, msg->hdr.len, 0); |
| if (ret != msg->hdr.len) |
| goto err3; |
| |
| ret = msg_get_resp(rs, &msg->hdr, CMD_OPEN); |
| if (ret) |
| goto err3; |
| |
| return 0; |
| |
| err3: |
| free(msg); |
| err2: |
| munmap(file_addr, bytes); |
| err1: |
| close(fd); |
| return ret; |
| } |
| |
| static int client_start_write(int rs) |
| { |
| struct msg_write msg; |
| int ret; |
| |
| printf("transferring"); |
| fflush(NULL); |
| memset(&msg, 0, sizeof msg); |
| msg.hdr.command = CMD_WRITE; |
| msg.hdr.len = sizeof(msg); |
| msg.size = bytes; |
| |
| ret = rsend(rs, &msg, sizeof msg, 0); |
| if (ret != msg.hdr.len) |
| return ret; |
| |
| return 0; |
| } |
| |
| static int client_close(int rs) |
| { |
| struct msg_hdr msg; |
| int ret; |
| |
| printf("closing..."); |
| fflush(NULL); |
| memset(&msg, 0, sizeof msg); |
| msg.command = CMD_CLOSE; |
| msg.len = sizeof msg; |
| ret = rsend(rs, (char *) &msg, msg.len, 0); |
| if (ret != msg.len) |
| goto out; |
| |
| ret = msg_get_resp(rs, &msg, CMD_CLOSE); |
| if (ret) |
| goto out; |
| |
| printf("done\n"); |
| out: |
| munmap(file_addr, bytes); |
| close(fd); |
| return ret; |
| } |
| |
| static int client_run(void) |
| { |
| struct msg_hdr ack; |
| int ret, rs; |
| size_t len; |
| |
| rs = client_connect(); |
| if (rs < 0) |
| return rs; |
| |
| ret = client_open(rs); |
| if (ret) |
| goto shutdown; |
| |
| ret = client_start_write(rs); |
| if (ret) |
| goto close; |
| |
| printf("..."); |
| fflush(NULL); |
| gettimeofday(&start, NULL); |
| len = rsend(rs, file_addr, bytes, 0); |
| if (len == bytes) |
| ret = msg_get_resp(rs, &ack, CMD_WRITE); |
| else |
| ret = (int) len; |
| |
| gettimeofday(&end, NULL); |
| |
| close: |
| client_close(rs); |
| shutdown: |
| rshutdown(rs, SHUT_RDWR); |
| rclose(rs); |
| if (!ret) |
| show_perf(); |
| return ret; |
| } |
| |
| static void show_usage(char *program) |
| { |
| printf("usage 1: %s [options]\n", program); |
| printf("\t starts the server application\n"); |
| printf("\t[-p port_number]\n"); |
| printf("usage 2: %s source server[:destination] [options]\n", program); |
| printf("\t source - file name and path\n"); |
| printf("\t server - name or address\n"); |
| printf("\t destination - file name and path\n"); |
| printf("\t[-p port_number]\n"); |
| exit(1); |
| } |
| |
| static void server_opts(int argc, char **argv) |
| { |
| int op; |
| |
| while ((op = getopt(argc, argv, "p:")) != -1) { |
| switch (op) { |
| case 'p': |
| port = optarg; |
| break; |
| default: |
| show_usage(argv[0]); |
| } |
| } |
| } |
| |
| static void client_opts(int argc, char **argv) |
| { |
| int op; |
| |
| if (argc < 3) |
| show_usage(argv[0]); |
| |
| src_file = argv[1]; |
| dst_addr = argv[2]; |
| dst_file = strchr(dst_addr, ':'); |
| if (dst_file) { |
| *dst_file = '\0'; |
| dst_file++; |
| } |
| if (!dst_file) |
| dst_file = src_file; |
| |
| while ((op = getopt(argc, argv, "p:")) != -1) { |
| switch (op) { |
| case 'p': |
| port = optarg; |
| break; |
| default: |
| show_usage(argv[0]); |
| } |
| } |
| |
| } |
| |
| int main(int argc, char **argv) |
| { |
| int ret; |
| |
| if (argc == 1 || argv[1][0] == '-') { |
| server_opts(argc, argv); |
| ret = server_run(); |
| } else { |
| client_opts(argc, argv); |
| ret = client_run(); |
| } |
| |
| return ret; |
| } |