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:
parent
f437fe840e
commit
27977765e8
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
54
main.cpp
54
main.cpp
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue