KineticStatus KineticClient_Connect(const KineticSession* config, KineticSessionHandle* handle) { if (handle == NULL) { LOG0("Session handle is NULL!"); return KINETIC_STATUS_SESSION_EMPTY; } *handle = KINETIC_HANDLE_INVALID; if (config == NULL) { LOG0("KineticSession is NULL!"); return KINETIC_STATUS_SESSION_EMPTY; } if (strlen(config->host) == 0) { LOG0("Host is empty!"); return KINETIC_STATUS_HOST_EMPTY; } if (config->hmacKey.len < 1 || config->hmacKey.data == NULL) { LOG0("HMAC key is NULL or empty!"); return KINETIC_STATUS_HMAC_EMPTY; } // Obtain a new connection/handle *handle = KineticConnection_NewConnection(config); if (*handle == KINETIC_HANDLE_INVALID) { LOG0("Failed connecting to device!"); return KINETIC_STATUS_SESSION_INVALID; } KineticConnection* connection = KineticConnection_FromHandle(*handle); if (connection == NULL) { LOG0("Failed getting valid connection from handle!"); return KINETIC_STATUS_CONNECTION_ERROR; } // Create the connection KineticStatus status = KineticConnection_Connect(connection); if (status != KINETIC_STATUS_SUCCESS) { LOGF0("Failed creating connection to %s:%d", config->host, config->port); KineticConnection_FreeConnection(handle); *handle = KINETIC_HANDLE_INVALID; return status; } // Wait for initial unsolicited status to be received in order to obtain connectionID while(connection->connectionID == 0) {sleep(1);} return status; }
KineticStatus KineticClient_CreateSession(KineticSessionConfig* const config, KineticClient * const client, KineticSession** session) { if (config == NULL) { LOG0("KineticSessionConfig is NULL!"); return KINETIC_STATUS_SESSION_INVALID; } if (session == NULL) { LOG0("Pointer to KineticSession pointer is NULL!"); return KINETIC_STATUS_SESSION_EMPTY; } if (strlen(config->host) == 0) { LOG0("Host is empty!"); return KINETIC_STATUS_HOST_EMPTY; } if (config->hmacKey.len < 1 || config->hmacKey.data == NULL) { LOG0("HMAC key is NULL or empty!"); return KINETIC_STATUS_HMAC_REQUIRED; } // Create a new session KineticSession* s = KineticAllocator_NewSession(client->bus, config); if (s == NULL) { LOG0("Failed to create session instance!"); return KINETIC_STATUS_MEMORY_ERROR; } KineticStatus status = KineticSession_Create(s, client); if (status != KINETIC_STATUS_SUCCESS) { LOG0("Failed to create session instance!"); KineticAllocator_FreeSession(s); return status; } // Establish the connection status = KineticSession_Connect(s); if (status != KINETIC_STATUS_SUCCESS) { LOGF0("Failed creating connection to %s:%d", config->host, config->port); KineticAllocator_FreeSession(s); return status; } *session = s; return status; }
KineticStatus bus_to_kinetic_status(bus_send_status_t const status) { KineticStatus res = KINETIC_STATUS_INVALID; switch(status) { // TODO scrutinize all these mappings case BUS_SEND_SUCCESS: res = KINETIC_STATUS_SUCCESS; break; case BUS_SEND_TX_TIMEOUT_NOTIFYING_LISTENER: case BUS_SEND_TX_TIMEOUT: res = KINETIC_STATUS_SOCKET_TIMEOUT; break; case BUS_SEND_TX_FAILURE: res = KINETIC_STATUS_SOCKET_ERROR; break; case BUS_SEND_RX_TIMEOUT: res = KINETIC_STATUS_OPERATION_TIMEDOUT; break; case BUS_SEND_RX_FAILURE: res = KINETIC_STATUS_SOCKET_ERROR; break; case BUS_SEND_BAD_RESPONSE: res = KINETIC_STATUS_SOCKET_ERROR; break; case BUS_SEND_UNREGISTERED_SOCKET: res = KINETIC_STATUS_SOCKET_ERROR; break; case BUS_SEND_RX_TIMEOUT_EXPECT: res = KINETIC_STATUS_OPERATION_TIMEDOUT; break; case BUS_SEND_UNDEFINED: default: { LOGF0("bus_to_kinetic_status: UNMATCHED %d", status); KINETIC_ASSERT(false); return KINETIC_STATUS_INVALID; } } LOGF3("bus_to_kinetic_status: mapping status %d => %d", status, res); return res; }
void KineticController_HandleResult(bus_msg_result_t *res, void *udata) { KineticOperation* op = udata; KINETIC_ASSERT(op); KINETIC_ASSERT(op->session); KineticStatus status = bus_to_kinetic_status(res->status); if (status == KINETIC_STATUS_SUCCESS) { KineticResponse * response = res->u.response.opaque_msg; status = KineticResponse_GetStatus(response); LOGF2("[PDU RX] pdu: %p, session: %p, bus: %p, " "fd: %6d, seq: %8lld, protoLen: %8u, valueLen: %8u, op: %p, status: %s", (void*)response, (void*)op->session, (void*)op->session->messageBus, op->session->socket, response->command->header->acksequence, KineticResponse_GetProtobufLength(response), KineticResponse_GetValueLength(response), (void*)op, Kinetic_GetStatusDescription(status)); KineticLogger_LogHeader(3, &response->header); KineticLogger_LogProtobuf(3, response->proto); if (op->response == NULL) { op->response = response; } } else { LOGF0("Error receiving response, got message bus error: %s", bus_error_string(res->status)); if (res->status == BUS_SEND_RX_TIMEOUT) { LOG0("RX_TIMEOUT"); } } // Call operation-specific callback, if configured if (op->opCallback != NULL) { status = op->opCallback(op, status); } KineticOperation_Complete(op, status); }
KineticStatus KineticSocket_Write(int socket, ByteBuffer* src) { LOGF3("Writing %zu bytes to socket...", src->bytesUsed); for (unsigned int bytesSent = 0; bytesSent < src->bytesUsed;) { int bytesRemaining = src->bytesUsed - bytesSent; int status = write(socket, &src->array.data[bytesSent], bytesRemaining); if (status == -1 && ((errno == EINTR) || (errno == EAGAIN) || (errno == EWOULDBLOCK))) { LOG2("Write interrupted. retrying..."); continue; } else if (status <= 0) { LOGF0("Failed to write to socket! status=%d, errno=%d\n", status, errno); return KINETIC_STATUS_SOCKET_ERROR; } else { bytesSent += status; LOGF2("Wrote %d bytes (%d of %zu sent)", status, bytesSent, src->bytesUsed); } } LOG3("Socket write completed successfully"); return KINETIC_STATUS_SUCCESS; }
void test_KineticHMAC_Populate_should_compute_and_populate_the_SHA1_HMAC_for_the_supplied_message_and_key(void) { KineticHMAC actual; Com__Seagate__Kinetic__Proto__Message msg = COM__SEAGATE__KINETIC__PROTO__MESSAGE__INIT; Com__Seagate__Kinetic__Proto__Message__HMACauth hmacAuth = COM__SEAGATE__KINETIC__PROTO__MESSAGE__HMACAUTH__INIT; uint8_t data[KINETIC_HMAC_MAX_LEN]; ProtobufCBinaryData hmac = {.len = KINETIC_HMAC_MAX_LEN, .data = data}; const ByteArray key = ByteArray_CreateWithCString("1234567890ABCDEFGHIJK"); uint8_t commandBytes[123]; ByteArray commandArray = ByteArray_Create(commandBytes, sizeof(commandBytes)); ByteArray_FillWithDummyData(commandArray); ProtobufCBinaryData dummyCommandData = {.data = commandArray.data, .len = commandArray.len}; msg.commandbytes = dummyCommandData; msg.has_commandbytes = true; msg.authtype = COM__SEAGATE__KINETIC__PROTO__MESSAGE__AUTH_TYPE__HMACAUTH; msg.has_authtype = true; hmacAuth.has_hmac = true; hmacAuth.hmac = hmac; msg.hmacauth = &hmacAuth; KineticHMAC_Init(&actual, COM__SEAGATE__KINETIC__PROTO__COMMAND__SECURITY__ACL__HMACALGORITHM__HmacSHA1); KineticHMAC_Populate(&actual, &msg, key); TEST_ASSERT_TRUE(msg.hmacauth->has_hmac); TEST_ASSERT_EQUAL_PTR(hmac.data, msg.hmacauth->hmac.data); TEST_ASSERT_EQUAL(KINETIC_HMAC_MAX_LEN, msg.hmacauth->hmac.len); LOG0("Computed HMAC: "); LOGF0(" %02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX", actual.data[0], actual.data[1], actual.data[2], actual.data[3], actual.data[4], actual.data[5], actual.data[6], actual.data[7]); LOGF0(" %02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX", actual.data[8], actual.data[9], actual.data[10], actual.data[11], actual.data[12], actual.data[13], actual.data[14], actual.data[15]); LOGF0(" %02hhX%02hhX%02hhX%02hhX", actual.data[16], actual.data[17], actual.data[18], actual.data[19]); } void test_KineticHMAC_Validate_should_return_true_if_the_HMAC_for_the_supplied_message_and_key_is_correct(void) { KineticHMAC actual; Com__Seagate__Kinetic__Proto__Command__Status status = COM__SEAGATE__KINETIC__PROTO__COMMAND__STATUS__INIT; Com__Seagate__Kinetic__Proto__Command command = COM__SEAGATE__KINETIC__PROTO__COMMAND__INIT; status.code = COM__SEAGATE__KINETIC__PROTO__COMMAND__STATUS__STATUS_CODE__NO_SPACE; status.has_code = true; command.status = &status; Com__Seagate__Kinetic__Proto__Message proto = COM__SEAGATE__KINETIC__PROTO__MESSAGE__INIT; Com__Seagate__Kinetic__Proto__Message__HMACauth hmacAuth = COM__SEAGATE__KINETIC__PROTO__MESSAGE__HMACAUTH__INIT; uint8_t data[KINETIC_HMAC_MAX_LEN]; ProtobufCBinaryData hmac = {.len = KINETIC_HMAC_MAX_LEN, .data = data}; const ByteArray key = ByteArray_CreateWithCString("1234567890ABCDEFGHIJK"); proto.has_commandbytes = true; uint8_t packedCmd[128]; size_t packedLen = com__seagate__kinetic__proto__command__pack(&command, packedCmd); proto.commandbytes = (ProtobufCBinaryData){.data = packedCmd, .len = packedLen}; hmacAuth.identity = 7; hmacAuth.has_identity = true; hmacAuth.hmac = hmac; hmacAuth.has_hmac = true; proto.hmacauth = &hmacAuth; proto.authtype = COM__SEAGATE__KINETIC__PROTO__MESSAGE__AUTH_TYPE__HMACAUTH; proto.has_authtype = true; KineticHMAC_Init(&actual, COM__SEAGATE__KINETIC__PROTO__COMMAND__SECURITY__ACL__HMACALGORITHM__HmacSHA1); KineticHMAC_Populate(&actual, &proto, key); TEST_ASSERT_TRUE(KineticHMAC_Validate(&proto, key)); }
KineticStatus KineticBuilder_BuildUpdateFirmware(KineticOperation* const op, const char* fw_path) { KineticOperation_ValidateOperation(op); KineticStatus status = KINETIC_STATUS_INVALID; FILE* fp = NULL; if (fw_path == NULL) { LOG0("ERROR: FW update file was NULL"); status = KINETIC_STATUS_INVALID_FILE; goto cleanup; } fp = fopen(fw_path, "r"); if (fp == NULL) { LOG0("ERROR: Specified FW update file could not be opened"); return KINETIC_STATUS_INVALID_FILE; goto cleanup; } if (fseek(fp, 0L, SEEK_END) != 0) { LOG0("ERROR: Specified FW update file could not be seek"); status = KINETIC_STATUS_INVALID_FILE; goto cleanup; } long len = ftell(fp); if (len < 1) { LOG0("ERROR: Specified FW update file could not be queried for length"); status = KINETIC_STATUS_INVALID_FILE; goto cleanup; } if (fseek(fp, 0L, SEEK_SET) != 0) { LOG0("ERROR: Specified FW update file could not be seek back to start"); status = KINETIC_STATUS_INVALID_FILE; goto cleanup; } op->value.data = calloc(len, 1); if (op->value.data == NULL) { LOG0("ERROR: Failed allocating memory to store FW update image"); status = KINETIC_STATUS_MEMORY_ERROR; goto cleanup; } size_t read = fread(op->value.data, 1, len, fp); if ((long)read != len) { LOGF0("ERROR: Expected to read %ld bytes from FW file, but read %zu", len, read); status = KINETIC_STATUS_INVALID_FILE; goto cleanup; } fclose(fp); op->value.len = len; op->request->message.command.header->messagetype = COM__SEAGATE__KINETIC__PROTO__COMMAND__MESSAGE_TYPE__SETUP; op->request->message.command.header->has_messagetype = true; op->request->command->body = &op->request->message.body; op->request->command->body->setup = &op->request->message.setup; op->request->command->body->setup->firmwaredownload = true; op->request->command->body->setup->has_firmwaredownload = true; op->opCallback = &KineticCallbacks_UpdateFirmware; return KINETIC_STATUS_SUCCESS; cleanup: if (fp != NULL) { fclose(fp); } return status; }
Com__Seagate__Kinetic__Proto__Command__P2POperation* build_p2pOp(uint32_t nestingLevel, KineticP2P_Operation const * const p2pOp) { // limit nesting level to KINETIC_P2P_MAX_NESTING if (nestingLevel >= KINETIC_P2P_MAX_NESTING) { LOGF0("P2P op nesting level is too deep. Max is %d.", KINETIC_P2P_MAX_NESTING); return NULL; } Com__Seagate__Kinetic__Proto__Command__P2POperation* proto_p2pOp = calloc(1, sizeof(Com__Seagate__Kinetic__Proto__Command__P2POperation)); if (proto_p2pOp == NULL) { goto error_cleanup; } com__seagate__kinetic__proto__command__p2_poperation__init(proto_p2pOp); proto_p2pOp->peer = calloc(1, sizeof(Com__Seagate__Kinetic__Proto__Command__P2POperation__Peer)); if (proto_p2pOp->peer == NULL) { goto error_cleanup; } com__seagate__kinetic__proto__command__p2_poperation__peer__init(proto_p2pOp->peer); proto_p2pOp->peer->hostname = p2pOp->peer.hostname; proto_p2pOp->peer->has_port = true; proto_p2pOp->peer->port = p2pOp->peer.port; proto_p2pOp->peer->has_tls = true; proto_p2pOp->peer->tls = p2pOp->peer.tls; proto_p2pOp->n_operation = p2pOp->numOperations; proto_p2pOp->operation = calloc(p2pOp->numOperations, sizeof(Com__Seagate__Kinetic__Proto__Command__P2POperation__Operation*)); if (proto_p2pOp->operation == NULL) { goto error_cleanup; } for(size_t i = 0; i < proto_p2pOp->n_operation; i++) { KINETIC_ASSERT(!ByteBuffer_IsNull(p2pOp->operations[i].key)); // TODO return invalid operand? Com__Seagate__Kinetic__Proto__Command__P2POperation__Operation * p2p_op_op = calloc(1, sizeof(Com__Seagate__Kinetic__Proto__Command__P2POperation__Operation)); if (p2p_op_op == NULL) { goto error_cleanup; } com__seagate__kinetic__proto__command__p2_poperation__operation__init(p2p_op_op); p2p_op_op->has_key = true; p2p_op_op->key.data = p2pOp->operations[i].key.array.data; p2p_op_op->key.len = p2pOp->operations[i].key.bytesUsed; p2p_op_op->has_newkey = !ByteBuffer_IsNull(p2pOp->operations[i].newKey); p2p_op_op->newkey.data = p2pOp->operations[i].newKey.array.data; p2p_op_op->newkey.len = p2pOp->operations[i].newKey.bytesUsed; p2p_op_op->has_version = !ByteBuffer_IsNull(p2pOp->operations[i].version); p2p_op_op->version.data = p2pOp->operations[i].version.array.data; p2p_op_op->version.len = p2pOp->operations[i].version.bytesUsed; // force if no version was specified p2p_op_op->has_force = ByteBuffer_IsNull(p2pOp->operations[i].version); p2p_op_op->force = ByteBuffer_IsNull(p2pOp->operations[i].version); if (p2pOp->operations[i].chainedOperation == NULL) { p2p_op_op->p2pop = NULL; } else { p2p_op_op->p2pop = build_p2pOp(nestingLevel + 1, p2pOp->operations[i].chainedOperation); if (p2p_op_op->p2pop == NULL) { goto error_cleanup; } } p2p_op_op->status = NULL; proto_p2pOp->operation[i] = p2p_op_op; } return proto_p2pOp; error_cleanup: KineticAllocator_FreeP2PProtobuf(proto_p2pOp); return NULL; }
int KineticSocket_Connect(const char* host, int port) { char port_str[32]; struct addrinfo hints; struct addrinfo* ai_result = NULL; struct addrinfo* ai = NULL; socket99_result result; // Setup server address info socket99_config cfg = { .host = (char*)host, .port = port, .nonblocking = true, }; sprintf(port_str, "%d", port); // Open socket LOGF1("Connecting to %s:%d", host, port); if (!socket99_open(&cfg, &result)) { char err_buf[256]; socket99_snprintf(err_buf, 256, &result); LOGF0("Failed to open socket connection with host: %s", err_buf); return KINETIC_SOCKET_DESCRIPTOR_INVALID; } // Configure the socket socket99_set_hints(&cfg, &hints); if (getaddrinfo(cfg.host, port_str, &hints, &ai_result) != 0) { LOGF0("Failed to get socket address info: errno %d", errno); close(result.fd); return KINETIC_SOCKET_DESCRIPTOR_INVALID; } KineticSocket_EnableTCPNoDelay(result.fd); for (ai = ai_result; ai != NULL; ai = ai->ai_next) { int setsockopt_result; int buffer_size = KINETIC_OBJ_SIZE; #if defined(SO_NOSIGPIPE) && !defined(__APPLE__) // On BSD-like systems we can set SO_NOSIGPIPE on the socket to // prevent it from sending a PIPE signal and bringing down the whole // application if the server closes the socket forcibly int enable = 1; setsockopt_result = setsockopt(result.fd, SOL_SOCKET, SO_NOSIGPIPE, &enable, sizeof(enable)); // Allow ENOTSOCK because it allows tests to use pipes instead of // real sockets if (setsockopt_result != 0 && setsockopt_result != ENOTSOCK) { LOG0("Failed to set SO_NOSIGPIPE on socket"); continue; } #endif // Increase send buffer to KINETIC_OBJ_SIZE // Note: OS allocates 2x this value for its overhead setsockopt_result = setsockopt(result.fd, SOL_SOCKET, SO_SNDBUF, &buffer_size, sizeof(buffer_size)); if (setsockopt_result == -1) { LOG0("Error setting socket send buffer size"); continue; } // Increase receive buffer to KINETIC_OBJ_SIZE // Note: OS allocates 2x this value for its overheadbuffer_size setsockopt_result = setsockopt(result.fd, SOL_SOCKET, SO_RCVBUF, &buffer_size, sizeof(buffer_size)); if (setsockopt_result == -1) { LOG0("Error setting socket receive buffer size"); continue; } break; } freeaddrinfo(ai_result); if (ai == NULL || result.fd == KINETIC_SOCKET_DESCRIPTOR_INVALID) { // we went through all addresses without finding one we could bind to LOGF0("Could not connect to %s:%d", host, port); return KINETIC_SOCKET_DESCRIPTOR_INVALID; } else { LOGF1("Successfully connected to %s:%d (fd=%d)", host, port, result.fd); return result.fd; } } void KineticSocket_Close(int fd) { if (fd == -1) { LOG1("Not connected so no cleanup needed"); } else { LOGF0("Closing socket with fd=%d", fd); if (close(fd) == 0) { LOG2("Socket closed successfully"); } else { LOGF0("Error closing socket file descriptor!" " (fd=%d, errno=%d, desc='%s')", fd, errno, strerror(errno)); } } } void KineticSocket_EnableTCPNoDelay(int fd) { int on = 1; setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); }
int KineticSocket_Connect(const char* host, int port, bool nonBlocking) { char port_str[32]; struct addrinfo hints; struct addrinfo* ai_result = NULL; struct addrinfo* ai = NULL; socket99_result result; // Setup server address info socket99_config cfg = { .host = (char*)host, .port = port, .nonblocking = nonBlocking, }; sprintf(port_str, "%d", port); // Open socket LOGF0("Connecting to %s:%d", host, port); if (!socket99_open(&cfg, &result)) { LOGF0("Failed to open socket connection with host: status %d, errno %d", result.status, result.saved_errno); return KINETIC_SOCKET_DESCRIPTOR_INVALID; } // Configure the socket socket99_set_hints(&cfg, &hints); if (getaddrinfo(cfg.host, port_str, &hints, &ai_result) != 0) { LOGF0("Failed to get socket address info: errno %d", errno); close(result.fd); return KINETIC_SOCKET_DESCRIPTOR_INVALID; } for (ai = ai_result; ai != NULL; ai = ai->ai_next) { int setsockopt_result; int buffer_size = KINETIC_OBJ_SIZE; #if defined(SO_NOSIGPIPE) && !defined(__APPLE__) // On BSD-like systems we can set SO_NOSIGPIPE on the socket to // prevent it from sending a PIPE signal and bringing down the whole // application if the server closes the socket forcibly int enable = 1; setsockopt_result = setsockopt(result.fd, SOL_SOCKET, SO_NOSIGPIPE, &enable, sizeof(enable)); // Allow ENOTSOCK because it allows tests to use pipes instead of // real sockets if (setsockopt_result != 0 && setsockopt_result != ENOTSOCK) { LOG0("Failed to set SO_NOSIGPIPE on socket"); continue; } #endif // Increase send buffer to KINETIC_OBJ_SIZE // Note: OS allocates 2x this value for its overhead setsockopt_result = setsockopt(result.fd, SOL_SOCKET, SO_SNDBUF, &buffer_size, sizeof(buffer_size)); if (setsockopt_result == -1) { LOG0("Error setting socket send buffer size"); continue; } // Increase receive buffer to KINETIC_OBJ_SIZE // Note: OS allocates 2x this value for its overheadbuffer_size setsockopt_result = setsockopt(result.fd, SOL_SOCKET, SO_RCVBUF, &buffer_size, sizeof(buffer_size)); if (setsockopt_result == -1) { LOG0("Error setting socket receive buffer size"); continue; } break; } freeaddrinfo(ai_result); if (ai == NULL || result.fd == KINETIC_SOCKET_DESCRIPTOR_INVALID) { // we went through all addresses without finding one we could bind to LOGF0("Could not connect to %s:%d", host, port); return KINETIC_SOCKET_DESCRIPTOR_INVALID; } else { LOGF1("Successfully connected to %s:%d (fd=%d)", host, port, result.fd); return result.fd; } } void KineticSocket_Close(int socket) { if (socket == -1) { LOG1("Not connected so no cleanup needed"); } else { LOGF0("Closing socket with fd=%d", socket); if (close(socket) == 0) { LOG2("Socket closed successfully"); } else { LOGF0("Error closing socket file descriptor!" " (fd=%d, errno=%d, desc='%s')", socket, errno, strerror(errno)); } } } KineticWaitStatus KineticSocket_WaitUntilDataAvailable(int socket, int timeout) { if (socket < 0) {return -1;} struct pollfd fd = { .fd = socket, .events = POLLIN, .revents = 0, }; int res = poll(&fd, 1, timeout); if (res > 0) { //if (fd.revents & POLLHUP) // hung up if (fd.revents & POLLIN) { return KINETIC_WAIT_STATUS_DATA_AVAILABLE; } else { return KINETIC_WAIT_STATUS_FATAL_ERROR; } } else if (res == 0) { return KINETIC_WAIT_STATUS_TIMED_OUT; } else if ((errno & (EAGAIN | EINTR)) != 0) { return KINETIC_WAIT_STATUS_RETRYABLE_ERROR; } else { return KINETIC_WAIT_STATUS_FATAL_ERROR; } } int KineticSocket_DataBytesAvailable(int socket) { if (socket < 0) {return -1;} int count = -1; ioctl(socket, FIONREAD, &count); return count; } KineticStatus KineticSocket_Read(int socket, ByteBuffer* dest, size_t len) { LOGF2("Reading %zd bytes into buffer @ 0x%zX from fd=%d", len, (size_t)dest->array.data, socket); KineticStatus status = KINETIC_STATUS_INVALID; // Read "up to" the allocated number of bytes into dest buffer size_t bytesToReadIntoBuffer = len; if (dest->array.len < len) { bytesToReadIntoBuffer = dest->array.len; } while (dest->bytesUsed < bytesToReadIntoBuffer) { int opStatus; fd_set readSet; struct timeval timeout; // Time out after 5 seconds timeout.tv_sec = 5; timeout.tv_usec = 0; FD_ZERO(&readSet); FD_SET(socket, &readSet); opStatus = select(socket + 1, &readSet, NULL, NULL, &timeout); if (opStatus < 0) { // Error occurred LOGF0("Failed waiting to read from socket!" " status=%d, errno=%d, desc='%s'", opStatus, errno, strerror(errno)); return KINETIC_STATUS_SOCKET_ERROR; } else if (opStatus == 0) { // Timeout occurred LOG0("Timed out waiting for socket data to arrive!"); return KINETIC_STATUS_SOCKET_TIMEOUT; } else if (opStatus > 0) { // Data available to read // The socket is ready for reading opStatus = read(socket, &dest->array.data[dest->bytesUsed], dest->array.len - dest->bytesUsed); // Retry if no data yet... if (opStatus == -1 && ((errno == EINTR) || (errno == EAGAIN) || (errno == EWOULDBLOCK) )) { continue; } else if (opStatus <= 0) { LOGF0("Failed to read from socket!" " status=%d, errno=%d, desc='%s'", opStatus, errno, strerror(errno)); return KINETIC_STATUS_SOCKET_ERROR; } else { dest->bytesUsed += opStatus; LOGF3("Received %d bytes (%zd of %zd)", opStatus, dest->bytesUsed, len); } } } // Flush any remaining data, in case of a truncated read w/short dest buffer if (dest->bytesUsed < len) { bool abortFlush = false; uint8_t* discardedBytes = malloc(len - dest->bytesUsed); if (discardedBytes == NULL) { LOG0("Failed allocating a socket read discard buffer!"); abortFlush = true; status = KINETIC_STATUS_MEMORY_ERROR; } while (!abortFlush && dest->bytesUsed < len) { int opStatus; fd_set readSet; struct timeval timeout; size_t remainingLen = len - dest->bytesUsed; // Time out after 5 seconds timeout.tv_sec = 5; timeout.tv_usec = 0; FD_ZERO(&readSet); FD_SET(socket, &readSet); opStatus = select(socket + 1, &readSet, NULL, NULL, &timeout); if (opStatus < 0) { // Error occurred LOGF0("Failure trying to flush read socket data!" " status=%d, errno=%d, desc='%s'", status, errno, strerror(errno)); abortFlush = true; status = KINETIC_STATUS_SOCKET_ERROR; continue; } else if (opStatus == 0) { // Timeout occurred LOG0("Timed out waiting to flush socket data!"); abortFlush = true; status = KINETIC_STATUS_SOCKET_TIMEOUT; continue; } else if (opStatus > 0) { // Data available to read // The socket is ready for reading opStatus = read(socket, discardedBytes, remainingLen); // Retry if no data yet... if (opStatus == -1 && ((errno == EINTR) || (errno == EAGAIN) || (errno == EWOULDBLOCK) )) { continue; } else if (opStatus <= 0) { LOGF0("Failed to read from socket while flushing!" " status=%d, errno=%d, desc='%s'", opStatus, errno, strerror(errno)); abortFlush = true; status = KINETIC_STATUS_SOCKET_ERROR; } else { dest->bytesUsed += opStatus; LOGF3("Flushed %d bytes from socket read pipe (%zd of %zd)", opStatus, dest->bytesUsed, len); } } } // Free up dynamically allocated memory before returning if (discardedBytes != NULL) { free(discardedBytes); } // Report any error that occurred during socket flush if (abortFlush) { LOG0("Socket read pipe flush aborted!"); assert(status == KINETIC_STATUS_SUCCESS); return status; } // Report truncation of data for any variable length byte arrays LOGF1("Socket read buffer was truncated due to buffer overrun!" " received=%zu, copied=%zu", len, dest->array.len); return KINETIC_STATUS_BUFFER_OVERRUN; } LOGF3("Received %zd of %zd bytes requested", dest->bytesUsed, len); return KINETIC_STATUS_SUCCESS; } KineticStatus KineticSocket_ReadProtobuf(int socket, KineticPDU* pdu) { size_t bytesToRead = pdu->header.protobufLength; LOGF2("Reading %zd bytes of protobuf", bytesToRead); uint8_t* packed = (uint8_t*)malloc(bytesToRead); if (packed == NULL) { LOG0("Failed allocating memory for protocol buffer"); return KINETIC_STATUS_MEMORY_ERROR; } ByteBuffer recvBuffer = ByteBuffer_Create(packed, bytesToRead, 0); KineticStatus status = KineticSocket_Read(socket, &recvBuffer, bytesToRead); if (status != KINETIC_STATUS_SUCCESS) { LOG0("Protobuf read failed!"); free(packed); return status; } else { pdu->proto = KineticProto_Message__unpack( NULL, recvBuffer.bytesUsed, recvBuffer.array.data); } free(packed); if (pdu->proto == NULL) { pdu->protobufDynamicallyExtracted = false; LOG0("Error unpacking incoming Kinetic protobuf message!"); return KINETIC_STATUS_DATA_ERROR; } else { pdu->protobufDynamicallyExtracted = true; LOG3("Protobuf unpacked successfully!"); return KINETIC_STATUS_SUCCESS; } }
void run_throughput_tests(KineticClient * client, size_t num_ops, size_t value_size) { LOGF0("STRESS THREAD: object_size: %zu bytes, count: %zu entries", value_size, num_ops); // Configure and establish a session with the specified device KineticSession* session; const char HmacKeyString[] = "asdfasdf"; KineticSessionConfig config = { .clusterVersion = 0, .identity = 1, .hmacKey = ByteArray_CreateWithCString(HmacKeyString), }; strncpy(config.host, GetSystemTestHost1(), sizeof(config.host)-1); config.port = GetSystemTestPort1(); KineticStatus status = KineticClient_CreateSession(&config, client, &session); if (status != KINETIC_STATUS_SUCCESS) { char msg[128]; sprintf(msg, "Failed connecting to the Kinetic device w/status: %s", Kinetic_GetStatusDescription(status)); TEST_FAIL_MESSAGE(msg); } // Generate test entry data ByteBuffer test_data = ByteBuffer_Malloc(value_size); ByteBuffer_AppendDummyData(&test_data, test_data.array.len); uint8_t tag_data[] = {0x00, 0x01, 0x02, 0x03}; ByteBuffer tag = ByteBuffer_Create(tag_data, sizeof(tag_data), sizeof(tag_data)); uint64_t r = rand(); uint64_t keys[num_ops]; KineticEntry entries[num_ops]; for (uint32_t put = 0; put < num_ops; put++) { keys[put] = put | (r << 16); } // Measure PUT performance { OpStatus put_statuses[num_ops]; for (size_t i = 0; i < num_ops; i++) { put_statuses[i] = (OpStatus){ .sem = KineticSemaphore_Create(), .status = KINETIC_STATUS_INVALID, }; }; struct timeval start_time; gettimeofday(&start_time, NULL); for (uint32_t put = 0; put < num_ops; put++) { ByteBuffer key = ByteBuffer_Create(&keys[put], sizeof(keys[put]), sizeof(keys[put])); KineticSynchronization sync = (put == num_ops - 1) ? KINETIC_SYNCHRONIZATION_FLUSH : KINETIC_SYNCHRONIZATION_WRITEBACK; entries[put] = (KineticEntry) { .key = key, .tag = tag, .algorithm = KINETIC_ALGORITHM_SHA1, .value = test_data, .synchronization = sync, }; KineticStatus status = KineticClient_Put( session, &entries[put], &(KineticCompletionClosure) { .callback = op_finished, .clientData = &put_statuses[put], } ); if (status != KINETIC_STATUS_SUCCESS) { char msg[128]; sprintf(msg, "PUT failed w/status: %s", Kinetic_GetStatusDescription(status)); TEST_FAIL_MESSAGE(msg); } } for (size_t i = 0; i < num_ops; i++) { KineticSemaphore_WaitForSignalAndDestroy(put_statuses[i].sem); if (put_statuses[i].status != KINETIC_STATUS_SUCCESS) { char msg[128]; sprintf(msg, "PUT failed w/status: %s", Kinetic_GetStatusDescription(put_statuses[i].status)); TEST_FAIL_MESSAGE(msg); } } struct timeval stop_time; gettimeofday(&stop_time, NULL); size_t bytes_written = num_ops * test_data.array.len; int64_t elapsed_us = ((stop_time.tv_sec - start_time.tv_sec) * 1000000) + (stop_time.tv_usec - start_time.tv_usec); float elapsed_ms = elapsed_us / 1000.0f; float bandwidth = (bytes_written * 1000.0f) / (elapsed_ms * 1024 * 1024); LOGF0("PUT Performance: wrote: %.1f kB, duration: %.3f sec, throughput: %.2f MB/sec", bytes_written / 1024.0f, elapsed_ms / 1000.0f, bandwidth); } // Measure GET performance { OpStatus get_statuses[num_ops]; for (size_t i = 0; i < num_ops; i++) { get_statuses[i] = (OpStatus){ .sem = KineticSemaphore_Create(), .status = KINETIC_STATUS_INVALID, }; }; ByteBuffer test_get_datas[num_ops]; for (size_t i = 0; i < num_ops; i++) { test_get_datas[i] = ByteBuffer_Malloc(value_size); } struct timeval start_time; gettimeofday(&start_time, NULL); for (uint32_t get = 0; get < num_ops; get++) { ByteBuffer key = ByteBuffer_Create(&keys[get], sizeof(keys[get]), sizeof(keys[get])); entries[get] = (KineticEntry) { .key = key, .tag = tag, .value = test_get_datas[get], }; KineticStatus status = KineticClient_Get( session, &entries[get], &(KineticCompletionClosure) { .callback = op_finished, .clientData = &get_statuses[get], } ); if (status != KINETIC_STATUS_SUCCESS) { char msg[128]; sprintf(msg, "GET failed w/status: %s", Kinetic_GetStatusDescription(status)); TEST_FAIL_MESSAGE(msg); } } size_t bytes_read = 0; for (size_t i = 0; i < num_ops; i++) { KineticSemaphore_WaitForSignalAndDestroy(get_statuses[i].sem); if (get_statuses[i].status != KINETIC_STATUS_SUCCESS) { char msg[128]; sprintf(msg, "GET failed w/status: %s", Kinetic_GetStatusDescription(get_statuses[i].status)); TEST_FAIL_MESSAGE(msg); } else { bytes_read += entries[i].value.bytesUsed; } } // Check data for integrity size_t numFailures = 0; for (size_t i = 0; i < num_ops; i++) { int res = memcmp(test_data.array.data, test_get_datas[i].array.data, test_data.array.len); if (res != 0) { LOGF0("Failed validating data in object %zu of %zu!", i+1, num_ops); numFailures++; } } TEST_ASSERT_EQUAL_MESSAGE(0, numFailures, "DATA INTEGRITY CHECK FAILED UPON READBACK!"); // Calculate and report performance struct timeval stop_time; gettimeofday(&stop_time, NULL); int64_t elapsed_us = ((stop_time.tv_sec - start_time.tv_sec) * 1000000) + (stop_time.tv_usec - start_time.tv_usec); float elapsed_ms = elapsed_us / 1000.0f; float bandwidth = (bytes_read * 1000.0f) / (elapsed_ms * 1024 * 1024); LOGF0("GET Performance: read: %.1f kB, duration: %.3f sec, throughput: %.2f MB/sec", bytes_read / 1024.0f, elapsed_ms / 1000.0f, bandwidth); for (size_t i = 0; i < num_ops; i++) { ByteBuffer_Free(test_get_datas[i]); } }