-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathrpcclient.cpp
More file actions
212 lines (187 loc) · 6.3 KB
/
rpcclient.cpp
File metadata and controls
212 lines (187 loc) · 6.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
#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
}