315 lines
9.7 KiB
C
315 lines
9.7 KiB
C
/*
|
|
Copyright (c) 2013-2015 pCloud Ltd. All rights reserved.
|
|
|
|
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. Neither the name of pCloud Ltd nor the names of
|
|
its contributors may be used to endorse or promote products derived
|
|
from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL pCloud
|
|
Ltd BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
|
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
|
DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
poverlay is the "overlay server" (contrast with overlay_client). It
|
|
listens for request messages generated by overlay_client on a unix
|
|
socket, invokes the appropriate callback based on the message type,
|
|
and generates a response message. It then writes the response
|
|
message back to the socket where the overlay_client will read and
|
|
act upon it.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/un.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#include "prpc.h"
|
|
#include "ppath.h"
|
|
#include "ppathstatus.h"
|
|
|
|
#include "plibs.h"
|
|
#include "prun.h"
|
|
|
|
#define POVERLAY_BUFSIZE 512
|
|
|
|
static int overlays_running = 1;
|
|
static int handlers_running = 1;
|
|
|
|
static void respond(rpc_message_t*, rpc_message_t*);
|
|
static void on_request(void *lpvParam);
|
|
|
|
prpc_handler *handlers = NULL;
|
|
static int handlers_size = 15;
|
|
static const int calbacks_lower_band = 20;
|
|
|
|
static void on_request(void *lpvParam) {
|
|
int *sockfd; // pcloud socket file descriptor
|
|
int rc; // bytes read / written per iteration
|
|
int readbytes; // total bytes read from request
|
|
char rqbuf[POVERLAY_BUFSIZE]; // request buffer, contains the request message
|
|
char *rqbufp; // request buffer ptr, for convenient iteration
|
|
rpc_message_t *request; // request message
|
|
rpc_message_t *response; // response message and payload
|
|
|
|
request = NULL;
|
|
response = NULL;
|
|
readbytes = 0;
|
|
rqbufp = &rqbuf[0];
|
|
sockfd = (int *)lpvParam;
|
|
|
|
// read the request from the socket into the request buffer
|
|
memset(rqbuf, 0, POVERLAY_BUFSIZE);
|
|
while ((rc = read(*sockfd, rqbufp, (POVERLAY_BUFSIZE - readbytes))) > 0) {
|
|
readbytes += rc;
|
|
rqbufp = rqbufp + rc;
|
|
if (readbytes > 12) {
|
|
request = (rpc_message_t *)rqbuf;
|
|
if (request->length == (uint64_t)readbytes)
|
|
break;
|
|
}
|
|
}
|
|
if (rc == -1) {
|
|
debug(D_ERROR, "Unix socket read error");
|
|
goto cleanup;
|
|
} else if (rc == 0 && readbytes == 0) {
|
|
debug(D_NOTICE, "Connection closed by client before sending data");
|
|
goto cleanup;
|
|
}
|
|
|
|
request = (rpc_message_t *)rqbuf;
|
|
response = (rpc_message_t *)malloc(POVERLAY_BUFSIZE);
|
|
if (request) {
|
|
respond(request, response);
|
|
|
|
ssize_t total_size = sizeof(uint32_t) + sizeof(uint64_t) + response->length;
|
|
ssize_t bytes_written = write(*sockfd, response, total_size);
|
|
|
|
if (bytes_written == -1) {
|
|
debug(D_ERROR, "Failed to write to socket: %s", strerror(errno));
|
|
return;
|
|
} else if (bytes_written < total_size) {
|
|
debug(D_ERROR, "Incomplete write to socket: wrote %zd of %zd bytes", bytes_written, total_size);
|
|
return;
|
|
}
|
|
|
|
debug(D_NOTICE, "Successfully wrote %zd bytes to socket", bytes_written);
|
|
} else {
|
|
debug(D_ERROR, "No valid request received");
|
|
}
|
|
|
|
cleanup:
|
|
if (sockfd) {
|
|
close(*sockfd);
|
|
}
|
|
if (response) {
|
|
psync_free(response);
|
|
}
|
|
|
|
debug(D_NOTICE, "InstanceThread exiting.");
|
|
}
|
|
|
|
static void respond_status(rpc_message_t *request, rpc_message_t *response, size_t available_space) {
|
|
psync_path_status_t stat;
|
|
|
|
stat = PSYNC_PATH_STATUS_NOT_OURS;
|
|
|
|
if (overlays_running) {
|
|
stat = ppathstatus_get(request->value);
|
|
}
|
|
switch (ppathstatus_get_status(stat)) {
|
|
case PSYNC_PATH_STATUS_IN_SYNC:
|
|
response->type = 10;
|
|
break;
|
|
case PSYNC_PATH_STATUS_IN_PROG:
|
|
response->type = 12;
|
|
break;
|
|
case PSYNC_PATH_STATUS_PAUSED:
|
|
case PSYNC_PATH_STATUS_REMOTE_FULL:
|
|
case PSYNC_PATH_STATUS_LOCAL_FULL:
|
|
response->type = 11;
|
|
break;
|
|
default:
|
|
response->type = 13;
|
|
snprintf(response->value, available_space, "No.");
|
|
}
|
|
}
|
|
|
|
static void respond_api(rpc_message_t *request, rpc_message_t *response, size_t available_space) {
|
|
int cbidx; // callback index (based on message type)
|
|
int cbret; // callback return value
|
|
|
|
cbidx = request->type - 20;
|
|
cbret = 0;
|
|
|
|
if (!handlers_running || (request->type >= ((uint32_t)calbacks_lower_band +
|
|
(uint32_t)handlers_size))) {
|
|
response->type = 13;
|
|
snprintf(response->value, available_space, "Invalid type.");
|
|
debug(D_NOTICE, "Invalid request type: %u", request->type);
|
|
return;
|
|
}
|
|
|
|
if (!handlers[cbidx]) {
|
|
response->type = 13;
|
|
snprintf(response->value, available_space,
|
|
"No callback with this id registered.");
|
|
debug(D_NOTICE, "No callback registered for type: %u", request->type);
|
|
return;
|
|
}
|
|
|
|
cbret = handlers[cbidx](request->value);
|
|
if (cbret == 0) {
|
|
response->type = 0;
|
|
} else {
|
|
response->type = cbret;
|
|
snprintf(response->value, available_space,
|
|
"Callback returned error code.");
|
|
debug(D_NOTICE, "Callback failed with return code: %d", cbret);
|
|
}
|
|
}
|
|
|
|
static void respond(rpc_message_t *request, rpc_message_t *response) {
|
|
const char *dbgmsg; // debug messages
|
|
size_t value_avail; // space available to store value (flexible array)
|
|
|
|
dbgmsg = NULL;
|
|
memset(response, 0, POVERLAY_BUFSIZE);
|
|
response->length = 0;
|
|
value_avail = POVERLAY_BUFSIZE - sizeof(rpc_message_t);
|
|
|
|
// never print the crypto password to the logs in plain text
|
|
dbgmsg = (request->type == 20) ? "REDACTED" : request->value;
|
|
debug(D_NOTICE, "Client Request type [%u] len [%lu] string: [%s]", request->type, request->length, dbgmsg);
|
|
if (request->type < 20) {
|
|
respond_status(request, response, value_avail);
|
|
} else {
|
|
respond_api(request, response, value_avail);
|
|
}
|
|
|
|
// a message with a type != 13 and a null string value after
|
|
// processing is considered successful. set value to "Ok."
|
|
if (response->type != 13 && response->value[0] == '\0') {
|
|
snprintf(response->value, value_avail, "Ok.");
|
|
} else {
|
|
debug(D_WARNING, "not updating value to Ok: response->msg->type=%d, " "response->msg->value=%s", response->type, response->value);
|
|
}
|
|
|
|
// truncate messages that exceed the buffer boundaries
|
|
size_t value_length = strnlen(response->value, value_avail);
|
|
response->length = sizeof(rpc_message_t) + value_length + 1;
|
|
if (response->length > POVERLAY_BUFSIZE) {
|
|
response->length = POVERLAY_BUFSIZE;
|
|
response->value[value_avail - 1] = '\0';
|
|
debug(D_WARNING, "Response message truncated to fit buffer");
|
|
}
|
|
}
|
|
|
|
void prpc_main_loop() {
|
|
struct sockaddr_un addr;
|
|
int fd, cl;
|
|
|
|
char *sockpath = prpc_sockpath();
|
|
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
|
|
debug(D_ERROR, "Unix socket error failed to open %s", sockpath);
|
|
return;
|
|
}
|
|
|
|
if (fchmod(fd, 0600) == -1) {
|
|
debug(D_ERROR, "Failed to set socket permissions");
|
|
return;
|
|
}
|
|
|
|
memset(&addr, 0, sizeof(addr));
|
|
addr.sun_family = AF_UNIX;
|
|
strncpy(addr.sun_path, sockpath, sizeof(addr.sun_path) - 1);
|
|
|
|
unlink(sockpath);
|
|
|
|
if (bind(fd, (struct sockaddr *)&addr, strlen(sockpath) + sizeof(addr.sun_family)) == -1) {
|
|
debug(D_ERROR, "Unix socket bind error");
|
|
return;
|
|
}
|
|
|
|
free(sockpath);
|
|
|
|
if (listen(fd, 5) == -1) {
|
|
debug(D_ERROR, "Unix socket listen error");
|
|
return;
|
|
}
|
|
|
|
while (1) {
|
|
if ((cl = accept(fd, NULL, NULL)) == -1) {
|
|
debug(D_ERROR, "Unix socket accept error");
|
|
continue;
|
|
}
|
|
|
|
// handle the request in a new thread
|
|
prun_thread1("Pipe request handle routine",
|
|
on_request, // thread proc
|
|
(void *)&cl // thread parameter
|
|
);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
int prpc_register(int cmdid, prpc_handler h) {
|
|
prpc_handler *handlers_old = handlers;
|
|
int handlers_size_old = handlers_size;
|
|
if (cmdid < calbacks_lower_band)
|
|
return -1;
|
|
if (cmdid > (calbacks_lower_band + handlers_size)) {
|
|
handlers_size = cmdid - calbacks_lower_band + 1;
|
|
prpc_init();
|
|
memcpy(handlers, handlers_old,
|
|
handlers_size_old * sizeof(prpc_handler));
|
|
psync_free(handlers_old);
|
|
}
|
|
handlers[cmdid - calbacks_lower_band] = h;
|
|
return 0;
|
|
}
|
|
|
|
void prpc_init() {
|
|
handlers = (prpc_handler *)psync_malloc(sizeof(prpc_handler) * handlers_size);
|
|
memset(handlers, 0, sizeof(prpc_handler) * handlers_size);
|
|
}
|
|
|
|
char *prpc_sockpath() {
|
|
char *home = ppath_home();
|
|
if (!home) {
|
|
return NULL;
|
|
}
|
|
|
|
const char *subdir = "/.pcloud/prpc.sock";
|
|
size_t len = strlen(home) + strlen(subdir) + 1;
|
|
char *sockpath = (char *)malloc(len);
|
|
if (!sockpath) {
|
|
return NULL;
|
|
}
|
|
|
|
snprintf(sockpath, len, "%s%s", home, subdir);
|
|
return sockpath;
|
|
}
|