mirror of
https://github.com/servalproject/serval-dna.git
synced 2024-12-23 15:02:27 +00:00
282 lines
9.1 KiB
C
282 lines
9.1 KiB
C
/*
|
|
Serval configuration command-line functions
|
|
Copyright (C) 2014 Serval Project Inc.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "cli.h"
|
|
#include "conf.h"
|
|
#include "commandline.h"
|
|
#include "strbuf.h"
|
|
#include "strbuf_helpers.h"
|
|
#include "instance.h"
|
|
#include "mdp_client.h"
|
|
#include "server.h"
|
|
|
|
DEFINE_CMD(app_config_schema, CLIFLAG_PERMISSIVE_CONFIG,
|
|
"Display configuration schema.",
|
|
"config", "schema");
|
|
static int app_config_schema(const struct cli_parsed *parsed, struct cli_context *context)
|
|
{
|
|
DEBUG_cli_parsed(verbose, parsed);
|
|
struct cf_om_node *root = NULL;
|
|
if (cf_sch_config_main(&root) == -1) {
|
|
cf_om_free_node(&root);
|
|
return -1;
|
|
}
|
|
struct cf_om_iterator it;
|
|
for (cf_om_iter_start(&it, root); it.node; cf_om_iter_next(&it))
|
|
if (it.node->text || it.node->nodc == 0) {
|
|
cli_put_string(context, it.node->fullkey,"=");
|
|
cli_put_string(context, it.node->text, "\n");
|
|
}
|
|
cf_om_free_node(&root);
|
|
return 0;
|
|
}
|
|
|
|
DEFINE_CMD(app_config_dump, CLIFLAG_PERMISSIVE_CONFIG,
|
|
"Dump configuration settings.",
|
|
"config","dump","[--full]");
|
|
static int app_config_dump(const struct cli_parsed *parsed, struct cli_context *context)
|
|
{
|
|
DEBUG_cli_parsed(verbose, parsed);
|
|
int full = 0 == cli_arg(parsed, "--full", NULL, NULL, NULL);
|
|
if (create_serval_instance_dir() == -1)
|
|
return -1;
|
|
struct cf_om_node *root = NULL;
|
|
int ret = cf_fmt_config_main(&root, &config);
|
|
if (ret == CFERROR) {
|
|
cf_om_free_node(&root);
|
|
return -1;
|
|
}
|
|
struct cf_om_iterator it;
|
|
for (cf_om_iter_start(&it, root); it.node; cf_om_iter_next(&it)) {
|
|
if (it.node->text && (full || it.node->line_number)) {
|
|
cli_put_string(context, it.node->fullkey, "=");
|
|
cli_put_string(context, it.node->text, "\n");
|
|
}
|
|
}
|
|
cf_om_free_node(&root);
|
|
return ret == CFOK ? 0 : 1;
|
|
}
|
|
|
|
static int mdp_client_sync_config(time_ms_t timeout)
|
|
{
|
|
/* Bind to MDP socket and await confirmation */
|
|
struct mdp_header mdp_header = {
|
|
.remote.port = MDP_SYNC_CONFIG,
|
|
};
|
|
int mdpsock = mdp_socket();
|
|
if (mdpsock == -1)
|
|
return WHY("cannot create MDP socket");
|
|
set_nonblock(mdpsock);
|
|
int r = mdp_send(mdpsock, &mdp_header, NULL, 0);
|
|
if (r == -1)
|
|
goto end;
|
|
time_ms_t deadline = gettime_ms() + timeout; // TODO add --timeout option
|
|
struct mdp_header rev_header;
|
|
do {
|
|
ssize_t len = mdp_poll_recv(mdpsock, deadline, &rev_header, NULL, 0);
|
|
if (len == -1){
|
|
r = -1;
|
|
goto end;
|
|
}
|
|
if (len == -2) {
|
|
WHYF("timeout while synchronising daemon configuration");
|
|
r = -1;
|
|
goto end;
|
|
}
|
|
} while (!(rev_header.flags & MDP_FLAG_CLOSE));
|
|
r = 0;
|
|
|
|
end:
|
|
mdp_close(mdpsock);
|
|
return r;
|
|
}
|
|
|
|
DEFINE_CMD(app_config_set, CLIFLAG_PERMISSIVE_CONFIG,
|
|
"Set and del specified configuration variables.",
|
|
"config","set","<variable>","<value>","...");
|
|
DEFINE_CMD(app_config_set, CLIFLAG_PERMISSIVE_CONFIG,
|
|
"Del and set specified configuration variables.",
|
|
"config","del","<variable>","...");
|
|
DEFINE_CMD(app_config_set, CLIFLAG_PERMISSIVE_CONFIG,
|
|
"Synchronise with the daemon's configuration.",
|
|
"config","sync","...");
|
|
static int app_config_set(const struct cli_parsed *parsed, struct cli_context *UNUSED(context))
|
|
{
|
|
DEBUG_cli_parsed(verbose, parsed);
|
|
if (create_serval_instance_dir() == -1)
|
|
return -1;
|
|
// <kludge>
|
|
// This fixes a subtle bug in when upgrading the Batphone app: the servald.conf file does
|
|
// not get upgraded. The bug goes like this:
|
|
// 1. new Batphone APK is installed, but prior servald.conf is not overwritten because it
|
|
// comes in serval.zip;
|
|
// 2. new Batphone is started, which calls JNI "stop" command, which reads the old servald.conf
|
|
// into memory buffer;
|
|
// 3. new Batphone unpacks serval.zip, overwriting servald.conf with new version;
|
|
// 4. new Batphone calls JNI "config set rhizome.enable 1", which sets the "rhizome.enable"
|
|
// config option in the existing memory buffer and overwrites servald.conf;
|
|
// Bingo, the old version of servald.conf is what remains. This kludge intervenes in step 4, by
|
|
// reading the new servald.conf into the memory buffer before applying the "rhizome.enable" set
|
|
// value and overwriting.
|
|
if (cf_om_reload() == -1)
|
|
return -1;
|
|
// </kludge>
|
|
const char *var[parsed->argc - 1];
|
|
const char *val[parsed->argc - 1];
|
|
unsigned nvar = 0;
|
|
unsigned i;
|
|
for (i = 1; i < parsed->argc; ++i) {
|
|
const char *arg = parsed->args[i];
|
|
int iv = -1;
|
|
if (strcmp(arg, "set") == 0) {
|
|
if (i + 2 > parsed->argc)
|
|
return WHYF("malformed command at args[%d]: 'set' not followed by two arguments", i);
|
|
var[nvar] = parsed->args[iv = ++i];
|
|
val[nvar] = parsed->args[++i];
|
|
} else if (strcmp(arg, "del") == 0) {
|
|
if (i + 1 > parsed->argc)
|
|
return WHYF("malformed command at args[%d]: 'del' not followed by one argument", i);
|
|
var[nvar] = parsed->args[iv = ++i];
|
|
val[nvar] = NULL;
|
|
} else if (strcmp(arg, "sync") == 0)
|
|
var[nvar] = val[nvar] = NULL;
|
|
else
|
|
return WHYF("malformed command at args[%d]: unsupported action '%s'", i, arg);
|
|
if (var[nvar] && !is_configvarname(var[nvar]))
|
|
return WHYF("malformed command at args[%d]: '%s' is not a valid config option name", iv, var[nvar]);
|
|
++nvar;
|
|
}
|
|
int changed = 0;
|
|
for (i = 0; i < nvar; ++i) {
|
|
if (var[i]) {
|
|
if (cf_om_set(&cf_om_root, var[i], val[i]) == -1)
|
|
return -1;
|
|
if (val[i])
|
|
INFOF("config set %s %s", var[i], alloca_str_toprint(val[i]));
|
|
else
|
|
INFOF("config del %s", var[i]);
|
|
changed = 1;
|
|
} else {
|
|
if (changed) {
|
|
if (cf_om_save() == -1)
|
|
return -1;
|
|
if (cf_reload() == -1) // logs an error if the new config is bad
|
|
return 2;
|
|
changed = 0;
|
|
}
|
|
int pid = server_pid();
|
|
if (pid) {
|
|
INFO("config sync");
|
|
// TODO make timeout configurable with --timeout option.
|
|
if (mdp_client_sync_config(10000) == -1)
|
|
return 3;
|
|
} else
|
|
INFO("config sync -- skipped, server not running");
|
|
}
|
|
}
|
|
if (changed) {
|
|
if (cf_om_save() == -1)
|
|
return -1;
|
|
if (cf_reload() == -1) // logs an error if the new config is bad
|
|
return 2;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
DEFINE_CMD(app_config_get, CLIFLAG_PERMISSIVE_CONFIG,
|
|
"Get specified configuration variable.",
|
|
"config","get","[<variable>]");
|
|
static int app_config_get(const struct cli_parsed *parsed, struct cli_context *context)
|
|
{
|
|
DEBUG_cli_parsed(verbose, parsed);
|
|
const char *var;
|
|
if (cli_arg(parsed, "variable", &var, is_configvarpattern, NULL) == -1)
|
|
return -1;
|
|
if (create_serval_instance_dir() == -1)
|
|
return -1;
|
|
if (cf_om_reload() == -1)
|
|
return -1;
|
|
if (var && is_configvarname(var)) {
|
|
const char *value = cf_om_get(cf_om_root, var);
|
|
if (value) {
|
|
cli_field_name(context, var, "=");
|
|
cli_put_string(context, value, "\n");
|
|
}
|
|
} else {
|
|
struct cf_om_iterator it;
|
|
for (cf_om_iter_start(&it, cf_om_root); it.node; cf_om_iter_next(&it)) {
|
|
if (var && cf_om_match(var, it.node) <= 0)
|
|
continue;
|
|
if (it.node->text) {
|
|
cli_field_name(context, it.node->fullkey, "=");
|
|
cli_put_string(context, it.node->text, "\n");
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
DEFINE_CMD(app_config_paths, CLIFLAG_PERMISSIVE_CONFIG,
|
|
"Dump file and directory paths.",
|
|
"config", "paths");
|
|
static int app_config_paths(const struct cli_parsed *parsed, struct cli_context *context)
|
|
{
|
|
DEBUG_cli_parsed(verbose, parsed);
|
|
if (cf_om_reload() == -1)
|
|
return -1;
|
|
char path[1024];
|
|
if (FORMF_SERVAL_ETC_PATH(path, NULL)) {
|
|
cli_field_name(context, "SERVAL_ETC_PATH", ":");
|
|
cli_put_string(context, path, "\n");
|
|
}
|
|
if (FORMF_SERVAL_RUN_PATH(path, NULL)) {
|
|
cli_field_name(context, "SERVAL_RUN_PATH", ":");
|
|
cli_put_string(context, path, "\n");
|
|
}
|
|
if (FORMF_SERVAL_CACHE_PATH(path, NULL)) {
|
|
cli_field_name(context, "SERVAL_CACHE_PATH", ":");
|
|
cli_put_string(context, path, "\n");
|
|
}
|
|
strbuf sb = strbuf_local_buf(path);
|
|
strbuf_system_log_path(sb);
|
|
if (!strbuf_overrun(sb)) {
|
|
cli_field_name(context, "SYSTEM_LOG_PATH", ":");
|
|
cli_put_string(context, path, "\n");
|
|
}
|
|
strbuf_reset(sb);
|
|
strbuf_serval_log_path(sb);
|
|
if (!strbuf_overrun(sb)) {
|
|
cli_field_name(context, "SERVAL_LOG_PATH", ":");
|
|
cli_put_string(context, path, "\n");
|
|
}
|
|
if (FORMF_SERVAL_TMP_PATH(path, NULL)) {
|
|
cli_field_name(context, "SERVAL_TMP_PATH", ":");
|
|
cli_put_string(context, path, "\n");
|
|
}
|
|
if (FORMF_SERVALD_PROC_PATH(path, NULL)) {
|
|
cli_field_name(context, "SERVALD_PROC_PATH", ":");
|
|
cli_put_string(context, path, "\n");
|
|
}
|
|
if (FORMF_RHIZOME_STORE_PATH(path, NULL)) {
|
|
cli_field_name(context, "RHIZOME_STORE_PATH", ":");
|
|
cli_put_string(context, path, "\n");
|
|
}
|
|
return 0;
|
|
}
|