cleanup control_tools API, inline command functions

This commit is contained in:
Levi Neely 2025-03-09 18:14:21 +01:00
parent f437fe840e
commit dbf3e11473
4 changed files with 193 additions and 303 deletions

View File

@ -39,8 +39,6 @@
#include <readline/readline.h>
#include <readline/history.h>
#include "control_tools.h"
#include "pclsync_lib.h"
#include "pclsync/pshm.h"
#include "pclsync/pfoldersync.h"
@ -53,196 +51,6 @@ namespace cc = console_client;
namespace control_tools {
static const int STOP = 0;
int list_sync_folders() {
int ret;
char *errm;
size_t errmsz;
int result;
psync_folder_list_t *flist;
psync_folder_t *folder;
int rval;
errm = NULL;
errmsz = 0;
rval = 0;
result = rpc_call(LISTSYNC, "", &ret, &errm, &errmsz);
if (result != 0) {
std::cout << "List Sync Folders failed. return is " << ret
<< " and message is " << (errm ? errm : "no message")
<< std::endl;
rval = result;
}
if(pshm_read((void**)&flist, NULL)) {
const int id_width = 12;
const int path_width = 30;
if(flist->foldercnt > 0) {
std::cout << std::left << std::setw(id_width) << "Folder ID"
<< std::setw(path_width) << "Local Path"
<< std::setw(path_width) << "Remote Path" << std::endl;
std::cout << std::string(id_width, '-')
<< std::string(path_width - 1, '-')
<< std::string(path_width - 1, '-') << std::endl;
for (uint32_t i = 0; i < flist->foldercnt; i++) {
folder = &flist->folders[i];
std::cout << std::left << std::setw(id_width) << folder->folderid
<< std::setw(path_width) << folder->localpath
<< std::setw(path_width) << folder->remotepath << std::endl;
}
} else {
std::cout << "No synchronized folders found." << std::endl;
rval = ret;
}
rval = ret;
free(flist);
} else {
std::cout << "failed to read folder list from shm" << std::endl;
return -1;
}
if(errm) {
free(errm);
}
return rval;
}
int start_crypto(const char *pass) {
int ret;
char *errm;
size_t errm_size;
int result = rpc_call(STARTCRYPTO, pass, &ret, &errm, &errm_size);
if (result != 0 || ret != 0) {
std::cout << "Start Crypto failed. return is " << ret << " and message is "
<< (errm ? errm : "no message") << std::endl;
} else {
std::cout << "Crypto started. " << std::endl;
}
if (errm)
free(errm);
return ret;
}
int stop_crypto() {
int ret;
char *errm;
size_t errm_size;
int result = rpc_call(STOPCRYPTO, "", &ret, &errm, &errm_size);
if (result != 0) {
std::cout << "Stop Crypto failed. return is " << ret << " and message is "
<< (errm ? errm : "no message") << std::endl;
} else {
std::cout << "Crypto Stopped. " << std::endl;
}
if (errm)
free(errm);
return ret;
}
int remove_sync_folder(const char *folderid) {
int ret;
char *errm;
size_t errmsz;
int result;
int rval;
errm = NULL;
errmsz = 0;
rval = 0;
result = rpc_call(STOPSYNC, folderid, &ret, &errm, &errmsz);
if (result != 0) {
std::cout << "Remove Sync Folder failed with unknown error. return is "
<< ret << " and message is " << (errm ? errm : "no message")
<< std::endl;
rval = result;
} else {
std::cout << "Successfully removed sync folder with folderid " << folderid
<< std::endl;
}
if(errm) {
free(errm);
}
return rval;
}
// TODO: should add support for specifying sync type. Need a better
// CLI processing solution first.
int add_sync_folder(std::string localpath, std::string remotepath) {
int ret;
char *errm;
size_t errmsz;
int result;
int rval;
errm = NULL;
errmsz = 0;
rval = 0;
std::string combinedPaths = localpath + '|' + remotepath;
result = rpc_call(ADDSYNC, combinedPaths.c_str(), &ret, &errm, &errmsz);
if (result != 0) {
if (result == -1) {
std::cout << "Add Sync Folders failed: remote folder " << remotepath
<< " not found." << std::endl;
} else {
std::cout << "Add Sync Folders failed with unknown error. return is "
<< ret << " and message is " << (errm ? errm : "no message")
<< std::endl;
}
rval = result;
} else {
rval = ret;
}
if(errm) {
free(errm);
}
return rval;
}
int finalize() {
int ret;
char *errm;
size_t errm_size;
rpc_call(FINALIZE, "", &ret, &errm, &errm_size);
std::cout << "Exiting ..." << std::endl;
if (errm){
free(errm);
}
return ret;
}
static void help() {
std::cout << "Supported commands are:" << std::endl
<< " help(?): Show this help message" << std::endl
<< " crypto(c):" << std::endl
<< " start <crypto pass>: Unlock crypto folder" << std::endl
<< " stop: Lock crypto folder" << std::endl
<< " sync(s):" << std::endl
<< " list(ls): List sync folders" << std::endl
<< " add <localpath> <remotepath>: Add sync folder" << std::endl
<< " remove(rm) <folderid>: Remove sync folder" << std::endl
<< " finalize(f): Kill daemon and quit" << std::endl
<< " quit(q): Exit this program" << std::endl;
}
static char* command_generator(const char* text, int state) {
static int list_index, len;
static const char* commands[] = {
@ -255,30 +63,25 @@ static char* command_generator(const char* text, int state) {
"quit", "q",
nullptr
};
// If this is a new word to complete, initialize
if (!state) {
list_index = 0;
len = strlen(text);
}
// Return the next name which partially matches
while (const char* command = commands[list_index++]) {
if (strncmp(command, text, len) == 0) {
return strdup(command); // Caller frees this with free()
}
}
return nullptr; // No more matches
return nullptr;
}
static char** command_completion(const char* text, int start, int end) {
// If this is the start of line, complete commands
if (start == 0) {
return rl_completion_matches(text, command_generator);
}
// Otherwise, don't complete
return nullptr;
}
@ -288,53 +91,160 @@ void process_commands() {
app.footer("Type 'help' or '?' for a list of supported commands.");
// top-level commands
app.add_subcommand("help", "Show help")->alias("?")->callback(help);
auto crypto_cmd =
app.add_subcommand("crypto", "Crypto-related commands")->alias("c");
app.add_subcommand("help", "Show help")->alias("?")->callback([] {
std::cout << "Supported commands are:" << std::endl
<< " help(?): Show this help message" << std::endl
<< " crypto(c):" << std::endl
<< " start <crypto pass>: Unlock crypto folder" << std::endl
<< " stop: Lock crypto folder" << std::endl
<< " sync(s):" << std::endl
<< " list(ls): List sync folders" << std::endl
<< " add <localpath> <remotepath>: Add sync folder" << std::endl
<< " remove(rm) <folderid>: Remove sync folder" << std::endl
<< " finalize(f): Kill daemon and quit" << std::endl
<< " quit(q): Exit this program" << std::endl;
});
auto crypto_cmd = app.add_subcommand("crypto", "Crypto-related commands")->alias("c");
crypto_cmd->require_subcommand();
auto sync_cmd =
app.add_subcommand("sync", "Sync-related commands")->alias("s");
auto sync_cmd = app.add_subcommand("sync", "Sync-related commands")->alias("s");
sync_cmd->require_subcommand();
// finalize command
app.add_subcommand("finalize", "Finalize and exit")->alias("f")->callback([] {
finalize();
char *errm = NULL;
size_t errm_size = 0;
rpc_call(FINALIZE, "", &errm, &errm_size);
std::cout << "Exiting ..." << std::endl;
if (errm) { free(errm); }
exit(0);
});
app.add_subcommand("quit", "Quit the program")->alias("q")->callback([] {
exit(0);
});
// crypto subcommands
// crypto start
auto start_crypto_cmd = crypto_cmd->add_subcommand("start", "Start crypto");
std::string start_crypto_pwd;
start_crypto_cmd->add_option("password", start_crypto_pwd, "Crypto password")
->required();
start_crypto_cmd->callback([&] { start_crypto(start_crypto_pwd.c_str()); });
crypto_cmd->add_subcommand("stop", "Stop crypto")->callback(stop_crypto);
// sync subcommands
sync_cmd->add_subcommand("list", "List sync folders")
->alias("ls")
->callback(list_sync_folders);
auto sync_add_cmd = sync_cmd->add_subcommand("add", "Add sync folder");
std::string syncadd_lpath, syncadd_rpath;
sync_add_cmd->add_option("localpath", syncadd_lpath, "Local Path")->required();
sync_add_cmd->add_option("remotepath", syncadd_rpath, "Remote Path")->required();
sync_add_cmd->callback([&] {
add_sync_folder(syncadd_lpath, syncadd_rpath);
start_crypto_cmd->add_option("password", start_crypto_pwd, "Crypto password")->required();
start_crypto_cmd->callback([&] {
char *errm = NULL;
size_t errm_size = 0;
if(int result = rpc_call(STARTCRYPTO, start_crypto_pwd.c_str(), &errm, &errm_size) != 0) {
std::cout << "Start Crypto failed: " << (errm ? errm : "no message") << std::endl;
if (errm) { free(errm); }
return result;
}
std::cout << "Crypto started." << std::endl;
if (errm) { free(errm); }
return 0;
});
auto sync_remove_cmd =
sync_cmd->add_subcommand("remove", "Remove sync folder (use ls to get folder ID)")->alias("rm");
// crypto stop
crypto_cmd->add_subcommand("stop", "Stop crypto")->callback([] {
char *errm = NULL;
size_t errm_size = 0;
if(int result = rpc_call(STOPCRYPTO, "", &errm, &errm_size) == 0) {
std::cout << "Stop Crypto failed: "<< (errm ? errm : "no message") << std::endl;
if (errm) { free(errm); }
return result;
}
std::cout << "Crypto Stopped." << std::endl;
if (errm) { free(errm); }
return 0;
});
// sync list
sync_cmd->add_subcommand("list", "List sync folders")->alias("ls")->callback([] {
char *errm = NULL;
size_t errmsz = 0;
if(int result = rpc_call(LISTSYNC, "", &errm, &errmsz) != 0) {
std::cout << "List Sync Folders failed: " << (errm ? errm : "no message") << std::endl;
if (errm) { free(errm); }
return result;
}
psync_folder_list_t *flist = NULL;
if(pshm_read((void**)&flist, NULL)) {
const int id_width = 12;
const int path_width = 30;
if(flist->foldercnt > 0) {
std::cout << std::left << std::setw(id_width) << "Folder ID"
<< std::setw(path_width) << "Local Path"
<< std::setw(path_width) << "Remote Path" << std::endl;
std::cout << std::string(id_width, '-')
<< std::string(path_width - 1, '-')
<< std::string(path_width - 1, '-') << std::endl;
for (uint32_t i = 0; i < flist->foldercnt; i++) {
psync_folder_t *folder = &flist->folders[i];
std::cout << std::left << std::setw(id_width) << folder->folderid
<< std::setw(path_width) << folder->localpath
<< std::setw(path_width) << folder->remotepath << std::endl;
}
} else {
std::cout << "No synchronized folders found." << std::endl;
}
free(flist);
} else {
std::cout << "failed to read folder list from shm" << std::endl;
return -1;
}
if(errm) { free(errm); }
return 0;
});
// sync add
auto sync_add_cmd = sync_cmd->add_subcommand("add", "Add sync folder");
std::string localpath, remotepath;
sync_add_cmd->add_option("localpath", localpath, "Local Path")->required();
sync_add_cmd->add_option("remotepath", remotepath, "Remote Path")->required();
sync_add_cmd->callback([&] {
char *errm = NULL;
size_t errmsz = 0;
std::string combinedPaths = localpath + '|' + remotepath;
if(int result = rpc_call(ADDSYNC, combinedPaths.c_str(), &errm, &errmsz) != 0) {
if (result == -1) {
std::cout << "Add Sync Folders failed: remote folder " << remotepath << " not found." << std::endl;
} else {
std::cout << "Add Sync Folders failed:" << (errm ? errm : "no message") << std::endl;
}
if (errm) { free(errm); }
return result;
}
if(errm) { free(errm); }
return 0;
});
// sync remove
auto sync_remove_cmd = sync_cmd->add_subcommand("remove", "Remove sync folder (use ls to get folder ID)")->alias("rm");
std::string syncrm_fid;
sync_remove_cmd->add_option("folderid", syncrm_fid, "Folder ID")->required();
sync_remove_cmd->callback([&] { remove_sync_folder(syncrm_fid.c_str()); });
sync_remove_cmd->callback([&] {
char *errm = NULL;
size_t errmsz = 0;
const char *folderid = syncrm_fid.c_str();
if(int result = rpc_call(STOPSYNC, folderid, &errm, &errmsz) != 0) {
std::cout << "Remove Sync Folder failed: " << (errm ? errm : "no message") << std::endl;
if (errm) { free(errm); }
return result;
}
std::cout << "Successfully removed sync folder with folderid " << folderid << std::endl;
if(errm) { free(errm); }
return 0;
});
// enable command history and auto-completion
using_history();
rl_attempted_completion_function = command_completion;
// command loop
rl_attempted_completion_function = command_completion;
while (true) {
char* line_read = readline("pcloud> ");
if (!line_read) break;
@ -350,10 +260,14 @@ void process_commands() {
std::vector<std::string> args;
std::istringstream iss(line);
std::string arg;
std::ostringstream invs;
while (iss >> arg) {
args.push_back(arg);
}
invs << "Invalid command: '" << "line"
<< "'. Type 'help' or '?' to get a list of valid commands.";
if (!args.empty()) {
try {
auto *subcom = app.get_subcommand(args[0]);
@ -361,20 +275,13 @@ void process_commands() {
std::cout << "Usage for '" << args[0] << "':" << std::endl;
std::cout << subcom->help() << std::endl;
} else {
std::cout
<< "Invalid command: '" << line
<< "'. Type 'help' or '?' to get a list of valid commands."
<< std::endl;
std::cout << invs.str() << std::endl;
}
} catch (...) {
std::cout << "Invalid command: '" << line
<< "'. Type 'help' or '?' to get a list of valid commands."
<< std::endl;
std::cout << invs.str() << std::endl;
}
} else {
std::cout << "Invalid command: '" << line
<< "'. Type 'help' or '?' to get a list of valid commands."
<< std::endl;
std::cout << invs.str() << std::endl;
}
}
}

View File

@ -31,11 +31,8 @@
namespace control_tools {
int start_crypto(const char *pass);
int stop_crypto();
int finalize();
int daemonize(bool do_commands);
void process_commands();
int daemonize(bool do_commands);
} // namespace control_tools

View File

@ -77,8 +77,7 @@
// 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...)
static int socket_connect(const char *sockpath, char **out, size_t *out_size,
int *ret) {
static int socket_connect(const char *sockpath, char **out, size_t *out_size) {
int fd;
struct sockaddr_un addr;
const char *error_msg;
@ -87,7 +86,6 @@ static int socket_connect(const char *sockpath, char **out, size_t *out_size,
error_msg = "Unable to create unix socket";
*out = strdup(error_msg);
*out_size = strlen(error_msg) + 1;
*ret = POVERLAY_SOCKET_CREATE_FAILED;
return POVERLAY_SOCKET_CREATE_FAILED;
}
@ -98,43 +96,36 @@ static int socket_connect(const char *sockpath, char **out, size_t *out_size,
error_msg = "Unable to connect to UNIX socket";
*out = strdup(error_msg);
*out_size = strlen(error_msg) + 1;
*ret = POVERLAY_SOCKET_CONNECT_FAILED;
return POVERLAY_SOCKET_CONNECT_FAILED;
}
return fd;
}
static int write_request(int fd, int msgtype, const char *value, char **out,
size_t *out_size, int *ret) {
static int write_request(int fd, int msgtype, const char *value, char **out, size_t *out_size) {
uint64_t bytes_written;
int rc;
int len;
int size;
char *buf;
const char *err;
rpc_message_t *request;
char *curbuf;
int len = strlen(value);
int size = sizeof(rpc_message_t) + len + 1;
char *buf = (char *)malloc(size);
*ret = 0; // Initialize ret to 0
len = strlen(value);
size = sizeof(rpc_message_t) + len + 1;
buf = (char *)malloc(size);
request = (rpc_message_t *)buf;
// 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;
curbuf = (char *)request;
while (bytes_written < request->length && *ret == 0) {
rc = write(fd, curbuf, (request->length - bytes_written));
// 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) {
err = "failed to write to socket.";
const char *err = "failed to write to socket.";
*out = strdup(err);
*ret = POVERLAY_WRITE_SOCK_ERR;
*out_size = strlen(err) + 1;
writeerr = POVERLAY_WRITE_SOCK_ERR;
}
} else {
bytes_written += rc;
@ -142,91 +133,86 @@ static int write_request(int fd, int msgtype, const char *value, char **out,
}
}
if (*ret == 0 && bytes_written != request->length) {
err = "Communication error";
// 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;
*ret = POVERLAY_WRITE_COMM_ERR;
writeerr = POVERLAY_WRITE_COMM_ERR;
}
free(buf);
return *ret;
return writeerr;
}
static int read_response(int fd, char **out, size_t *out_size, int *ret) {
rpc_message_t *msg;
ssize_t bytes_read;
static int read_response(int fd, char **out, size_t *out_size) {
msg = (rpc_message_t *)malloc(POVERLAY_BUFSIZE);
rpc_message_t *msg = (rpc_message_t *)malloc(POVERLAY_BUFSIZE);
if (msg == NULL) {
const char *error_msg = "Memory allocation failed";
*out = strdup(error_msg);
*out_size = strlen(error_msg) + 1;
*ret = -1;
return -1;
}
bytes_read = read(fd, msg, POVERLAY_BUFSIZE);
ssize_t bytes_read = read(fd, msg, POVERLAY_BUFSIZE);
if (bytes_read <= 0) {
const char *error_msg = (bytes_read == 0) ? "Connection closed" : "Read error";
free(msg);
*out = strdup(error_msg);
*out_size = strlen(error_msg) + 1;
*ret = -1;
free(msg);
return -1;
}
*out = (char *)malloc(msg->length);
memcpy(*out, msg->value, msg->length);
*out_size = msg->length;
*ret = msg->type;
free(msg);
return 0;
}
int rpc_get_state(pCloud_FileState *state, char *path) {
char *errm = NULL;
size_t errm_size = 0;
int rep = 0;
char *errm;
size_t errm_size;
if (!rpc_call(4, path /*IN*/, &rep, &errm, &errm_size)) {
if ((rep = rpc_call(4, path, &errm, &errm_size) == 0)) {
debug(D_NOTICE, "rpc_get_state responese rep[%d] path[%s]", rep, path);
if (errm)
if (errm) {
debug(D_NOTICE, "The error is %s", errm);
if (rep == 10)
}
if (rep == 10) {
*state = FileStateInSync;
else if (rep == 12)
} else if (rep == 12) {
*state = FileStateInProgress;
else if (rep == 11)
} else if (rep == 11) {
*state = FileStateNoSync;
else
} else {
*state = FileStateInvalid;
} else
}
} else {
debug(D_ERROR, "rpc_get_state ERROR rep[%d] path[%s]", rep, path);
free(errm);
}
if(errm) {
free(errm);
}
return 0;
}
// path contains the input argument(s).
int rpc_call(int id, const char *path, int *ret, char **errm, size_t *errmsz) {
int result;
int sockfd;
int rpc_call(int id, const char *path, char **errm, size_t *errmsz) {
int result = 0;
int sockfd = -1;
sockfd = -1;
result = 0;
*errm = NULL;
*errmsz = 0;
*ret = 0;
sockfd = socket_connect(PRPC_SOCK_PATH, errm, errmsz, ret);
sockfd = socket_connect(PRPC_SOCK_PATH, errm, errmsz);
if (sockfd >= 0) {
if ((result = write_request(sockfd, id, path, errm, errmsz, ret)) == 0) {
result = read_response(sockfd, errm, errmsz, ret);
if ((result = write_request(sockfd, id, path, errm, errmsz)) == 0) {
result = read_response(sockfd, errm, errmsz);
}
close(sockfd);
} else {
result = -1;
}
return *ret; // always 0 on success
return result; // always 0 on success
}

View File

@ -49,7 +49,7 @@ typedef enum _pCloud_FileState {
} pCloud_FileState;
int rpc_get_state(pCloud_FileState *, char *);
int rpc_call(int id, const char *, int *, char **, size_t *);
int rpc_call(int id, const char *path, char **errm, size_t *errmsz);
#ifdef __cplusplus
}