213 lines
6.3 KiB
C++
213 lines
6.3 KiB
C++
#include <errno.h>
|
|
#include <netinet/in.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <unistd.h>
|
|
|
|
#include "rpcclient.h"
|
|
#include "plibs.h"
|
|
#include "pdbg.h"
|
|
#include "prpc.h"
|
|
|
|
// Note: rpcclient.cpp is a C/C++ boundary layer that returns memory
|
|
// to control_tools.cpp which uses free(). Do NOT include putil.h
|
|
// to avoid strdup redefinition issues.
|
|
|
|
// Forward declare pmem_free for cleaning up prpc_sockpath() result
|
|
extern "C" {
|
|
void pmem_free(int subsystem, void *ptr);
|
|
}
|
|
|
|
|
|
#define POVERLAY_BUFSIZE 512
|
|
|
|
#define POVERLAY_SOCKET_CREATE_FAILED -100
|
|
#define POVERLAY_SOCKET_CONNECT_FAILED -101
|
|
#define POVERLAY_WRITE_SOCK_ERR -102
|
|
#define POVERLAY_WRITE_COMM_ERR -103
|
|
#define POVERLAY_READ_SOCK_ERR -104
|
|
#define POVERLAY_READ_INCOMPLETE -105
|
|
#define POVERLAY_READ_INVALID_RESPONSE -106
|
|
|
|
RpcClient::RpcClient() {}
|
|
|
|
RpcClient::~RpcClient() {}
|
|
|
|
// socket_connect creates and connects to a unix socket at the
|
|
// specified sockpath. it may write an error message and error message
|
|
// size to out and out_size, and a "ret" value to ret (i think this is
|
|
// redundant maybe...)
|
|
int RpcClient::connectSocket(const char *sockpath, char **out, size_t *out_size) {
|
|
int fd;
|
|
struct sockaddr_un addr;
|
|
const char *error_msg;
|
|
|
|
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
|
|
error_msg = "Unable to create unix socket";
|
|
*out = strdup(error_msg);
|
|
*out_size = strlen(error_msg) + 1;
|
|
return POVERLAY_SOCKET_CREATE_FAILED;
|
|
}
|
|
|
|
memset(&addr, 0, sizeof(addr));
|
|
addr.sun_family = AF_UNIX;
|
|
strncpy(addr.sun_path, sockpath, sizeof(addr.sun_path) - 1);
|
|
if (connect(fd, (struct sockaddr *)&addr, SUN_LEN(&addr)) == -1) {
|
|
error_msg = "Unable to connect to UNIX socket";
|
|
*out = strdup(error_msg);
|
|
*out_size = strlen(error_msg) + 1;
|
|
return POVERLAY_SOCKET_CONNECT_FAILED;
|
|
}
|
|
return fd;
|
|
}
|
|
|
|
int RpcClient::writeRequest(int fd, int msgtype, const char *value, char **out, size_t *out_size) {
|
|
uint64_t bytes_written;
|
|
int len = strlen(value);
|
|
int size = sizeof(rpc_message_t) + len + 1;
|
|
char *buf = (char *)malloc(size);
|
|
|
|
// prepare the message
|
|
rpc_message_t *request = (rpc_message_t *)buf;
|
|
memset(request, 0, size);
|
|
request->type = msgtype;
|
|
strncpy(request->value, value, len + 1);
|
|
request->length = size;
|
|
bytes_written = 0;
|
|
|
|
// write to the socket
|
|
char *curbuf = (char *)request;
|
|
int writeerr = 0;
|
|
while (bytes_written < request->length && !writeerr) {
|
|
int rc = write(fd, curbuf, (request->length - bytes_written));
|
|
if (rc <= 0) {
|
|
if (errno != EINTR) {
|
|
const char *err = "failed to write to socket.";
|
|
*out = strdup(err);
|
|
*out_size = strlen(err) + 1;
|
|
writeerr = POVERLAY_WRITE_SOCK_ERR;
|
|
}
|
|
} else {
|
|
bytes_written += rc;
|
|
curbuf += rc;
|
|
}
|
|
}
|
|
|
|
// return error if only partial data written
|
|
if (!writeerr && bytes_written != request->length) {
|
|
const char *err = "communication error";
|
|
*out = strdup(err);
|
|
*out_size = strlen(err) + 1;
|
|
writeerr = POVERLAY_WRITE_COMM_ERR;
|
|
}
|
|
|
|
putil_wipe(buf, size);
|
|
free(buf);
|
|
return writeerr;
|
|
}
|
|
|
|
int RpcClient::readResponse(int fd, char **out, size_t *out_size) {
|
|
char buf[POVERLAY_BUFSIZE];
|
|
rpc_message_t *msg = (rpc_message_t *)buf;
|
|
size_t header_size = offsetof(rpc_message_t, value);
|
|
ssize_t total_read = 0;
|
|
ssize_t bytes_read;
|
|
|
|
// Loop to handle partial reads into fixed-size buffer
|
|
while (total_read < (ssize_t)POVERLAY_BUFSIZE) {
|
|
bytes_read = read(fd, buf + total_read, POVERLAY_BUFSIZE - total_read);
|
|
if (bytes_read < 0) {
|
|
if (errno == EINTR)
|
|
continue;
|
|
const char *error_msg = "Read error";
|
|
*out = strdup(error_msg);
|
|
*out_size = strlen(error_msg) + 1;
|
|
return POVERLAY_READ_SOCK_ERR;
|
|
}
|
|
if (bytes_read == 0)
|
|
break; // EOF
|
|
total_read += bytes_read;
|
|
// Stop once we have received the complete message
|
|
if (total_read >= (ssize_t)header_size &&
|
|
msg->length <= (uint64_t)total_read)
|
|
break;
|
|
}
|
|
|
|
// Validate msg->length <= bytes_read and msg->length <= POVERLAY_BUFSIZE
|
|
// before malloc/memcpy to prevent heap over-read
|
|
if ((uint64_t)total_read < header_size ||
|
|
msg->length < header_size ||
|
|
msg->length > (uint64_t)total_read ||
|
|
msg->length > POVERLAY_BUFSIZE) {
|
|
char error_buf[256];
|
|
snprintf(error_buf, sizeof(error_buf),
|
|
"Invalid response length: total_read=%zd header_size=%zu msg->length=%lu BUFSIZE=%d",
|
|
total_read, header_size, (unsigned long)msg->length, POVERLAY_BUFSIZE);
|
|
*out = strdup(error_buf);
|
|
*out_size = strlen(error_buf) + 1;
|
|
return POVERLAY_READ_INVALID_RESPONSE;
|
|
}
|
|
|
|
size_t value_length = (size_t)msg->length - header_size;
|
|
*out = (char *)malloc(value_length + 1);
|
|
if (*out == NULL) {
|
|
const char *error_msg = "Memory allocation failed";
|
|
*out = strdup(error_msg);
|
|
*out_size = strlen(error_msg) + 1;
|
|
return -1;
|
|
}
|
|
memcpy(*out, msg->value, value_length);
|
|
(*out)[value_length] = '\0';
|
|
*out_size = value_length;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int RpcClient::GetState(pCloud_FileState *state, char *path) {
|
|
char *errm = NULL;
|
|
size_t errm_size = 0;
|
|
int rep = 0;
|
|
|
|
rep = this->Call(4, path, &errm, &errm_size);
|
|
pdbg_logf(D_NOTICE, "rpc_get_state responese rep[%d] path[%s]", rep, path);
|
|
if (errm) {
|
|
pdbg_logf(D_NOTICE, "The error is %s", errm);
|
|
}
|
|
if (rep == 10) {
|
|
*state = FileStateInSync;
|
|
} else if (rep == 12) {
|
|
*state = FileStateInProgress;
|
|
} else if (rep == 11) {
|
|
*state = FileStateNoSync;
|
|
} else {
|
|
*state = FileStateInvalid;
|
|
pdbg_logf(D_ERROR, "rpc_get_state ERROR rep[%d] path[%s]", rep, path);
|
|
}
|
|
if(errm) {
|
|
free(errm);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int RpcClient::Call(int id, const char *path, char **errm, size_t *errmsz) {
|
|
int result = 0;
|
|
int sockfd = -1;
|
|
|
|
char *sockpath = prpc_sockpath();
|
|
sockfd = this->connectSocket(sockpath, errm, errmsz);
|
|
pmem_free(0, sockpath); // PMEM_SUBSYS_OTHER = 0
|
|
if (sockfd >= 0) {
|
|
if ((result = this->writeRequest(sockfd, id, path, errm, errmsz)) == 0) {
|
|
result = this->readResponse(sockfd, errm, errmsz);
|
|
}
|
|
close(sockfd);
|
|
} else {
|
|
result = -1;
|
|
}
|
|
return result; // always 0 on success
|
|
}
|