mirror of
https://github.com/servalproject/serval-dna.git
synced 2024-12-30 09:58:55 +00:00
71cbe86566
This introduces a new way of linking Serval executables and dynamic libraries from static libraries like libservald.a -- called "feature-driven" linking. The Makefile now links servald and serval-tests from libservald.a, rather than from an explicit list of object (.o) files. Thanks to the section-based method for registering functions such as HTTP handlers, CLI commands and MDP handlers, these object files had become "stand-alone" and hence were no longer included in the link because there was no unresolved reference that required them to be linked in. The new "feature.h" provides the DECLARE_FEATURE(name) macro that each stand-alone source file uses to declare the named feature(s) it provides. Each executable can call the USE_FEATURE(name) macro in any of its explicitly-linked source files to cause the corresponding object(s) to be included in the link, eg, servald_features.c. The DEFINE_BINDING() macro has been extended so that every individual MDP binding is given a feature name based on its port number macro, eg, "mdp_binding_MDP_PORT_ECHO". Some features have been factored into their own separate source files so they can be omitted or included in a build independently of each other: - the MDP bindings for MDP_PORT_DNALOOKUP, MDP_PORT_ECHO, MDP_PORT_TRACE, MDP_PORT_KEYMAPREQUEST, MDP_PORT_RHIZOME_xxx, MDP_PORT_PROBE, MDP_PORT_STUN, MDP_PORT_STUNREQ - the CLI "log" and "echo" commands - the CLI "rhizome direct" command The JNI source files are only compiled if the <jni.h> header is present, otherwise they are omitted from libservald.so.
285 lines
9.2 KiB
C
285 lines
9.2 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 "str.h"
|
|
#include "strbuf.h"
|
|
#include "strbuf_helpers.h"
|
|
#include "instance.h"
|
|
#include "mdp_client.h"
|
|
#include "server.h"
|
|
|
|
DEFINE_FEATURE(cli_config);
|
|
|
|
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;
|
|
}
|