non interactive command processing (#123)

* support non-interactive command processing
- app is now configured in setup_app function
- added process_command() to api
- support piped input and -c argument
- fix memleaks
This commit is contained in:
Levi Neely 2025-03-09 19:18:38 +01:00 committed by GitHub
parent f437fe840e
commit 27977765e8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 269 additions and 355 deletions

View File

@ -39,8 +39,6 @@
#include <readline/readline.h> #include <readline/readline.h>
#include <readline/history.h> #include <readline/history.h>
#include "control_tools.h"
#include "pclsync_lib.h" #include "pclsync_lib.h"
#include "pclsync/pshm.h" #include "pclsync/pshm.h"
#include "pclsync/pfoldersync.h" #include "pclsync/pfoldersync.h"
@ -53,196 +51,6 @@ namespace cc = console_client;
namespace control_tools { 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 char* command_generator(const char* text, int state) {
static int list_index, len; static int list_index, len;
static const char* commands[] = { static const char* commands[] = {
@ -256,85 +64,204 @@ static char* command_generator(const char* text, int state) {
nullptr nullptr
}; };
// If this is a new word to complete, initialize
if (!state) { if (!state) {
list_index = 0; list_index = 0;
len = strlen(text); len = strlen(text);
} }
// Return the next name which partially matches
while (const char* command = commands[list_index++]) { while (const char* command = commands[list_index++]) {
if (strncmp(command, text, len) == 0) { if (strncmp(command, text, len) == 0) {
return strdup(command); // Caller frees this with free() return strdup(command); // Caller frees this with free()
} }
} }
return nullptr; // No more matches
}
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; return nullptr;
} }
void process_commands() { static char** command_completion(const char* text, int start, int end) {
CLI::App app{"pcloudcc-lneely"}; if (start == 0) {
app.fallthrough(); return rl_completion_matches(text, command_generator);
app.footer("Type 'help' or '?' for a list of supported commands."); }
return nullptr;
}
void setup_app(CLI::App *app) {
app->fallthrough();
app->footer("Type 'help' or '?' for a list of supported commands.");
// top-level commands // top-level commands
app.add_subcommand("help", "Show help")->alias("?")->callback(help); app->add_subcommand("help", "Show help")->alias("?")->callback([] {
auto crypto_cmd = std::cout << "Supported commands are:" << std::endl
app.add_subcommand("crypto", "Crypto-related commands")->alias("c"); << " 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(); crypto_cmd->require_subcommand();
auto sync_cmd = auto sync_cmd = app->add_subcommand("sync", "Sync-related commands")->alias("s");
app.add_subcommand("sync", "Sync-related commands")->alias("s");
sync_cmd->require_subcommand(); sync_cmd->require_subcommand();
app.add_subcommand("finalize", "Finalize and exit")->alias("f")->callback([] {
finalize(); // finalize command
exit(0); app->add_subcommand("finalize", "Finalize and exit")->alias("f")->callback([] {
}); char *errm = NULL;
app.add_subcommand("quit", "Quit the program")->alias("q")->callback([] { size_t errm_size = 0;
rpc_call(FINALIZE, "", &errm, &errm_size);
std::cout << "Exiting ..." << std::endl;
if (errm) { free(errm); }
exit(0); exit(0);
}); });
// crypto subcommands app->add_subcommand("quit", "Quit the program")->alias("q")->callback([] {
exit(0);
});
// crypto start
auto start_crypto_cmd = crypto_cmd->add_subcommand("start", "Start crypto"); auto start_crypto_cmd = crypto_cmd->add_subcommand("start", "Start crypto");
std::string start_crypto_pwd; static std::string start_crypto_pwd;
start_crypto_cmd->add_option("password", start_crypto_pwd, "Crypto password") start_crypto_cmd->add_option("password", start_crypto_pwd, "Crypto password")->required();
->required(); start_crypto_cmd->callback([] {
start_crypto_cmd->callback([&] { start_crypto(start_crypto_pwd.c_str()); }); char *errm = NULL;
size_t errm_size = 0;
crypto_cmd->add_subcommand("stop", "Stop crypto")->callback(stop_crypto); 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;
// sync subcommands if (errm) { free(errm); }
sync_cmd->add_subcommand("list", "List sync folders") return result;
->alias("ls") }
->callback(list_sync_folders); std::cout << "Crypto started." << std::endl;
if (errm) { free(errm); }
auto sync_add_cmd = sync_cmd->add_subcommand("add", "Add sync folder"); return 0;
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);
}); });
auto sync_remove_cmd = // crypto stop
sync_cmd->add_subcommand("remove", "Remove sync folder (use ls to get folder ID)")->alias("rm"); crypto_cmd->add_subcommand("stop", "Stop crypto")->callback([] {
std::string syncrm_fid; char *errm = NULL;
sync_remove_cmd->add_option("folderid", syncrm_fid, "Folder ID")->required(); size_t errm_size = 0;
sync_remove_cmd->callback([&] { remove_sync_folder(syncrm_fid.c_str()); }); 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;
}
if(flist) { free(flist); }
} else {
std::cout << "failed to read folder list from shm" << std::endl;
if(flist) { free(flist); }
return -1;
}
if(errm) { free(errm); }
return 0;
});
// sync add
auto sync_add_cmd = sync_cmd->add_subcommand("add", "Add sync folder");
static 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");
static std::string syncrm_fid;
sync_remove_cmd->add_option("folderid", syncrm_fid, "Folder ID")->required();
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;
});
}
int process_command(const std::string &command) {
CLI::App app = CLI::App{"pcloudcc-lneely"};
setup_app(&app);
try {
app.parse(command);
return 0;
} catch (const CLI::ParseError &e) {
std::cerr << "Invalid command: '" << command << "'" << std::endl;
return 1;
}
}
void process_commands() {
CLI::App app = CLI::App{"pcloudcc-lneely"};
setup_app(&app);
// enable command history and auto-completion
using_history(); using_history();
rl_attempted_completion_function = command_completion;
// command loop // command loop
rl_attempted_completion_function = command_completion;
while (true) { while (true) {
char* line_read = readline("pcloud> "); char* line_read = readline("pcloud> ");
if (!line_read) break; if (!line_read) break;
@ -350,10 +277,14 @@ void process_commands() {
std::vector<std::string> args; std::vector<std::string> args;
std::istringstream iss(line); std::istringstream iss(line);
std::string arg; std::string arg;
std::ostringstream invs;
while (iss >> arg) { while (iss >> arg) {
args.push_back(arg); args.push_back(arg);
} }
invs << "Invalid command: '" << line
<< "'. Type 'help' or '?' to get a list of valid commands.";
if (!args.empty()) { if (!args.empty()) {
try { try {
auto *subcom = app.get_subcommand(args[0]); auto *subcom = app.get_subcommand(args[0]);
@ -361,20 +292,13 @@ void process_commands() {
std::cout << "Usage for '" << args[0] << "':" << std::endl; std::cout << "Usage for '" << args[0] << "':" << std::endl;
std::cout << subcom->help() << std::endl; std::cout << subcom->help() << std::endl;
} else { } else {
std::cout std::cout << invs.str() << std::endl;
<< "Invalid command: '" << line
<< "'. Type 'help' or '?' to get a list of valid commands."
<< std::endl;
} }
} catch (...) { } catch (...) {
std::cout << "Invalid command: '" << line std::cout << invs.str() << std::endl;
<< "'. Type 'help' or '?' to get a list of valid commands."
<< std::endl;
} }
} else { } else {
std::cout << "Invalid command: '" << line std::cout << invs.str() << std::endl;
<< "'. Type 'help' or '?' to get a list of valid commands."
<< std::endl;
} }
} }
} }

View File

@ -29,13 +29,13 @@
#ifndef CONTROL_TOOLS_H #ifndef CONTROL_TOOLS_H
#define CONTROL_TOOLS_H #define CONTROL_TOOLS_H
#include <string>
namespace control_tools { namespace control_tools {
int start_crypto(const char *pass);
int stop_crypto();
int finalize();
int daemonize(bool do_commands);
void process_commands(); void process_commands();
int daemonize(bool do_commands);
int process_command(const std::string &command);
} // namespace control_tools } // namespace control_tools

View File

@ -58,31 +58,21 @@ int main(int argc, char **argv) {
try { try {
po::options_description desc("Allowed options"); po::options_description desc("Allowed options");
desc.add_options()("help,h", "Show this help message.")( desc.add_options()
"username,u", po::value<std::string>(&username), ("help,h", "Show this help message.")
"pCloud account name.")( ("username,u", po::value<std::string>(&username), "pCloud account name.")
"password,p", po::bool_switch(&passwordsw), ("password,p", po::bool_switch(&passwordsw), "Ask for pCloud account password.")
"Ask for pCloud account password.")( ("tfa_code,t", po::value<std::string>(&tfa_code), "pCloud tfa code")
"tfa_code,t", po::value<std::string>(&tfa_code), ("trusted_device,r", po::bool_switch(&trusted_device), "Trust this device.")
"pCloud tfa code")( ("crypto,c", po::bool_switch(&crypto), "Ask for crypto password.")
"trusted_device,r", po::bool_switch(&trusted_device), ("passascrypto,y", po::value<std::string>(), "User password is the same as crypto password.")
"Trust this device.")( ("daemonize,d", po::bool_switch(&daemon), "Run the process as a background daemon.")
"crypto,c", po::bool_switch(&crypto), ("commands ,o", po::bool_switch(&commands), "Keep parent process alive and process commands. ")
"Ask for crypto password.")( ("mountpoint,m", po::value<std::string>(), "Specify where pCloud filesystem is mounted.")
"passascrypto,y", po::value<std::string>(), ("commands_only,k", po::bool_switch(&commands_only), "Open command prompt to interact with running daemon.")
"User password is the same as crypto password.")( ("command,c", po::value<std::string>(), "Execute a single command and exit.")
"daemonize,d", po::bool_switch(&daemon), ("newuser,n", po::bool_switch(&newuser), "Register a new pCloud user account.")
"Run the process as a background daemon.")( ("savepassword,s", po::bool_switch(&save_pass), "Save user password in the database.");
"commands ,o", po::bool_switch(&commands),
"Keep parent process alive and process commands. ")(
"mountpoint,m", po::value<std::string>(),
"Specify where pCloud filesystem is mounted.")(
"commands_only,k", po::bool_switch(&commands_only),
"Open command prompt to interact with running daemon.")(
"newuser,n", po::bool_switch(&newuser),
"Register a new pCloud user account.")(
"savepassword,s", po::bool_switch(&save_pass),
"Save user password in the database.");
po::command_line_parser parser{argc, argv}; po::command_line_parser parser{argc, argv};
po::positional_options_description p; po::positional_options_description p;
@ -102,6 +92,20 @@ int main(int argc, char **argv) {
exit(0); exit(0);
} }
bool has_piped_input = !isatty(STDIN_FILENO);
if (has_piped_input && !vm.count("help")) {
std::string line;
if (std::getline(std::cin, line) && !line.empty()) {
return ct::process_command(line);
}
}
if (vm.count("command")) {
std::string command = vm["command"].as<std::string>();
return ct::process_command(command);
}
if ((!vm.count("username"))) { if ((!vm.count("username"))) {
std::cout << "Username option is required, specify with " std::cout << "Username option is required, specify with "
<< "-u or --username." << std::endl; << "-u or --username." << std::endl;

View File

@ -191,11 +191,12 @@ char *ppath_private(char *name) {
if (!path) if (!path)
return NULL; return NULL;
rpath = psync_strcat(path, "/", name, NULL); rpath = psync_strcat(path, "/", name, NULL);
free(path);
if (stat(rpath, &st) && mkdir(path, PSYNC_DEFAULT_POSIX_FOLDER_MODE)) { if (stat(rpath, &st) && mkdir(path, PSYNC_DEFAULT_POSIX_FOLDER_MODE)) {
psync_free(rpath); psync_free(rpath);
free(path);
return NULL; return NULL;
} }
free(path);
return rpath; return rpath;
} }

View File

@ -21,6 +21,7 @@ key_t pshm_get_key() {
return (key_t)-1; return (key_t)-1;
} }
snprintf(path, sizeof(path), "%s/.pcloud/data.db", home); snprintf(path, sizeof(path), "%s/.pcloud/data.db", home);
free(home);
return ftok(path, 'A'); return ftok(path, 'A');
} }

View File

@ -610,9 +610,8 @@ psync_rsa_publickey_t prsa_load_public(const unsigned char *keydata,
// function gets the actual key length from the ASN.1 header, trims any bytes // function gets the actual key length from the ASN.1 header, trims any bytes
// that exceed that length, and writes the trimmed key and length to keydata // that exceed that length, and writes the trimmed key and length to keydata
// and keylen. // and keylen.
static unsigned char* trim_der_key(const unsigned char *keydata, size_t *keylen) { static void trim_der_key(unsigned char *keydata, size_t *keylen) {
size_t len, header_size, correct_len; size_t len, header_size, correct_len;
unsigned char tag;
unsigned char *p = (unsigned char *)keydata; unsigned char *p = (unsigned char *)keydata;
const unsigned char *end; const unsigned char *end;
int ret; int ret;
@ -621,17 +620,16 @@ static unsigned char* trim_der_key(const unsigned char *keydata, size_t *keylen)
end = keydata + *keylen; end = keydata + *keylen;
ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
if (ret != 0) { if (ret != 0) {
return NULL; return;
} }
header_size = p - keydata; header_size = p - keydata;
correct_len = header_size + len; correct_len = header_size + len;
trimmed = malloc(correct_len); trimmed = malloc(correct_len);
if (!trimmed) return NULL; if (!trimmed) return;
memcpy(trimmed, keydata, correct_len); memcpy(trimmed, keydata, correct_len);
memcpy(keydata, trimmed, correct_len);
free(trimmed);
*keylen = correct_len; *keylen = correct_len;
return trimmed;
} }
psync_rsa_privatekey_t prsa_load_private(const unsigned char *keydata, size_t keylen) { psync_rsa_privatekey_t prsa_load_private(const unsigned char *keydata, size_t keylen) {
@ -639,7 +637,7 @@ psync_rsa_privatekey_t prsa_load_private(const unsigned char *keydata, size_t ke
mbedtls_rsa_context *rsa; mbedtls_rsa_context *rsa;
int ret; int ret;
trim_der_key(keydata, &keylen); trim_der_key((unsigned char *)keydata, &keylen);
mbedtls_pk_init(&ctx); mbedtls_pk_init(&ctx);
ret = mbedtls_pk_parse_key(&ctx, keydata, keylen, NULL, 0, rng_get, &rng); ret = mbedtls_pk_parse_key(&ctx, keydata, keylen, NULL, 0, rng_get, &rng);

View File

@ -77,8 +77,7 @@
// specified sockpath. it may write an error message and error message // 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 // size to out and out_size, and a "ret" value to ret (i think this is
// redundant maybe...) // redundant maybe...)
static int socket_connect(const char *sockpath, char **out, size_t *out_size, static int socket_connect(const char *sockpath, char **out, size_t *out_size) {
int *ret) {
int fd; int fd;
struct sockaddr_un addr; struct sockaddr_un addr;
const char *error_msg; 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"; error_msg = "Unable to create unix socket";
*out = strdup(error_msg); *out = strdup(error_msg);
*out_size = strlen(error_msg) + 1; *out_size = strlen(error_msg) + 1;
*ret = POVERLAY_SOCKET_CREATE_FAILED;
return 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"; error_msg = "Unable to connect to UNIX socket";
*out = strdup(error_msg); *out = strdup(error_msg);
*out_size = strlen(error_msg) + 1; *out_size = strlen(error_msg) + 1;
*ret = POVERLAY_SOCKET_CONNECT_FAILED;
return POVERLAY_SOCKET_CONNECT_FAILED; return POVERLAY_SOCKET_CONNECT_FAILED;
} }
return fd; return fd;
} }
static int write_request(int fd, int msgtype, const char *value, char **out, static int write_request(int fd, int msgtype, const char *value, char **out, size_t *out_size) {
size_t *out_size, int *ret) {
uint64_t bytes_written; uint64_t bytes_written;
int rc; int len = strlen(value);
int len; int size = sizeof(rpc_message_t) + len + 1;
int size; char *buf = (char *)malloc(size);
char *buf;
const char *err;
rpc_message_t *request;
char *curbuf;
*ret = 0; // Initialize ret to 0 // prepare the message
rpc_message_t *request = (rpc_message_t *)buf;
len = strlen(value);
size = sizeof(rpc_message_t) + len + 1;
buf = (char *)malloc(size);
request = (rpc_message_t *)buf;
memset(request, 0, size); memset(request, 0, size);
request->type = msgtype; request->type = msgtype;
strncpy(request->value, value, len + 1); strncpy(request->value, value, len + 1);
request->length = size; request->length = size;
bytes_written = 0; bytes_written = 0;
curbuf = (char *)request;
while (bytes_written < request->length && *ret == 0) { // write to the socket
rc = write(fd, curbuf, (request->length - bytes_written)); 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 (rc <= 0) {
if (errno != EINTR) { if (errno != EINTR) {
err = "failed to write to socket."; const char *err = "failed to write to socket.";
*out = strdup(err); *out = strdup(err);
*ret = POVERLAY_WRITE_SOCK_ERR; *out_size = strlen(err) + 1;
writeerr = POVERLAY_WRITE_SOCK_ERR;
} }
} else { } else {
bytes_written += rc; 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) { // return error if only partial data written
err = "Communication error"; if (!writeerr && bytes_written != request->length) {
const char *err = "communication error";
*out = strdup(err); *out = strdup(err);
*out_size = strlen(err) + 1; *out_size = strlen(err) + 1;
*ret = POVERLAY_WRITE_COMM_ERR; writeerr = POVERLAY_WRITE_COMM_ERR;
} }
free(buf); free(buf);
return *ret; return writeerr;
} }
static int read_response(int fd, char **out, size_t *out_size, int *ret) { static int read_response(int fd, char **out, size_t *out_size) {
rpc_message_t *msg;
ssize_t bytes_read;
msg = (rpc_message_t *)malloc(POVERLAY_BUFSIZE); rpc_message_t *msg = (rpc_message_t *)malloc(POVERLAY_BUFSIZE);
if (msg == NULL) { if (msg == NULL) {
const char *error_msg = "Memory allocation failed"; const char *error_msg = "Memory allocation failed";
*out = strdup(error_msg); *out = strdup(error_msg);
*out_size = strlen(error_msg) + 1; *out_size = strlen(error_msg) + 1;
*ret = -1;
return -1; return -1;
} }
bytes_read = read(fd, msg, POVERLAY_BUFSIZE); ssize_t bytes_read = read(fd, msg, POVERLAY_BUFSIZE);
if (bytes_read <= 0) { if (bytes_read <= 0) {
const char *error_msg = (bytes_read == 0) ? "Connection closed" : "Read error"; const char *error_msg = (bytes_read == 0) ? "Connection closed" : "Read error";
free(msg);
*out = strdup(error_msg); *out = strdup(error_msg);
*out_size = strlen(error_msg) + 1; *out_size = strlen(error_msg) + 1;
*ret = -1; free(msg);
return -1; return -1;
} }
*out = (char *)malloc(msg->length); *out = (char *)malloc(msg->length);
memcpy(*out, msg->value, msg->length); memcpy(*out, msg->value, msg->length);
*out_size = msg->length; *out_size = msg->length;
*ret = msg->type;
free(msg); free(msg);
return 0; return 0;
} }
int rpc_get_state(pCloud_FileState *state, char *path) { int rpc_get_state(pCloud_FileState *state, char *path) {
char *errm = NULL;
size_t errm_size = 0;
int rep = 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); 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); debug(D_NOTICE, "The error is %s", errm);
if (rep == 10) }
if (rep == 10) {
*state = FileStateInSync; *state = FileStateInSync;
else if (rep == 12) } else if (rep == 12) {
*state = FileStateInProgress; *state = FileStateInProgress;
else if (rep == 11) } else if (rep == 11) {
*state = FileStateNoSync; *state = FileStateNoSync;
else } else {
*state = FileStateInvalid; *state = FileStateInvalid;
} else }
} else {
debug(D_ERROR, "rpc_get_state ERROR rep[%d] path[%s]", rep, path); debug(D_ERROR, "rpc_get_state ERROR rep[%d] path[%s]", rep, path);
}
if(errm) {
free(errm); free(errm);
}
return 0; return 0;
} }
// path contains the input argument(s). // path contains the input argument(s).
int rpc_call(int id, const char *path, int *ret, char **errm, size_t *errmsz) { int rpc_call(int id, const char *path, char **errm, size_t *errmsz) {
int result; int result = 0;
int sockfd; int sockfd = -1;
sockfd = -1; sockfd = socket_connect(PRPC_SOCK_PATH, errm, errmsz);
result = 0;
*errm = NULL;
*errmsz = 0;
*ret = 0;
sockfd = socket_connect(PRPC_SOCK_PATH, errm, errmsz, ret);
if (sockfd >= 0) { if (sockfd >= 0) {
if ((result = write_request(sockfd, id, path, errm, errmsz, ret)) == 0) { if ((result = write_request(sockfd, id, path, errm, errmsz)) == 0) {
result = read_response(sockfd, errm, errmsz, ret); result = read_response(sockfd, errm, errmsz);
} }
close(sockfd); close(sockfd);
} else { } else {
result = -1; 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; } pCloud_FileState;
int rpc_get_state(pCloud_FileState *, char *); 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 #ifdef __cplusplus
} }