Expose daemon's primary SID in 'proc/primary_sid'

This helps tests distinguish which of a daemon's keyring identities
is used as its primary identity, which is not otherwise obvious
without consulting the routing table, because slots are allocated
in random order.
This commit is contained in:
Andrew Bettison 2018-03-26 22:35:34 +10:30
parent 798e34cc5b
commit 1a091aa8a1
5 changed files with 121 additions and 43 deletions

View File

@ -1502,10 +1502,11 @@ static keyring_identity *keyring_new_identity()
keyring_identity *keyring_inmemory_identity(){
keyring_identity *id = keyring_new_identity();
keyring_finalise_identity(NULL, id);
if (id)
if (id) {
keyring_finalise_identity(NULL, id);
add_subscriber(id);
INFOF("created in-memory identity SID=%s", alloca_tohex_sid_t(id->subscriber->sid));
INFOF("created in-memory identity SID=%s", alloca_tohex_sid_t(id->subscriber->sid));
}
return id;
}

View File

@ -1,5 +1,6 @@
/*
Serval DNA MDP addressing
Copyright (C) 2016-2018 Flinders University
Copyright (C) 2012-2015 Serval Project Inc.
Copyright (C) 2012 Paul Gardner-Stephen
@ -28,6 +29,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include <assert.h>
#include <arpa/inet.h>
#include "lang.h" // for bool_t
#include "serval.h"
#include "conf.h"
#include "keyring.h"
@ -54,37 +56,64 @@ static struct broadcast bpilist[MAX_BPIS];
static __thread struct tree_root root={.index_size_bytes=SID_SIZE};
static __thread struct subscriber *my_subscriber=NULL;
static __thread bool_t primary_sid_written = 0;
static __thread struct subscriber *primary_sid = NULL;
static __thread struct subscriber *my_subscriber = NULL;
struct subscriber *get_my_subscriber(bool_t create){
struct subscriber *get_my_subscriber(bool_t create)
{
if (!serverMode)
return NULL;
assert(keyring != NULL);
if (my_subscriber && my_subscriber->reachable != REACHABLE_SELF)
my_subscriber = NULL;
if (!my_subscriber){
keyring_identity *id = keyring->identities;
while(id){
if (id->subscriber->reachable == REACHABLE_SELF){
// Look for a reachable self-identity in the keyring.
if (!my_subscriber) {
assert(keyring != NULL);
keyring_identity *id;
for (id = keyring->identities; id; id = id->next)
if (id->subscriber->reachable == REACHABLE_SELF) {
my_subscriber = id->subscriber;
return my_subscriber;
break;
}
id = id->next;
}
// If there is no reachable self-identity in the keyring, then roll one in-memory, which will
// persist until the server terminates.
if (create){
id = keyring_inmemory_identity();
}
// If there is no reachable self-identity in the keyring, then roll one in-memory, which will
// persist until the server terminates.
if (!my_subscriber && create) {
keyring_identity *id = keyring_inmemory_identity();
if (id)
my_subscriber = id->subscriber;
}
// Normally, the server creates files in proc/ before it creates its pidfile, and does not modify
// those files while running. This avoids any race conditions with other processes that read the
// files. In this case, the proc/primary_sid file is written _after_ the pidfile, and could
// potentially be re-written while running (eg, if the primary identity is deleted, causing the
// server to choose another). Potential race conditions are avoided because (1) the size of the
// file never alters, and (2) the server_write_proc_state() function overwrites any existing
// content using a single, indivisible write(2) system call.
if (!primary_sid_written || primary_sid != my_subscriber) {
primary_sid = my_subscriber;
if (my_subscriber) {
const char *sidhex = alloca_tohex_sid_t(primary_sid->sid);
server_write_proc_state("primary_sid", "%s", sidhex);
INFOF("PRIMARY IDENTITY sid=%s", sidhex);
}
else {
server_unlink_proc_state("primary_sid");
INFOF("NO PRIMARY IDENTITY");
}
primary_sid_written = 1;
}
return my_subscriber;
}
void release_my_subscriber(){
void release_my_subscriber()
{
if (my_subscriber && my_subscriber->identity->slot==0)
keyring_free_identity(my_subscriber->identity);
server_unlink_proc_state("primary_sid");
my_subscriber = NULL;
primary_sid = NULL;
primary_sid_written = 0;
}
static int free_node(void **record, void *UNUSED(context))
@ -98,6 +127,8 @@ static int free_node(void **record, void *UNUSED(context))
alloca_tohex_sid_t(subscriber->sid), subscriber->sync_state);
if (subscriber->identity)
FATAL("Can't free a subscriber that is unlocked in the keyring");
if (subscriber == my_subscriber)
FATAL("Can't free a subscriber that is the primary identity");
free(subscriber);
*record=NULL;
return 0;

View File

@ -77,7 +77,6 @@ static void serverCleanUp();
static const char *_server_pidfile_path(struct __sourceloc __whence);
#define server_pidfile_path() (_server_pidfile_path(__WHENCE__))
static int server_write_proc_state(const char *path, const char *fmt, ...);
static int server_get_proc_state(const char *path, char *buff, size_t buff_len);
static void server_stop_alarms();
@ -343,6 +342,9 @@ static int server_write_pid()
server_write_proc_state("http_port", "%d", httpd_server_port);
server_write_proc_state("mdp_inet_port", "%d", mdp_loopback_port);
// Create or unlink the "primary_sid" proc state file.
get_my_subscriber(0);
// Create a locked pidfile to advertise that the server is now running.
const char *pidfile_path = server_pidfile_path();
if (pidfile_path == NULL)
@ -452,29 +454,46 @@ static int get_proc_path(const char *path, char *buf, size_t bufsiz)
return 0;
}
static int server_write_proc_state(const char *path, const char *fmt, ...)
int server_write_proc_state(const char *path, const char *fmt, ...)
{
char path_buf[400];
if (get_proc_path(path, path_buf, sizeof path_buf)==-1)
return -1;
// Create the directory that contains the path, if it does not already exist.
size_t dirsiz = strlen(path_buf) + 1;
char dir_buf[dirsiz];
strcpy(dir_buf, path_buf);
const char *dir = dirname(dir_buf); // modifies dir_buf[]
if (mkdirs_info(dir, 0700) == -1)
return WHY_perror("mkdirs()");
if (emkdirs_info(dir, 0700) == -1)
return -1;
// Format the file's new content in a local buffer on the stack.
strbuf sb;
STRBUF_ALLOCA_FIT(sb, 1024, strbuf_va_printf(sb, fmt));
FILE *f = fopen(path_buf, "w");
if (!f)
return WHY_perror("fopen()");
va_list ap;
va_start(ap, fmt);
vfprintf(f, fmt, ap);
va_end(ap);
fclose(f);
// Overwrite the file, creating it if necessary, using a single write(2) system call, followed by
// a ftruncate(2) system call (in case the file already existed and was longer than its new
// content). This allows potential race conditions to be avoided for files that are overwritten
// while the server runs, as long as the written contents are always the same size, or always
// contain a terminating sequence (eg, newline).
int fd = open(path_buf, O_CREAT | O_WRONLY, 0700);
if (fd == -1)
return WHYF_perror("open(%s, O_CREAT|O_WRONLY, 0700)", alloca_str_toprint(path_buf));
int ret = write_all(fd, strbuf_str(sb), strbuf_len(sb));
if (ret != -1 && (ret = ftruncate(fd, strbuf_len(sb)) == -1))
ret = WHYF_perror("ftruncate(%s, %zu)", alloca_str_toprint(path_buf), strbuf_len(sb));
close(fd);
return ret;
}
int server_unlink_proc_state(const char *path)
{
char path_buf[400];
if (get_proc_path(path, path_buf, sizeof path_buf)==-1)
return -1;
if (unlink(path) == -1 && errno != ENOENT)
return WHYF_perror("unlink(%s)", alloca_str_toprint(path));
return 0;
}
@ -483,16 +502,20 @@ static int server_get_proc_state(const char *path, char *buff, size_t buff_len)
char path_buf[400];
if (get_proc_path(path, path_buf, sizeof path_buf)==-1)
return -1;
FILE *f = fopen(path_buf, "r");
if (!f)
return -1;
int ret=0;
if (!fgets(buff, buff_len, f))
ret = WHY_perror("fgets");
if (!f) {
if (errno != ENOENT)
return WHYF_perror("fopen(%s)", alloca_str_toprint(path_buf));
return 1;
}
int ret = 0;
errno = 0; // fgets() does not set errno on end-of-file
if (!fgets(buff, buff_len, f)) {
if (errno)
ret = WHYF_perror("fgets from %s", alloca_str_toprint(path_buf));
else
ret = 1;
}
fclose(f);
return ret;
}
@ -788,11 +811,15 @@ static void cli_server_details(struct cli_context *context, const struct pid_tid
cli_put_long(context, id->tid, "\n");
}
char buff[256];
if (server_get_proc_state("http_port", buff, sizeof buff)!=-1){
if (server_get_proc_state("primary_sid", buff, sizeof buff) == 0){
cli_field_name(context, "primary_sid", ":");
cli_put_string(context, buff, "\n");
}
if (server_get_proc_state("http_port", buff, sizeof buff) == 0){
cli_field_name(context, "http_port", ":");
cli_put_string(context, buff, "\n");
}
if (server_get_proc_state("mdp_inet_port", buff, sizeof buff)!=-1){
if (server_get_proc_state("mdp_inet_port", buff, sizeof buff) == 0){
cli_field_name(context, "mdp_inet_port", ":");
cli_put_string(context, buff, "\n");
}

View File

@ -49,6 +49,9 @@ void server_close();
void server_loop(time_ms_t (*waiting)(time_ms_t, time_ms_t, time_ms_t), void (*wokeup)());
int server_write_proc_state(const char *path, const char *fmt, ...);
int server_unlink_proc_state(const char *path);
void server_rhizome_add_bundle(uint64_t rowid);
DECLARE_TRIGGER(shutdown);

View File

@ -421,6 +421,22 @@ servald_start() {
SERVALD_SERVER_CHDIR="$instance_dir" SERVALD_LOG_FILE="$instance_servald_log" $servald start "$@"
}
# Utility function:
# - fetch the daemon's primary SID
get_servald_primary_sid() {
local _instance="$2"
[ -z "$_instance" ] || push_and_set_instance $_instance || return $?
local _var="$1"
local _sid=$(<"$SERVALINSTANCE_PATH/proc/primary_sid")
assert --message="instance $instance_name primary SID is known" [ -n "$_sid" ]
if [ -n "$_var" ]; then
eval "$_var=\$_sid"
tfw_log "$_var=$_sid"
fi
[ -z "$_instance" ] || pop_instance
return 0
}
# Utility function:
# - test whether the daemon's HTTP server has started
servald_http_server_started() {