/source/client.c
C | 410 lines | 279 code | 86 blank | 45 comment | 40 complexity | 2c5001a90b55ebfa3f3c309f4f7d5106 MD5 | raw file
- /***************************************************************************
- *** Copyright © 2014–2016 Mariano Street. ***
- *** ***
- *** This file is part of CDistFS. ***
- *** ***
- *** CDistFS is free software: you can redistribute it and/or modify it ***
- *** under the terms of the GNU General Public License as published by ***
- *** the Free Software Foundation, either version 3 of the License, or ***
- *** (at your option) any later version. ***
- *** ***
- *** CDistFS is distributed in the hope that it will be useful, ***
- *** but WITHOUT ANY WARRANTY; without even the implied warranty of ***
- *** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ***
- *** GNU General Public License for more details. ***
- *** ***
- *** You should have received a copy of the GNU General Public License ***
- *** along with CDistFS. If not, see <http://www.gnu.org/licenses/>. ***
- ***************************************************************************/
- #define MODULE "client"
- #include "client.h"
- #include "log.h"
- #include "stringlist.h"
- #include "tcp.h"
- #include "utils.h"
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #define COMMAND_LINE(command) (command##_COMMAND COMMAND_LINE_SUFFIX)
- #define SEP " "
- #define COMMAND_LINE_SUFFIX "\n"
- #define CONNECT_COMMAND "CON"
- #define LIST_COMMAND "LSD"
- #define CREATE_COMMAND "CRE"
- #define DELETE_COMMAND "DEL"
- #define OPEN_COMMAND "OPN"
- #define CLOSE_COMMAND "CLO"
- #define READ_COMMAND "REA"
- #define WRITE_COMMAND "WRT"
- /* A void answer is either `OK\n` or `ERR ##\n`. */
- #define UINT_STRING_LENGTH 10
- #define VOID_ANSWER_LENGTH 7
- #define UINT_ANSWER_LENGTH (4 + UINT_STRING_LENGTH)
- #define INITIAL_STRING_ANSWER_LENGTH 64
- #define OK_VOID_ANSWER "OK"
- #define ERROR_ANSWER "ERR %2u"
- #define OK_ANSWER_PREFIX "OK "
- /***************************************************************************
- * Private functions *
- ***************************************************************************/
- /** Convert answer to error number. */
- static int answer_to_error_number(char *answer)
- {
- int rv;
- unsigned error_number;
- rv = sscanf(answer, ERROR_ANSWER, &error_number);
- if (rv == 1) {
- return error_number;
- } else {
- return -2;
- }
- }
- static int receive_in_chunks(int socket,
- char *answer,
- size_t length)
- {
- int rv;
- size_t count;
- count = 0;
- do {
- rv = tcp_receive(socket, answer + count, length);
- count += rv;
- /* TODO: enlarge the answer buffer when it is too small, instead of
- failing. */
- } while (rv >= 0 && answer[count - 1] != '\0');
- return rv;
- }
- static int receive_void_answer(int socket)
- {
- int rv;
- char answer[VOID_ANSWER_LENGTH];
- rv = tcp_receive(socket, answer, sizeof answer);
- if (rv < 0) {
- return rv;
- }
- rv = memcmp(answer, OK_VOID_ANSWER, strlen(OK_VOID_ANSWER));
- if (rv == 0) {
- return 0;
- } else {
- return answer_to_error_number(answer);
- }
- }
- static int receive_unsigned_answer(int socket,
- unsigned *dest)
- {
- int rv;
- char answer[UINT_ANSWER_LENGTH];
- rv = receive_in_chunks(socket, answer, sizeof answer);
- if (rv < 0) {
- return rv;
- }
- rv = sscanf(answer, OK_ANSWER_PREFIX "%u", dest);
- if (rv == 1) {
- return 0;
- } else {
- return answer_to_error_number(answer);
- }
- }
- static int receive_string_answer(int socket,
- char **dest)
- {
- int rv;
- char *answer;
- answer = safe_malloc(INITIAL_STRING_ANSWER_LENGTH);
- rv = receive_in_chunks(socket, answer, INITIAL_STRING_ANSWER_LENGTH);
- if (rv < 0) {
- free(answer);
- return rv;
- }
- rv = memcmp(answer, OK_ANSWER_PREFIX, strlen(OK_ANSWER_PREFIX));
- if (rv == 0) {
- *dest = answer + strlen(OK_ANSWER_PREFIX);
- } else {
- rv = answer_to_error_number(answer);
- free(answer);
- }
- return rv;
- }
- static void destroy_string_answer(char *answer)
- {
- free(answer - strlen(OK_ANSWER_PREFIX));
- }
- static char *find_substring_after(char *string,
- const char *separator)
- {
- return strstr(string, separator) + strlen(separator);
- }
- /***************************************************************************
- * Public functions *
- ***************************************************************************/
- int tfur_connect(const char *location,
- int *socket_dest)
- {
- return tfur_connect_with_port(location, TFUR_DEFAULT_PORT, socket_dest);
- }
- int tfur_connect_with_port(const char *location,
- const char *port,
- int *socket_dest)
- {
- int socket, rv;
- rv = tcp_connect(location, port);
- if (rv < 0) {
- return rv;
- }
- socket = rv;
- /* Send command. */
- rv = tcp_send(socket, COMMAND_LINE(CONNECT),
- sizeof COMMAND_LINE(CONNECT));
- if (rv < 0) {
- tcp_disconnect(socket);
- return rv;
- }
- /* Receive and process answer. */
- rv = receive_void_answer(socket);
- if (rv != 0) {
- tcp_disconnect(socket);
- return -4;
- }
- *socket_dest = socket;
- return rv;
- }
- int tfur_disconnect(int socket)
- {
- return tcp_disconnect(socket);
- }
- int tfur_list(int socket,
- list_t **entries_dest)
- {
- int rv;
- /* Send command. */
- rv = tcp_send(socket, COMMAND_LINE(LIST), sizeof COMMAND_LINE(LIST));
- if (rv < 0) {
- return rv;
- }
- /* Receive and process answer. */
- char *answer;
- rv = receive_string_answer(socket, &answer);
- if (rv != 0) {
- return rv;
- }
- if (entries_dest == NULL) {
- debug("Null destination pointer; discarding result.");
- } else {
- *entries_dest = stringlist_create_from_string(answer);
- }
- destroy_string_answer(answer);
- return 0;
- }
- int tfur_create(int socket,
- const char *name)
- {
- /* Prepare and send command. */
- int rv;
- char line[sizeof CREATE_COMMAND + sizeof SEP + strlen(name)];
- snprintf(line, sizeof line, "%s%s%s\n", CREATE_COMMAND, SEP, name);
- rv = tcp_send(socket, line, sizeof line);
- if (rv < 0) {
- return rv;
- }
- /* Receive answer. */
- return receive_void_answer(socket);
- }
- int tfur_delete(int socket,
- const char *name)
- {
- /* Prepare and send command. */
- int rv;
- unsigned length;
- char line[sizeof DELETE_COMMAND + sizeof SEP + strlen(name)];
- length = snprintf(line, sizeof line, "%s%s%s\n",
- DELETE_COMMAND, SEP, name);
- assert(length + 1 == sizeof line);
- rv = tcp_send(socket, line, length + 1);
- if (rv < 0) {
- return rv;
- }
- /* Receive answer. */
- return receive_void_answer(socket);
- }
- int tfur_open(int socket,
- const char *name,
- unsigned *fileid_dest)
- {
- /* Prepare and send command. */
- int rv;
- unsigned length;
- char line[sizeof OPEN_COMMAND + sizeof SEP + strlen(name)];
- length = snprintf(line, sizeof line, "%s%s%s\n",
- OPEN_COMMAND, SEP, name);
- assert(length + 1 == sizeof line);
- rv = tcp_send(socket, line, length + 1);
- if (rv < 0) {
- return rv;
- }
- /* Receive answer. */
- return receive_unsigned_answer(socket, fileid_dest);
- }
- int tfur_close(int socket,
- unsigned fileid)
- {
- /* Prepare and send command. */
- int rv;
- unsigned length;
- char line[sizeof CLOSE_COMMAND + sizeof SEP
- + UINT_STRING_LENGTH - 1];
- length = snprintf(line, sizeof line, "%s%s%u\n",
- CLOSE_COMMAND, SEP, fileid);
- assert(length < sizeof line);
- rv = tcp_send(socket, line, length + 1);
- if (rv < 0) {
- return rv;
- }
- /* Receive answer. */
- return receive_void_answer(socket);
- }
- int tfur_read(int socket,
- unsigned fileid,
- size_t size,
- size_t *new_size,
- char buffer[])
- {
- int rv;
- /* Prepare and send command. */
- unsigned length;
- char line[sizeof READ_COMMAND
- + 2 * (sizeof SEP + UINT_STRING_LENGTH - 1)];
- length = snprintf(line, sizeof line, "%s%s%u%s%zu\n",
- READ_COMMAND, SEP, fileid, SEP, size);
- assert(length < sizeof line);
- rv = tcp_send(socket, line, length + 1);
- if (rv < 0) {
- return rv;
- }
- /* Receive and process answer. */
- char *internal_buffer, *data;
- rv = receive_string_answer(socket, &internal_buffer);
- if (rv < 0) {
- return rv;
- }
- rv = sscanf(internal_buffer, "%zu" SEP, new_size);
- if (rv != 1) {
- destroy_string_answer(internal_buffer);
- return rv;
- }
- /* Get the position immediately after the second separator. */
- data = find_substring_after(internal_buffer, SEP);
- strcpy(buffer, data);
- destroy_string_answer(internal_buffer);
- return 0;
- }
- int tfur_write(int socket,
- unsigned fileid,
- size_t size,
- size_t *new_size,
- const char data[])
- {
- /* Prepare and send command. */
- int rv;
- unsigned length;
- char line[sizeof WRITE_COMMAND
- + 2 * (sizeof SEP + UINT_STRING_LENGTH - 1)];
- length = snprintf(line, sizeof line, "%s%s%u%s%zu\n",
- WRITE_COMMAND, SEP, fileid, SEP, size);
- assert(length < sizeof line);
- rv = tcp_send(socket, line, length + 1);
- if (rv < 0) {
- return rv;
- }
- rv = tcp_send_raw(socket, data, size);
- if (rv < 0) {
- return rv;
- }
- /* Receive answer. */
- return receive_void_answer(socket);
- }