Filesystem Heirarchy Standard (FHS) paths

If no instance directory specified, then use paths like
/etc/serval/serval.conf
/var/cache/serval
/var/log/serval
/var/run/serval
etc. for files, instead of all in a single directory.

Log all directory creation as INFO messages.

Interpretation of log.file.directory_path has changed slightly.

Updated servald configuration tech doc.
This commit is contained in:
Andrew Bettison 2014-03-26 15:35:43 +10:30
parent d228165814
commit 53c1b1c04c
23 changed files with 672 additions and 282 deletions

View File

@ -2,6 +2,8 @@ prefix=@prefix@
exec_prefix=@exec_prefix@
bindir=@bindir@
sbindir=@sbindir@
sysconfdir=@sysconfdir@
localstatedir=@localstatedir@
NACL_BASE= nacl/src
include $(NACL_BASE)/nacl.mk
@ -42,6 +44,7 @@ MDP_CLIENT_OBJS= $(MDP_CLIENT_SRCS:.c=.o)
LDFLAGS=@LDFLAGS@ @LIBS@ @PORTAUDIO_LIBS@ @SRC_LIBS@ @SPANDSP_LIBS@ @CODEC2_LIBS@ @PTHREAD_LIBS@
CFLAGS= -Isqlite-amalgamation-3070900 @CPPFLAGS@ @CFLAGS@ @PORTAUDIO_CFLAGS@ @SRC_CFLAGS@ @SPANDSP_CFLAGS@ @PTHREAD_CFLAGS@ $(VOIPTEST_CFLAGS) -Inacl/include
CFLAGS+=-DSYSCONFDIR="\"$(sysconfdir)\"" -DLOCALSTATEDIR="\"$(localstatedir)\""
CFLAGS+=-fPIC
CFLAGS+=-Wall -Wno-unused-value
# Solaris magic

View File

@ -932,8 +932,13 @@ int app_server_start(const struct cli_parsed *parsed, struct cli_context *contex
RETURN(WHY("Server process did not start"));
ret = 0;
}
cli_field_name(context, "instancepath", ":");
cli_put_string(context, serval_instancepath(), "\n");
const char *ipath = instance_path();
if (ipath) {
cli_field_name(context, "instancepath", ":");
cli_put_string(context, ipath, "\n");
}
cli_field_name(context, "pidfile", ":");
cli_put_string(context, server_pidfile_path(), "\n");
cli_field_name(context, "pid", ":");
cli_put_long(context, pid, "\n");
char buff[256];
@ -968,9 +973,13 @@ int app_server_stop(const struct cli_parsed *parsed, struct cli_context *context
DEBUG_cli_parsed(parsed);
int pid, tries, running;
time_ms_t timeout;
const char *instancepath = serval_instancepath();
cli_field_name(context, "instancepath", ":");
cli_put_string(context, instancepath, "\n");
const char *ipath = instance_path();
if (ipath) {
cli_field_name(context, "instancepath", ":");
cli_put_string(context, ipath, "\n");
}
cli_field_name(context, "pidfile", ":");
cli_put_string(context, server_pidfile_path(), "\n");
pid = server_pid();
/* Not running, nothing to stop */
if (pid <= 0)
@ -983,8 +992,8 @@ int app_server_stop(const struct cli_parsed *parsed, struct cli_context *context
running = pid;
while (running == pid) {
if (tries >= 5) {
WHYF("Servald pid=%d for instance '%s' did not stop after %d SIGHUP signals",
pid, instancepath, tries);
WHYF("Servald pid=%d (pidfile=%s) did not stop after %d SIGHUP signals",
pid, server_pidfile_path(), tries);
return 253;
}
++tries;
@ -999,7 +1008,7 @@ int app_server_stop(const struct cli_parsed *parsed, struct cli_context *context
break;
}
WHY_perror("kill");
WHYF("Error sending SIGHUP to Servald pid=%d for instance '%s'", pid, instancepath);
WHYF("Error sending SIGHUP to Servald pid=%d (pidfile %s)", pid, server_pidfile_path());
return 252;
}
/* Allow a few seconds for the process to die. */
@ -1019,8 +1028,13 @@ int app_server_status(const struct cli_parsed *parsed, struct cli_context *conte
if (config.debug.verbose)
DEBUG_cli_parsed(parsed);
int pid = server_pid();
cli_field_name(context, "instancepath", ":");
cli_put_string(context, serval_instancepath(), "\n");
const char *ipath = instance_path();
if (ipath) {
cli_field_name(context, "instancepath", ":");
cli_put_string(context, ipath, "\n");
}
cli_field_name(context, "pidfile", ":");
cli_put_string(context, server_pidfile_path(), "\n");
cli_field_name(context, "status", ":");
cli_put_string(context, pid > 0 ? "running" : "stopped", "\n");
if (pid > 0) {
@ -1426,6 +1440,52 @@ int app_config_get(const struct cli_parsed *parsed, struct cli_context *context)
return 0;
}
int app_config_paths(const struct cli_parsed *parsed, struct cli_context *context)
{
if (config.debug.verbose)
DEBUG_cli_parsed(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(path, sizeof 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;
}
int app_rhizome_hash_file(const struct cli_parsed *parsed, struct cli_context *context)
{
if (config.debug.verbose)
@ -3019,6 +3079,8 @@ struct cli_schema command_line_options[]={
"Del and set specified configuration variables."},
{app_config_get,{"config","get","[<variable>]",NULL},CLIFLAG_PERMISSIVE_CONFIG,
"Get specified configuration variable."},
{app_config_paths,{"config","paths",NULL},CLIFLAG_PERMISSIVE_CONFIG,
"Dump file and directory paths."},
{app_vomp_console,{"console",NULL}, 0,
"Test phone call life-cycle from the console"},
{app_meshms_conversations,{"meshms","list","conversations" KEYRING_PIN_OPTIONS, "<sid>","[<offset>]","[<count>]",NULL},0,

4
conf.c
View File

@ -44,7 +44,7 @@ static struct file_meta config_meta = FILE_META_UNKNOWN;
static const char *conffile_path()
{
static char path[1024] = "";
if (!path[0] && !FORM_SERVAL_INSTANCE_PATH(path, CONFFILE_NAME))
if (!path[0] && !FORMF_SERVAL_ETC_PATH(path, CONFFILE_NAME))
abort();
return path;
}
@ -144,7 +144,7 @@ int cf_om_save()
const char *path = conffile_path();
char tempfile[1024];
FILE *outf = NULL;
if (!FORM_SERVAL_INSTANCE_PATH(tempfile, "serval.conf.temp"))
if (!FORMF_SERVAL_ETC_PATH(tempfile, CONFFILE_NAME ".temp"))
return -1;
if ((outf = fopen(tempfile, "w")) == NULL)
return WHYF_perror("fopen(%s, \"w\")", tempfile);

View File

@ -292,7 +292,7 @@ LOG_FORMAT_OPTIONS
END_STRUCT
STRUCT(log_format_file)
STRING(256, directory_path, "log", str_nonempty,, "Path of directory for log files, either absolute or relative to instance directory")
STRING(256, directory_path, "", str,, "Path of directory for log files, either absolute or relative to instance log directory")
STRING(256, path, "", str_nonempty,, "Path of single log file, either absolute or relative to directory_path")
ATOM(unsigned short, rotate, 12, ushort,, "Number of log files to rotate, zero means no deletion")
ATOM(uint32_t, duration, 3600, uint32_time_interval,, "Time duration of each log file, zero means one file per invocation")

View File

@ -14,6 +14,30 @@ dnl Specify default instance path
AC_ARG_VAR([INSTANCE_PATH], [default instance path for servald])
AS_IF([test "x$INSTANCE_PATH" != x], [AC_DEFINE_UNQUOTED([INSTANCE_PATH], ["$INSTANCE_PATH"], [default instance path])])
dnl Specify default Serval config directory
AC_ARG_VAR([SERVAL_ETC_PATH], [default Serval config directory])
AS_IF([test "x$SERVAL_ETC_PATH" != x], [AC_DEFINE_UNQUOTED([SERVAL_ETC_PATH], ["$SERVAL_ETC_PATH"], [default config directory])])
dnl Specify default Serval run directory
AC_ARG_VAR([SERVAL_RUN_PATH], [default Serval run directory])
AS_IF([test "x$SERVAL_RUN_PATH" != x], [AC_DEFINE_UNQUOTED([SERVAL_RUN_PATH], ["$SERVAL_RUN_PATH"], [default run directory])])
dnl Specify default Serval log directory
AC_ARG_VAR([SERVAL_LOG_PATH], [default Serval log directory])
AS_IF([test "x$SERVAL_LOG_PATH" != x], [AC_DEFINE_UNQUOTED([SERVAL_LOG_PATH], ["$SERVAL_LOG_PATH"], [default log directory])])
dnl Specify default system log directory
AC_ARG_VAR([SYSTEM_LOG_PATH], [default system log directory])
AS_IF([test "x$SYSTEM_LOG_PATH" != x], [AC_DEFINE_UNQUOTED([SYSTEM_LOG_PATH], ["$SYSTEM_LOG_PATH"], [default system log directory])])
dnl Specify default Serval tmp directory
AC_ARG_VAR([SERVAL_TMP_PATH], [default Serval tmp directory])
AS_IF([test "x$SERVAL_TMP_PATH" != x], [AC_DEFINE_UNQUOTED([SERVAL_TMP_PATH], ["$SERVAL_TMP_PATH"], [default Serval tmp directory])])
dnl Specify default Rhizome store directory
AC_ARG_VAR([RHIZOME_STORE_PATH], [default Rhizome store directory])
AS_IF([test "x$RHIZOME_STORE_PATH" != x], [AC_DEFINE_UNQUOTED([RHIZOME_STORE_PATH], ["$RHIZOME_STORE_PATH"], [default Rhizome store directory])])
dnl VoIP test app
AC_ARG_ENABLE(voiptest,
AS_HELP_STRING([--enable-voiptest], [Require VoIP test program (default: only build if dependencies are present)])

View File

@ -38,23 +38,6 @@ An option **VALUE** is a string of [US-ASCII][] characters, excluding newline
If a VALUE does not parse correctly, it is *invalid*.
Instance directory
------------------
By default, **servald** stores its configuration, keyring, and other temporary
files in its *instance directory*. The instance directory is set at run time
by the `SERVALINSTANCE_PATH` environment variable. If this is not set, then
**servald** uses a built-in default path which depends on its build-time option
and target platform:
* as specified by the `./configure INSTANCE_PATH=<path>` option when **servald**
was built from source
* on Android `/data/data/org.servalproject/var/serval-node`
* on other platforms `/var/serval-node`
**servald** will create its instance directory (and all enclosing parent
directories) if it does not already exist.
Configuration persistence
-------------------------
@ -115,22 +98,152 @@ defective file. This means that **servald** may always be used to inspect and
correct the configuration, and to stop a running daemon, despite a defective
configuration file.
Daemon processes
Configuration reloading
-----------------------
A running daemon re-loads its configuration whenever the `serval.conf` file is
changed. It does this by periodically checking the file's size and
modification time, and if they have changed, parses the file and updates its
own internal copy of the configuration settings.
As described above, the **servald** `start` command will not start a daemon
process if the `serval.conf` file is defective. However, the file may become
defective *after* the daemon has started.
In this case, the daemon will not terminate, nor load the defective file, but
will log an error and continue execution with its internal configuration
unchanged. The daemon's internal configuration will no longer be consistent
with the contents of the file. If the daemon is stopped or killed, it cannot
be re-started while `serval.conf` remains defective.
Despite detecting a defective configuration file, the daemon continues to check
for changes to the file, and attempts to re-load whenever it changes. As soon
as the defective `serval.conf` is fixed, the running daemon will load it
successfully and continue execution with the new configuration. The internal
configuration and the file contents will once again be consistent.
Daemon instances
----------------
To run more than one **servald** daemon process on the same device, each daemon
must have its own instance path (and hence its own `serval.conf`). Set the
`SERVALINSTANCE_PATH` environment variable to a different directory path before
starting each daemon.
It order to support more than one daemon running on the same host, each daemon
can be configured to use its own *instance directory*, as follows:
As described above, a defective `serval.conf` will prevent the **servald**
`start` command from starting a daemon process. Once the daemon is running, it
periodically checks whether `serval.conf` has changed (by comparing size and
modification time) and attempts to re-load it if it detects a change. If the
re-loaded file is defective, the daemon rejects it, logs an error, and
continues execution with its prior configuration unchanged. If the daemon is
stopped or killed, it cannot be re-started while `serval.conf` remains
defective.
* A daemon's instance directory can be set at run time by setting the
`SERVALINSTANCE_PATH` environment variable prior to starting the daemon.
This overrides all default paths. Once a daemon is running, the only way
to change its instance directory is to stop it and start another daemon
with a different value for the environment variable.
* If the instance directory is not set at run time, then if **servald** was
built with the `./configure INSTANCE_PATH=DIR` option, then the **servald**
executable will use the instance directory in `DIR` by default. The FHS
paths will never be used.
* On an Android system, if none of the above are used, then **servald** will
use the instance directory `/data/data/org.servalproject/var/serval-node`
by default. The FHS paths will never be used.
* If none of the above apply, then there is no *instance directory*.
Instead, [FHS][] paths are used (see below). Only one daemon can run in
this situation on the same host, since the single, common PID file will
prevent more than one being started.
The main use for multiple instances on a single host is for testing, and this
is used extensively in the automated test suite. Deployments other than
Android are unlikely to use an instance path, so the FHS paths are most likely
to be used in practice.
FHS paths
---------
By default, **servald** locates its files such as configuration, logs, Rhizome
storage, etc. in accordance with the [Filesystem Heirarchy Standard][FHS] 2.3:
* the **servald** executable is installed in `/usr/local/bin/servald`
* the configuration is stored in `/etc/serval/serval.conf`
* the keyring is stored in `/etc/serval/serval.keyring`
* the PID file is `/var/run/serval/servald.pid`
* the daemon creates its process information files under
`/var/run/serval/proc/`
* the daemon creates and rotates log files files under
`/var/log/serval/`
* the daemon creates and updates a symbolic link to the latest log file in
`/var/log/servald.log`
* the Rhizome store is in `/var/cache/serval/` (unless overridden by
configuration):
* the SQLite database is `/var/cache/serval/rhizome.db`
* large payloads are stored under `/var/cache/serval/blob/`
* the SQLite temporary directory is `/tmp/serval/sqlite3tmp/`
* dummy interface files are created under `/tmp/serval/` (unless overridden
by configuration)
The **servald** `start` command will create all sub-directories within the
standard FHS paths as needed, and any failure will cause the daemon not to
start.
The following build-time configuration options are available to alter the paths
described above, which can help adapt **servald** to systems which use a
different software installation convention (eg, all packages are installed
under `/opt/packagename`) or which have a volatile `/var` directory (eg, on
OpenWRT, `/var` is a symlink to `/tmp`):
* If **servald** is built with the `./configure --prefix=DIR` option, then
the executable is installed in `DIR/bin` instead of `/usr/local/bin`
* If **servald** is built with the `./configure --sysconfdir=DIR` option,
then it will use `DIR` in place of `/etc`
* If **servald** is built with the `./configure --localstatedir=DIR` option,
then it will use `DIR` in place of `/var`
* If **servald** is built with the `./configure SERVAL_ETC_PATH=DIR` option,
then it will use `DIR` in place of `/etc/serval`
* If **servald** is built with the `./configure SERVAL_RUN_PATH=DIR` option,
then it will use `DIR` in place of `/var/run/serval`
* If **servald** is built with the `./configure SYSTEM_LOG_PATH=DIR` option,
then it will use `DIR` in place of `/var/log`
* If **servald** is built with the `./configure SERVAL_LOG_PATH=DIR` option,
then it will use `DIR` in place of `/var/log/serval`
* If **servald** is built with the `./configure RHIZOME_STORE_PATH=DIR`
option, then it will use `DIR` in place of `/var/cache/serval` for the
Rhizome store
* If **servald** is built with the `./configure SERVAL_TMP_PATH=DIR` option,
then it will use `DIR` in place of `/tmp/serval`
The **servald** `config paths` command will display all the paths in use, based
on the built-in defaults as overridden by configuration settings and run-time
environment variables available to the command. This command will work even if
configuration is defective, so is a useful diagnostic tool.
Instance directory paths
------------------------
If **servald** is started with an *instance directory*, then all configuration,
state, and temporary files are stored in or beneath that directory, denoted
`IDIR`:
* the configuration is stored in `IDIR/serval.conf`
* the keyring is stored in `IDIR/serval.keyring`
* the PID file is `IDIR/servald.pid`
* the daemon creates its process information files under `IDIR/proc/`
* the daemon creates and rotates log files files under `IDIR/log/`
* the daemon creates and updates a symbolic link to the latest log file in
`IDIR/servald.log`
* the Rhizome store is in `IDIR/` (unless overridden by configuration):
* the SQLite database is `IDIR/rhizome.db`
* large payloads are stored under `IDIR/blob/`
* the SQLite temporary directory is `IDIR/sqlite3tmp/`
* dummy interface files are created under `IDIR/` (unless overridden by
configuration)
The **servald** `start` command will create its instance directory (and all
enclosing parent directories) if it does not already exist. Failure will cause
the daemon not to start.
About the examples
------------------
@ -300,8 +413,10 @@ All log destinations support the following configuration options:
In addition, the *file* destination has these extra configuration options:
* `log.file.directory_path` If set, log files are created in this directory,
which is created if it does not exist. This defaults to the `log`
directory within the instance directory.
which is created if it does not exist. Relative paths are interpreted
relative to the `log` sub-directory of the instance directory. The default
setting (empty string) causes log files to be created within the `log`
sub-directory of the instance directory.
* `log.file.path` If set, all log messages are appended directly to the file
at the given path. If the path is not absolute, it is interpreted relative

View File

@ -24,46 +24,207 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "strbuf.h"
#include "strbuf_helpers.h"
static char *thisinstancepath = NULL;
/*
* A default INSTANCE_PATH can be set on the ./configure command line, eg:
*
* ./configure INSTANCE_PATH=/var/local/serval/node
*
* This will cause servald to never use FHS paths, and always use an instance
* path, even if the SERVALINSTANCE_PATH environment variable is not set.
*/
#ifdef INSTANCE_PATH
#define DEFAULT_INSTANCE_PATH INSTANCE_PATH
#else
const char *serval_instancepath()
/* On Android systems, always use an instance path (no FHS paths).
*/
#ifdef ANDROID
#define DEFAULT_INSTANCE_PATH "/data/data/org.servalproject/var/serval-node"
#define SERVAL_ETC_PATH ""
#define SERVAL_RUN_PATH ""
#define SYSTEM_LOG_PATH ""
#define SERVAL_LOG_PATH ""
#define SERVAL_CACHE_PATH ""
#define SERVAL_TMP_PATH ""
#define RHIZOME_STORE_PATH ""
#else
#define DEFAULT_INSTANCE_PATH NULL
#endif
#endif
/* The following paths are based on the Filesystem Hierarchy Standard (FHS) 2.3
* but can be altered using ./configure arguments, eg:
*
* ./configure SERVAL_LOG_PATH=/tmp/serval/log
*/
#ifndef SERVAL_ETC_PATH
#define SERVAL_ETC_PATH SYSCONFDIR "/serval"
#endif
#ifndef SERVAL_RUN_PATH
#define SERVAL_RUN_PATH LOCALSTATEDIR "/run/serval"
#endif
#ifndef SYSTEM_LOG_PATH
#define SYSTEM_LOG_PATH LOCALSTATEDIR "/log"
#endif
#ifndef SERVAL_LOG_PATH
#define SERVAL_LOG_PATH SYSTEM_LOG_PATH "/serval"
#endif
#ifndef SERVAL_CACHE_PATH
#define SERVAL_CACHE_PATH LOCALSTATEDIR "/cache/serval"
#endif
#ifndef SERVAL_TMP_PATH
#define SERVAL_TMP_PATH "/tmp/serval"
#endif
#ifndef RHIZOME_STORE_PATH
#define RHIZOME_STORE_PATH SERVAL_CACHE_PATH
#endif
static int know_instancepath = 0;
static char *instancepath = NULL;
const char *instance_path()
{
if (thisinstancepath)
return thisinstancepath;
const char *instancepath = getenv("SERVALINSTANCE_PATH");
if (!instancepath)
instancepath = DEFAULT_INSTANCE_PATH;
if (!know_instancepath) {
instancepath = getenv("SERVALINSTANCE_PATH");
if (instancepath == NULL)
instancepath = DEFAULT_INSTANCE_PATH;
know_instancepath = 1;
}
return instancepath;
}
int formf_serval_instance_path(struct __sourceloc __whence, char *buf, size_t bufsiz, const char *fmt, ...)
static int vformf_path(struct __sourceloc __whence, strbuf b, const char *syspath, const char *fmt, va_list ap)
{
va_list ap;
va_start(ap, fmt);
int ret = vformf_serval_instance_path(__whence, buf, bufsiz, fmt, ap);
va_end(ap);
return ret;
}
int vformf_serval_instance_path(struct __sourceloc __whence, char *buf, size_t bufsiz, const char *fmt, va_list ap)
{
strbuf b = strbuf_local(buf, bufsiz);
strbuf_va_vprintf(b, fmt, ap);
if (!strbuf_overrun(b) && strbuf_len(b) && buf[0] != '/') {
strbuf_reset(b);
strbuf_puts(b, serval_instancepath());
strbuf_putc(b, '/');
if (fmt)
strbuf_va_vprintf(b, fmt, ap);
if (!strbuf_overrun(b) && (strbuf_len(b) == 0 || strbuf_str(b)[0] != '/')) {
strbuf_reset(b);
const char *ipath = instance_path();
#ifdef ANDROID
assert(ipath != NULL);
#endif
strbuf_puts(b, ipath ? ipath : syspath);
if (fmt) {
if (strbuf_substr(b, -1)[0] != '/')
strbuf_putc(b, '/');
strbuf_va_vprintf(b, fmt, ap);
}
}
if (!strbuf_overrun(b))
return 1;
WHYF("instance path overflow (strlen %lu, sizeof buffer %lu): %s",
(unsigned long)strbuf_count(b),
(unsigned long)bufsiz,
alloca_str_toprint(buf));
(unsigned long)strbuf_size(b),
alloca_str_toprint(strbuf_str(b)));
return 0;
}
int create_serval_instance_dir() {
return emkdirs(serval_instancepath(), 0700);
int _formf_serval_etc_path(struct __sourceloc __whence, char *buf, size_t bufsiz, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
int ret = vformf_path(__whence, strbuf_local(buf, bufsiz), SERVAL_ETC_PATH, fmt, ap);
va_end(ap);
return ret;
}
int _formf_serval_run_path(struct __sourceloc __whence, char *buf, size_t bufsiz, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
int ret = _vformf_serval_run_path(__whence, buf, bufsiz, fmt, ap);
va_end(ap);
return ret;
}
int _vformf_serval_run_path(struct __sourceloc __whence, char *buf, size_t bufsiz, const char *fmt, va_list ap)
{
return vformf_path(__whence, strbuf_local(buf, bufsiz), SERVAL_RUN_PATH, fmt, ap);
}
strbuf strbuf_system_log_path(strbuf sb)
{
const char *ipath = instance_path();
strbuf_puts(sb, ipath ? ipath : SYSTEM_LOG_PATH);
return sb;
}
strbuf strbuf_serval_log_path(strbuf sb)
{
const char *ipath = instance_path();
if (ipath)
strbuf_path_join(sb, ipath, "log", NULL);
else
strbuf_puts(sb, SERVAL_LOG_PATH);
return sb;
}
int _formf_serval_cache_path(struct __sourceloc __whence, char *buf, size_t bufsiz, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
int ret = vformf_path(__whence, strbuf_local(buf, bufsiz), SERVAL_CACHE_PATH, fmt, ap);
va_end(ap);
return ret;
}
int _formf_rhizome_store_path(struct __sourceloc __whence, char *buf, size_t bufsiz, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
int ret = vformf_path(__whence, strbuf_local(buf, bufsiz), RHIZOME_STORE_PATH, fmt, ap);
va_end(ap);
return ret;
}
int _formf_serval_tmp_path(struct __sourceloc __whence, char *buf, size_t bufsiz, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
int ret = vformf_path(__whence, strbuf_local(buf, bufsiz), SERVAL_TMP_PATH, fmt, ap);
va_end(ap);
return ret;
}
int _formf_servald_proc_path(struct __sourceloc __whence, char *buf, size_t bufsiz, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
int ret = vformf_path(__whence, strbuf_local(buf, bufsiz), SERVAL_RUN_PATH "/proc", fmt, ap);
va_end(ap);
return ret;
}
int create_serval_instance_dir()
{
int ret = 0;
char path[PATH_MAX];
if (FORMF_SERVAL_ETC_PATH(path, NULL) && emkdirs_info(path, 0755) == -1)
ret = -1;
if (FORMF_SERVAL_RUN_PATH(path, NULL) && emkdirs_info(path, 0700) == -1)
ret = -1;
if (FORMF_SERVAL_CACHE_PATH(path, NULL) && emkdirs_info(path, 0700) == -1)
ret = -1;
strbuf sb = strbuf_local(path, sizeof path);
strbuf_system_log_path(sb);
if (!strbuf_overrun(sb) && emkdirs_info(path, 0700) == -1)
ret = -1;
strbuf_reset(sb);
strbuf_serval_log_path(sb);
if (!strbuf_overrun(sb) && emkdirs_info(path, 0700) == -1)
ret = -1;
if (FORMF_SERVAL_TMP_PATH(path, NULL) && emkdirs_info(path, 0700) == -1)
ret = -1;
if (FORMF_SERVALD_PROC_PATH(path, NULL) && emkdirs_info(path, 0755) == -1)
ret = -1;
if (FORMF_RHIZOME_STORE_PATH(path, NULL) && emkdirs_info(path, 0700) == -1)
ret = -1;
return ret;
}

View File

@ -21,34 +21,40 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#define __SERVAL_DNA__INSTANCE_H
#include "log.h"
#include "strbuf.h"
/*
* A default INSTANCE_PATH can be set on the ./configure command line, eg:
*
* ./configure INSTANCE_PATH=/var/local/serval/node
*
* This will cause servald to never use FHS paths, and always use an instance
* path, even if the SERVALINSTANCE_PATH environment variable is not set.
*/
#ifdef INSTANCE_PATH
#define DEFAULT_INSTANCE_PATH INSTANCE_PATH
#else
#ifdef ANDROID
#define DEFAULT_INSTANCE_PATH "/data/data/org.servalproject/var/serval-node"
#else
#define DEFAULT_INSTANCE_PATH NULL
#endif
#endif
/* Handy statement for forming a path to an instance file in a char buffer whose declaration
* is in scope (so that sizeof(buf) will work). Evaluates to true if the pathname fitted into
* the provided buffer, false (0) otherwise (after logging an error).
*/
#define FORM_SERVAL_INSTANCE_PATH(buf, path) (formf_serval_instance_path(__WHENCE__, buf, sizeof(buf), "%s", (path)))
const char *serval_instancepath();
const char *instance_path(); // returns NULL if not using an instance path
int create_serval_instance_dir();
int formf_serval_instance_path(struct __sourceloc, char *buf, size_t bufsiz, const char *fmt, ...) __attribute__((format(printf,4,5)));
int vformf_serval_instance_path(struct __sourceloc, char *buf, size_t bufsiz, const char *fmt, va_list);
int _formf_serval_etc_path(struct __sourceloc, char *buf, size_t bufsiz, const char *fmt, ...) __attribute__((format(printf,4,5)));
int _formf_serval_run_path(struct __sourceloc, char *buf, size_t bufsiz, const char *fmt, ...) __attribute__((format(printf,4,5)));
int _vformf_serval_run_path(struct __sourceloc, char *buf, size_t bufsiz, const char *fmt, va_list);
int _formf_serval_cache_path(struct __sourceloc, char *buf, size_t bufsiz, const char *fmt, ...) __attribute__((format(printf,4,5)));
int _formf_serval_tmp_path(struct __sourceloc, char *buf, size_t bufsiz, const char *fmt, ...) __attribute__((format(printf,4,5)));
int _formf_servald_proc_path(struct __sourceloc, char *buf, size_t bufsiz, const char *fmt, ...) __attribute__((format(printf,4,5)));
int _formf_rhizome_store_path(struct __sourceloc, char *buf, size_t bufsiz, const char *fmt, ...) __attribute__((format(printf,4,5)));
#define formf_serval_etc_path(buf,bufsz,fmt,...) _formf_serval_etc_path(__WHENCE__, buf, bufsz, fmt, ##__VA_ARGS__)
#define formf_serval_run_path(buf,bufsz,fmt,...) _formf_serval_run_path(__WHENCE__, buf, bufsz, fmt, ##__VA_ARGS__)
#define vformf_serval_run_path(buf,bufsz,fmt,ap) _vformf_serval_run_path(__WHENCE__, buf, bufsz, fmt, ap)
#define formf_serval_cache_path(buf,bufsz,fmt,...) _formf_serval_cache_path(__WHENCE__, buf, bufsz, fmt, ##__VA_ARGS__)
#define formf_serval_tmp_path(buf,bufsz,fmt,...) _formf_serval_tmp_path(__WHENCE__, buf, bufsz, fmt, ##__VA_ARGS__)
#define formf_servald_proc_path(buf,bufsz,fmt,...) _formf_servald_proc_path(__WHENCE__, buf, bufsz, fmt, ##__VA_ARGS__)
#define formf_rhizome_store_path(buf,bufsz,fmt,...) _formf_rhizome_store_path(__WHENCE__, buf, bufsz, fmt, ##__VA_ARGS__)
/* Handy macros for forming the absolute paths of various files, using a char[]
* buffer whose declaration is in scope (so that sizeof(buf) will work).
* Evaluates to true if the pathname fits into the provided buffer, false (0)
* otherwise (after logging an error).
*/
#define FORMF_SERVAL_ETC_PATH(buf,fmt,...) formf_serval_etc_path(buf, sizeof(buf), fmt, ##__VA_ARGS__)
#define FORMF_SERVAL_RUN_PATH(buf,fmt,...) formf_serval_run_path(buf, sizeof(buf), fmt, ##__VA_ARGS__)
#define FORMF_SERVAL_CACHE_PATH(buf,fmt,...) formf_serval_cache_path(buf, sizeof(buf), fmt, ##__VA_ARGS__)
#define FORMF_SERVAL_TMP_PATH(buf,fmt,...) formf_serval_tmp_path(buf, sizeof(buf), fmt, ##__VA_ARGS__)
#define FORMF_SERVALD_PROC_PATH(buf,fmt,...) formf_servald_proc_path(buf, sizeof(buf), fmt, ##__VA_ARGS__)
#define FORMF_RHIZOME_STORE_PATH(buf,fmt,...) formf_rhizome_store_path((buf), sizeof(buf), (fmt), ##__VA_ARGS__)
strbuf strbuf_system_log_path(strbuf sb);
strbuf strbuf_serval_log_path(strbuf sb);
#endif // __SERVAL_DNA__INSTANCE_H

View File

@ -2020,7 +2020,7 @@ keyring_file *keyring_open_instance()
if (!env)
env = "serval.keyring";
char keyringFile[1024];
if (!FORM_SERVAL_INSTANCE_PATH(keyringFile, env))
if (!FORMF_SERVAL_ETC_PATH(keyringFile, "%s", env))
RETURN(NULL);
// Work out if the keyring file is writeable.
int writeable = 0;

60
log.c
View File

@ -95,9 +95,12 @@ static FILE *_log_file = NULL;
static void _open_log_file(_log_iterator *);
static void _rotate_log_file(_log_iterator *it);
static void _flush_log_file();
struct _log_state state_file;
struct config_log_format config_file;
time_t _log_file_start_time;
static struct _log_state state_file;
static struct config_log_format config_file;
static struct { size_t len; mode_t mode; } mkdir_trace[10];
static mode_t mkdir_trace_latest_mode;
static unsigned mkdir_count;
static time_t _log_file_start_time;
static char _log_file_buf[8192];
static struct strbuf _log_file_strbuf = STRUCT_STRBUF_EMPTY;
@ -424,11 +427,6 @@ static void _logs_printf_nl(int level, struct __sourceloc whence, const char *fm
va_end(ap);
}
const char *log_file_directory_path()
{
return config.log.file.directory_path;
}
static void _compute_file_start_time(_log_iterator *it)
{
if (it->file_start_time == 0) {
@ -440,6 +438,27 @@ static void _compute_file_start_time(_log_iterator *it)
}
}
static void trace_mkdir(struct __sourceloc UNUSED(whence), const char *path, mode_t mode)
{
if (mkdir_count < NELS(mkdir_trace)) {
mkdir_trace[mkdir_count].len = strlen(path);
mkdir_trace[mkdir_count].mode = mode;
}
++mkdir_count;
mkdir_trace_latest_mode = mode;
}
static void log_mkdir_trace(const char *dir)
{
unsigned i;
for (i = 0; i < mkdir_count && i < NELS(mkdir_trace); ++i)
_logs_printf_nl(LOG_LEVEL_INFO, __NOWHERE__, "Created %s (mode %04o)", alloca_toprint(-1, dir, mkdir_trace[i].len), mkdir_trace[i].mode);
if (mkdir_count > NELS(mkdir_trace) + 1)
_logs_printf_nl(LOG_LEVEL_INFO, __NOWHERE__, "Created ...");
if (mkdir_count > NELS(mkdir_trace))
_logs_printf_nl(LOG_LEVEL_INFO, __NOWHERE__, "Created %s (mode %04o)", alloca_str_toprint(dir), mkdir_trace_latest_mode);
}
static void _open_log_file(_log_iterator *it)
{
assert(it->state == &state_file);
@ -448,14 +467,15 @@ static void _open_log_file(_log_iterator *it)
_log_file_path = getenv("SERVALD_LOG_FILE");
if (_log_file_path == NULL && !cf_limbo) {
strbuf sbfile = strbuf_local(_log_file_path_buf, sizeof _log_file_path_buf);
strbuf_path_join(sbfile, serval_instancepath(), log_file_directory_path(), NULL);
strbuf_serval_log_path(sbfile);
strbuf_path_join(sbfile, config.log.file.directory_path, "", NULL); // with trailing '/'
_compute_file_start_time(it);
if (config.log.file.path[0]) {
strbuf_path_join(sbfile, config.log.file.path, NULL);
} else {
struct tm tm;
(void)localtime_r(&it->file_start_time, &tm);
strbuf_append_strftime(sbfile, "/serval-%Y%m%d%H%M%S.log", &tm);
strbuf_append_strftime(sbfile, "serval-%Y%m%d%H%M%S.log", &tm);
}
if (strbuf_overrun(sbfile)) {
_log_file = NO_FILE;
@ -477,16 +497,27 @@ static void _open_log_file(_log_iterator *it)
char _dir[dirsiz];
strcpy(_dir, _log_file_path);
const char *dir = dirname(_dir); // modifies _dir[]
if (mkdirs(dir, 0700) != -1 && (_log_file = fopen(_log_file_path, "a"))) {
mkdir_count = 0;
if (mkdirs_log(dir, 0700, trace_mkdir) == -1) {
_log_file = NO_FILE;
log_mkdir_trace(dir);
_logs_printf_nl(LOG_LEVEL_WARN, __HERE__, "Cannot mkdir %s - %s [errno=%d]", alloca_str_toprint(dir), strerror(errno), errno);
} else if ((_log_file = fopen(_log_file_path, "a")) == NULL) {
_log_file = NO_FILE;
log_mkdir_trace(dir);
_logs_printf_nl(LOG_LEVEL_WARN, __HERE__, "Cannot create-append %s - %s [errno=%d]", _log_file_path, strerror(errno), errno);
} else {
setlinebuf(_log_file);
memset(it->state, 0, sizeof *it->state);
// The first line in every log file must be the starting time stamp. (After that, it is up
// to _log_update() to insert other mandatory messages in any suitable order.)
_log_current_datetime(it, LOG_LEVEL_INFO);
log_mkdir_trace(dir);
_logs_printf_nl(LOG_LEVEL_INFO, __NOWHERE__, "Logging to %s (fd %d)", _log_file_path, fileno(_log_file));
// Update the log symlink to point to the latest log file.
strbuf sbsymlink = strbuf_alloca(400);
strbuf_path_join(sbsymlink, serval_instancepath(), "serval.log", NULL);
strbuf_system_log_path(sbsymlink);
strbuf_path_join(sbsymlink, "serval.log", NULL);
if (strbuf_overrun(sbsymlink))
_logs_printf_nl(LOG_LEVEL_ERROR, __HERE__, "Cannot form log symlink name - buffer overrun");
else {
@ -556,9 +587,6 @@ static void _open_log_file(_log_iterator *it)
_logs_printf_nl(LOG_LEVEL_INFO, __NOWHERE__, "Unlink %s", path);
unlink(path);
}
} else {
_log_file = NO_FILE;
_logs_printf_nl(LOG_LEVEL_WARN, __HERE__, "Cannot create/append %s - %s [errno=%d]", _log_file_path, strerror(errno), errno);
}
}
}
@ -741,7 +769,7 @@ int log_backtrace(int level, struct __sourceloc whence)
if (get_self_executable_path(execpath, sizeof execpath) == -1)
return WHY("cannot log backtrace: own executable path unknown");
char tempfile[MAXPATHLEN];
if (!FORM_SERVAL_INSTANCE_PATH(tempfile, "servalgdb.XXXXXX"))
if (!FORMF_SERVAL_TMP_PATH(tempfile, "servalgdb.XXXXXX"))
return -1;
int tmpfd = mkstemp(tempfile);
if (tmpfd == -1)

1
log.h
View File

@ -92,7 +92,6 @@ extern const struct __sourceloc __whence; // see above
extern int serverMode;
const char *log_file_directory_path();
int create_log_file_directory();
void close_log_file();

43
os.c
View File

@ -18,6 +18,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#define __SERVAL_DNA__OS_INLINE
#include "constants.h"
#include "os.h"
#include "str.h"
#include "log.h"
@ -31,39 +32,52 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include <time.h>
#include <string.h>
int mkdirs(const char *path, mode_t mode)
void log_info_mkdir(struct __sourceloc __whence, const char *path, mode_t mode)
{
return mkdirsn(path, strlen(path), mode);
INFOF("mkdir %s (mode %04o)", alloca_str_toprint(path), mode);
}
int _emkdirs(struct __sourceloc __whence, const char *path, mode_t mode)
int _mkdirs(struct __sourceloc __whence, const char *path, mode_t mode, MKDIR_LOG_FUNC *logger)
{
if (mkdirs(path, mode) == -1)
return _mkdirsn(__whence, path, strlen(path), mode, logger);
}
int _emkdirs(struct __sourceloc __whence, const char *path, mode_t mode, MKDIR_LOG_FUNC *logger)
{
if (_mkdirs(__whence, path, mode, logger) == -1)
return WHYF_perror("mkdirs(%s,%o)", alloca_str_toprint(path), mode);
return 0;
}
int _emkdirsn(struct __sourceloc __whence, const char *path, size_t len, mode_t mode)
int _emkdirsn(struct __sourceloc __whence, const char *path, size_t len, mode_t mode, MKDIR_LOG_FUNC *logger)
{
if (mkdirsn(path, len, mode) == -1)
if (_mkdirsn(__whence, path, len, mode, logger) == -1)
return WHYF_perror("mkdirsn(%s,%lu,%o)", alloca_toprint(-1, path, len), (unsigned long)len, mode);
return 0;
}
/* This variant must not log anything, because it is used by the logging subsystem itself!
/* This variant must not log anything itself, because it is called by the logging subsystem, and
* that would cause infinite recursion!
*
* The path need not be NUL terminated.
*
* The logger function pointer is usually NULL, for no logging, but may be any function the caller
* supplies (for example, log_info_mkdir).
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
int mkdirsn(const char *path, size_t len, mode_t mode)
int _mkdirsn(struct __sourceloc whence, const char *path, size_t len, mode_t mode, MKDIR_LOG_FUNC *logger)
{
if (len == 0)
errno = EINVAL;
else {
char *pathfrag = alloca(len + 1);
strncpy(pathfrag, path, len);
pathfrag[len] = '\0';
if (mkdir(pathfrag, mode) != -1)
strncpy(pathfrag, path, len)[len] = '\0';
if (mkdir(pathfrag, mode) != -1) {
if (logger)
logger(whence, pathfrag, mode);
return 0;
}
if (errno == EEXIST) {
DIR *d = opendir(pathfrag);
if (d) {
@ -78,10 +92,13 @@ int mkdirsn(const char *path, size_t len, mode_t mode)
while (lastsep != path && *--lastsep == '/')
;
if (lastsep != path) {
if (mkdirsn(path, lastsep - path + 1, mode) == -1)
if (_mkdirsn(whence, path, lastsep - path + 1, mode, logger) == -1)
return -1;
if (mkdir(pathfrag, mode) != -1)
if (mkdir(pathfrag, mode) != -1) {
if (logger)
logger(whence, pathfrag, mode);
return 0;
}
}
}
}

26
os.h
View File

@ -103,13 +103,27 @@ __SERVAL_DNA__OS_INLINE off64_t lseek64(int fd, off64_t offset, int whence) {
/* The "e" variants log the error before returning -1.
*/
int mkdirs(const char *path, mode_t mode);
int mkdirsn(const char *path, size_t len, mode_t mode);
int _emkdirs(struct __sourceloc, const char *path, mode_t mode);
int _emkdirsn(struct __sourceloc, const char *path, size_t len, mode_t mode);
typedef void MKDIR_LOG_FUNC(struct __sourceloc, const char *, mode_t);
MKDIR_LOG_FUNC log_info_mkdir;
int _mkdirs(struct __sourceloc, const char *path, mode_t mode, MKDIR_LOG_FUNC *);
int _mkdirsn(struct __sourceloc, const char *path, size_t len, mode_t mode, MKDIR_LOG_FUNC *);
int _emkdirs(struct __sourceloc, const char *path, mode_t mode, MKDIR_LOG_FUNC *);
int _emkdirsn(struct __sourceloc, const char *path, size_t len, mode_t mode, MKDIR_LOG_FUNC *);
#define emkdirs(path, mode) _emkdirs(__WHENCE__, (path), (mode))
#define emkdirsn(path, len, mode) _emkdirsn(__WHENCE__, (path), (len), (mode))
#define mkdirs_log(path, mode, func) _mkdirs(__WHENCE__, (path), (mode), (func))
#define mkdirsn_log(path, len, mode, func) _mkdirsn(__WHENCE__, (path), (len), (mode), (func))
#define emkdirs_log(path, mode, func) _emkdirs(__WHENCE__, (path), (mode), (func))
#define emkdirsn_log(path, len, mode, func) _emkdirsn(__WHENCE__, (path), (len), (mode), (func))
#define mkdirs(path, mode) mkdirs_log((path), (mode), NULL)
#define mkdirsn(path, len, mode) mkdirsn_log((path), (len), (mode), NULL)
#define emkdirs(path, mode) emkdirs_log((path), (mode), NULL)
#define emkdirsn(path, len, mode) emkdirsn_log((path), (len), (mode), NULL)
#define mkdirs_info(path, mode) mkdirs_log((path), (mode), log_info_mkdir)
#define mkdirsn_info(path, len, mode) mkdirsn_log((path), (len), (mode), log_info_mkdir)
#define emkdirs_info(path, mode) emkdirs_log((path), (mode), log_info_mkdir)
#define emkdirsn_info(path, len, mode) emkdirsn_log((path), (len), (mode), log_info_mkdir)
void srandomdev();
int urandombytes(unsigned char *buf, size_t len);

View File

@ -497,14 +497,9 @@ overlay_interface_init(const char *name, struct socket_address *addr,
return WHY("overlay_interface_init_socket() failed");
}else{
char read_file[1024];
interface->local_echo = interface->point_to_point?0:1;
strbuf d = strbuf_local(read_file, sizeof read_file);
strbuf_path_join(d, serval_instancepath(), config.server.interface_path, ifconfig->file, NULL);
if (strbuf_overrun(d))
return WHYF("interface file name overrun: %s", alloca_str_toprint(strbuf_str(d)));
if (!FORMF_SERVAL_TMP_PATH(read_file, "%s/%s", config.server.interface_path, ifconfig->file))
return -1;
if ((interface->alarm.poll.fd = open(read_file, O_APPEND|O_RDWR)) == -1) {
if (errno == ENOENT && ifconfig->socket_type == SOCK_FILE) {
cleanup_ret = 1;
@ -1071,10 +1066,7 @@ void overlay_interface_discover(struct sched_ent *alarm)
{
// use a local dgram socket
// no abstract sockets for now
strbuf d = strbuf_local(addr.local.sun_path, sizeof addr.local.sun_path);
strbuf_path_join(d, serval_instancepath(), config.server.interface_path, ifconfig->file, NULL);
if (strbuf_overrun(d)){
WHYF("interface file name overrun: %s", alloca_str_toprint(strbuf_str(d)));
if (!FORMF_SERVAL_RUN_PATH(addr.local.sun_path, "%s/%s", config.server.interface_path, ifconfig->file)) {
// TODO set ifconfig->exclude to prevent spam??
break;
}

View File

@ -97,26 +97,28 @@ static int mdp_send2(const struct socket_address *client, const struct mdp_heade
/* Delete all UNIX socket files in instance directory. */
void overlay_mdp_clean_socket_files()
{
const char *instance_path = serval_instancepath();
DIR *dir;
struct dirent *dp;
if ((dir = opendir(instance_path)) == NULL) {
WARNF_perror("opendir(%s)", alloca_str_toprint(instance_path));
return;
}
while ((dp = readdir(dir)) != NULL) {
char path[PATH_MAX];
if (!FORM_SERVAL_INSTANCE_PATH(path, dp->d_name))
continue;
struct stat st;
if (lstat(path, &st)) {
WARNF_perror("stat(%s)", alloca_str_toprint(path));
continue;
char path[PATH_MAX];
if (FORMF_SERVAL_RUN_PATH(path, NULL)) {
DIR *dir;
struct dirent *dp;
if ((dir = opendir(path)) == NULL) {
WARNF_perror("opendir(%s)", alloca_str_toprint(path));
return;
}
if (S_ISSOCK(st.st_mode))
unlink(path);
while ((dp = readdir(dir)) != NULL) {
path[0] = '\0';
if (!FORMF_SERVAL_RUN_PATH(path, "%s", dp->d_name))
continue;
struct stat st;
if (lstat(path, &st)) {
WARNF_perror("stat(%s)", alloca_str_toprint(path));
continue;
}
if (S_ISSOCK(st.st_mode))
unlink(path);
}
closedir(dir);
}
closedir(dir);
}
static void overlay_mdp_fill_legacy(

View File

@ -395,16 +395,6 @@ int rhizome_configure();
int rhizome_enabled();
int rhizome_fetch_delay_ms();
int rhizome_set_datastore_path(const char *path);
const char *rhizome_datastore_path();
int form_rhizome_datastore_path(struct __sourceloc, char * buf, size_t bufsiz, const char *fmt, ...);
/* Handy statement for forming the path of a rhizome store file in a char buffer whose declaration
* is in scope (so that sizeof(buf) will work). Evaluates to true if the pathname fitted into
* the provided buffer, false (0) otherwise (after logging an error). */
#define FORM_RHIZOME_DATASTORE_PATH(buf,fmt,...) (form_rhizome_datastore_path(__WHENCE__, (buf), sizeof(buf), (fmt), ##__VA_ARGS__))
#define RHIZOME_BLOB_SUBDIR "blob"
extern sqlite3 *rhizome_db;

View File

@ -30,51 +30,19 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "str.h"
#include "keyring.h"
static char rhizome_thisdatastore_path[256];
static int rhizome_delete_manifest_retry(sqlite_retry_state *retry, const rhizome_bid_t *bidp);
static int rhizome_delete_file_retry(sqlite_retry_state *retry, const rhizome_filehash_t *hashp);
static int rhizome_delete_payload_retry(sqlite_retry_state *retry, const rhizome_bid_t *bidp);
const char *rhizome_datastore_path()
{
if (!rhizome_thisdatastore_path[0])
rhizome_set_datastore_path(NULL);
return rhizome_thisdatastore_path;
}
int rhizome_set_datastore_path(const char *path)
{
strbuf b = strbuf_local(rhizome_thisdatastore_path, sizeof rhizome_thisdatastore_path);
strbuf_path_join(b, serval_instancepath(), config.rhizome.datastore_path, path, NULL);
INFOF("Rhizome datastore path = %s", alloca_str_toprint(rhizome_thisdatastore_path));
return 0;
}
int form_rhizome_datastore_path(struct __sourceloc __whence, char * buf, size_t bufsiz, const char *fmt, ...)
{
va_list ap;
strbuf b = strbuf_local(buf, bufsiz);
strbuf_puts(b, rhizome_datastore_path());
if (fmt) {
va_start(ap, fmt);
if (*strbuf_substr(b, -1) != '/')
strbuf_putc(b, '/');
strbuf_vsprintf(b, fmt, ap);
va_end(ap);
}
if (strbuf_overrun(b)) {
WHY("Path buffer overrun");
return 0;
}
return 1;
}
static int create_rhizome_datastore_dir()
static int create_rhizome_store_dir()
{
char rdpath[1024];
if (!formf_rhizome_store_path(rdpath, sizeof rdpath, "%s", config.rhizome.datastore_path))
return -1;
INFOF("Rhizome datastore path = %s", alloca_str_toprint(rdpath));
if (config.debug.rhizome)
DEBUGF("mkdirs(%s, 0700)", rhizome_datastore_path());
return emkdirs(rhizome_datastore_path(), 0700);
DEBUGF("mkdirs(%s, 0700)", alloca_str_toprint(rdpath));
return emkdirs_info(rdpath, 0700);
}
sqlite3 *rhizome_db = NULL;
@ -239,23 +207,23 @@ int rhizome_opendb()
IN();
if (create_rhizome_datastore_dir() == -1)
if (create_rhizome_store_dir() == -1)
RETURN(-1);
char dbpath[1024];
if (!FORM_RHIZOME_DATASTORE_PATH(dbpath, RHIZOME_BLOB_SUBDIR))
if (!FORMF_RHIZOME_STORE_PATH(dbpath, RHIZOME_BLOB_SUBDIR))
RETURN(-1);
if (emkdirs(dbpath, 0700) == -1)
if (emkdirs_info(dbpath, 0700) == -1)
RETURN(-1);
if (!sqlite3_temp_directory) {
if (!FORM_RHIZOME_DATASTORE_PATH(dbpath, "sqlite3tmp"))
if (!FORMF_RHIZOME_STORE_PATH(dbpath, "sqlite3tmp"))
RETURN(-1);
if (emkdirs(dbpath, 0700) == -1)
if (emkdirs_info(dbpath, 0700) == -1)
RETURN(-1);
sqlite3_temp_directory = sqlite3_mprintf("%s", dbpath);
}
sqlite3_config(SQLITE_CONFIG_LOG,sqlite_log,NULL);
if (!FORM_RHIZOME_DATASTORE_PATH(dbpath, "rhizome.db"))
if (!FORMF_RHIZOME_STORE_PATH(dbpath, "rhizome.db"))
RETURN(-1);
if (sqlite3_open(dbpath,&rhizome_db)){
RETURN(WHYF("SQLite could not open database %s: %s", dbpath, sqlite3_errmsg(rhizome_db)));
@ -1191,7 +1159,7 @@ static int rhizome_delete_external(const char *id)
{
// attempt to remove any external blob
char blob_path[1024];
if (!FORM_RHIZOME_DATASTORE_PATH(blob_path, "%s/%s", RHIZOME_BLOB_SUBDIR, id))
if (!FORMF_RHIZOME_STORE_PATH(blob_path, "%s/%s", RHIZOME_BLOB_SUBDIR, id))
return -1;
if (unlink(blob_path) == -1) {
if (errno != ENOENT)

View File

@ -204,9 +204,7 @@ static int rhizome_direct_addfile_end(struct http_request *hr)
char manifestTemplate[1024];
manifestTemplate[0] = '\0';
if (config.rhizome.api.addfile.manifest_template_file[0]) {
strbuf b = strbuf_local(manifestTemplate, sizeof manifestTemplate);
strbuf_path_join(b, serval_instancepath(), config.rhizome.api.addfile.manifest_template_file, NULL);
if (strbuf_overrun(b)) {
if (!FORMF_SERVAL_ETC_PATH(manifestTemplate, "%s", config.rhizome.api.addfile.manifest_template_file)) {
rhizome_direct_clear_temporary_files(r);
http_request_simple_response(&r->http, 500, "Internal Error: Template path too long");
return 0;

View File

@ -105,7 +105,7 @@ enum rhizome_payload_status rhizome_open_write(struct rhizome_write *write, cons
}
char blob_path[1024];
if (file_length == RHIZOME_SIZE_UNSET || file_length > config.rhizome.max_blob_size) {
if (!FORM_RHIZOME_DATASTORE_PATH(blob_path, "%s/%"PRIu64, RHIZOME_BLOB_SUBDIR, write->temp_id))
if (!FORMF_RHIZOME_STORE_PATH(blob_path, "%s/%"PRIu64, RHIZOME_BLOB_SUBDIR, write->temp_id))
return RHIZOME_PAYLOAD_STATUS_ERROR;
if (config.debug.rhizome_store)
DEBUGF("Attempting to put blob for id='%"PRIu64"' in %s", write->temp_id, blob_path);
@ -473,7 +473,7 @@ enum rhizome_payload_status rhizome_finish_write(struct rhizome_write *write)
SHA512_End(&write->sha512_context, NULL);
char blob_path[1024];
if (!FORM_RHIZOME_DATASTORE_PATH(blob_path, "%s/%"PRIu64, RHIZOME_BLOB_SUBDIR, write->temp_id)) {
if (!FORMF_RHIZOME_STORE_PATH(blob_path, "%s/%"PRIu64, RHIZOME_BLOB_SUBDIR, write->temp_id)) {
WHYF("Failed to generate external blob path");
status = RHIZOME_PAYLOAD_STATUS_ERROR;
goto failure;
@ -549,7 +549,7 @@ enum rhizome_payload_status rhizome_finish_write(struct rhizome_write *write)
if (external) {
char dest_path[1024];
if (!FORM_RHIZOME_DATASTORE_PATH(dest_path, "%s/%s", RHIZOME_BLOB_SUBDIR, alloca_tohex_rhizome_filehash_t(write->id)))
if (!FORMF_RHIZOME_STORE_PATH(dest_path, "%s/%s", RHIZOME_BLOB_SUBDIR, alloca_tohex_rhizome_filehash_t(write->id)))
goto dbfailure;
if (rename(blob_path, dest_path) == -1) {
WHYF_perror("rename(%s, %s)", blob_path, dest_path);
@ -776,7 +776,7 @@ enum rhizome_payload_status rhizome_open_read(struct rhizome_read *read, const r
} else {
// No row in FILEBLOBS, look for an external blob file.
char blob_path[1024];
if (!FORM_RHIZOME_DATASTORE_PATH(blob_path, "%s/%s", RHIZOME_BLOB_SUBDIR, alloca_tohex_rhizome_filehash_t(read->id)))
if (!FORMF_RHIZOME_STORE_PATH(blob_path, "%s/%s", RHIZOME_BLOB_SUBDIR, alloca_tohex_rhizome_filehash_t(read->id)))
return RHIZOME_PAYLOAD_STATUS_ERROR;
read->blob_fd = open(blob_path, O_RDONLY);
if (read->blob_fd == -1) {

View File

@ -206,7 +206,6 @@ extern char *gatewayspec;
int rhizome_enabled();
int rhizome_http_server_running();
const char *rhizome_datastore_path();
#define MAX_PEERS 1024
extern int peer_count;
@ -279,6 +278,8 @@ struct slip_decode_state{
int server_pid();
const char *_server_pidfile_path(struct __sourceloc);
#define server_pidfile_path() (_server_pidfile_path(__WHENCE__))
void server_save_argv(int argc, const char *const *argv);
int server(const struct cli_parsed *parsed);
int server_write_pid();

105
server.c
View File

@ -31,9 +31,12 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "strbuf_helpers.h"
#include "overlay_interface.h"
#define PROC_SUBDIR "proc"
#define PIDFILE_NAME "servald.pid"
#define STOPFILE_NAME "servald.stop"
static char pidfile_path[256];
#define EXEC_NARGS 20
char *exec_args[EXEC_NARGS + 1];
unsigned exec_argc = 0;
@ -49,31 +52,45 @@ void crash_handler(int signal);
*/
int server_pid()
{
const char *instancepath = serval_instancepath();
struct stat st;
if (stat(instancepath, &st) == -1)
return WHYF_perror("stat(%s)", alloca_str_toprint(instancepath));
if ((st.st_mode & S_IFMT) != S_IFDIR)
return WHYF("Instance path '%s' is not a directory", instancepath);
char filename[1024];
if (!FORM_SERVAL_INSTANCE_PATH(filename, PIDFILE_NAME))
char dirname[1024];
if (!FORMF_SERVAL_RUN_PATH(dirname, NULL))
return -1;
FILE *f = fopen(filename, "r");
struct stat st;
if (stat(dirname, &st) == -1)
return WHYF_perror("stat(%s)", alloca_str_toprint(dirname));
if ((st.st_mode & S_IFMT) != S_IFDIR)
return WHYF("Not a directory: %s", dirname);
const char *ppath = server_pidfile_path();
if (ppath == NULL)
return -1;
const char *p = strrchr(ppath, '/');
assert(p != NULL);
FILE *f = fopen(ppath, "r");
if (f == NULL) {
if (errno != ENOENT)
return WHYF_perror("fopen(%s,\"r\")", alloca_str_toprint(filename));
return WHYF_perror("fopen(%s,\"r\")", alloca_str_toprint(ppath));
} else {
char buf[20];
int pid = (fgets(buf, sizeof buf, f) != NULL) ? atoi(buf) : -1;
fclose(f);
if (pid > 0 && kill(pid, 0) != -1)
return pid;
INFOF("Unlinking stale pidfile %s", filename);
unlink(filename);
INFOF("Unlinking stale pidfile %s", ppath);
unlink(ppath);
}
return 0;
}
const char *_server_pidfile_path(struct __sourceloc __whence)
{
if (!pidfile_path[0]) {
if (!FORMF_SERVAL_RUN_PATH(pidfile_path, PIDFILE_NAME))
return NULL;
}
return pidfile_path;
}
void server_save_argv(int argc, const char *const *argv)
{
/* Save our argv[] to use for relaunching */
@ -123,26 +140,22 @@ int server(const struct cli_parsed *parsed)
int server_write_pid()
{
/* Record PID to advertise that the server is now running */
char filename[1024];
if (!FORM_SERVAL_INSTANCE_PATH(filename, PIDFILE_NAME))
const char *ppath = server_pidfile_path();
if (ppath == NULL)
return -1;
FILE *f=fopen(filename,"w");
if (!f) {
WHY_perror("fopen");
return WHYF("Could not write to PID file %s", filename);
}
FILE *f = fopen(ppath, "w");
if (!f)
return WHYF_perror("fopen(%s,\"w\")", alloca_str_toprint(ppath));
server_getpid = getpid();
fprintf(f,"%d\n", server_getpid);
fclose(f);
return 0;
}
static int get_proc_path(const char *path, char *buff, size_t buff_len)
static int get_proc_path(const char *path, char *buf, size_t bufsiz)
{
strbuf sbname = strbuf_local(buff, buff_len);
strbuf_path_join(sbname, serval_instancepath(), "proc", path, NULL);
if (strbuf_overrun(sbname))
return WHY("Buffer overrun building proc filename");
if (!formf_serval_run_path(buf, bufsiz, PROC_SUBDIR "/%s", path))
return -1;
return 0;
}
@ -156,7 +169,7 @@ int server_write_proc_state(const char *path, const char *fmt, ...)
char dir_buf[dirsiz];
strcpy(dir_buf, path_buf);
const char *dir = dirname(dir_buf); // modifies dir_buf[]
if (mkdirs(dir, 0700) == -1)
if (mkdirs_info(dir, 0700) == -1)
return WHY_perror("mkdirs()");
FILE *f = fopen(path_buf, "w");
@ -249,7 +262,7 @@ void server_shutdown_check(struct sched_ent *alarm)
int server_create_stopfile()
{
char stopfile[1024];
if (!FORM_SERVAL_INSTANCE_PATH(stopfile, STOPFILE_NAME))
if (!FORMF_SERVAL_RUN_PATH(stopfile, STOPFILE_NAME))
return -1;
FILE *f;
if ((f = fopen(stopfile, "w")) == NULL) {
@ -263,7 +276,7 @@ int server_create_stopfile()
int server_remove_stopfile()
{
char stopfile[1024];
if (!FORM_SERVAL_INSTANCE_PATH(stopfile, STOPFILE_NAME))
if (!FORMF_SERVAL_RUN_PATH(stopfile, STOPFILE_NAME))
return -1;
if (unlink(stopfile) == -1) {
if (errno == ENOENT)
@ -277,7 +290,7 @@ int server_remove_stopfile()
int server_check_stopfile()
{
char stopfile[1024];
if (!FORM_SERVAL_INSTANCE_PATH(stopfile, STOPFILE_NAME))
if (!FORMF_SERVAL_RUN_PATH(stopfile, STOPFILE_NAME))
return -1;
int r = access(stopfile, F_OK);
if (r == 0)
@ -292,27 +305,23 @@ int server_check_stopfile()
static void clean_proc()
{
char path_buf[400];
strbuf sbname = strbuf_local(path_buf, sizeof path_buf);
strbuf_path_join(sbname, serval_instancepath(), "proc", NULL);
DIR *dir;
struct dirent *dp;
if ((dir = opendir(path_buf)) == NULL) {
WARNF_perror("opendir(%s)", alloca_str_toprint(path_buf));
return;
}
while ((dp = readdir(dir)) != NULL) {
strbuf_reset(sbname);
strbuf_path_join(sbname, serval_instancepath(), "proc", dp->d_name, NULL);
struct stat st;
if (lstat(path_buf, &st)) {
WARNF_perror("stat(%s)", path_buf);
continue;
if (FORMF_SERVAL_RUN_PATH(path_buf, PROC_SUBDIR)) {
DIR *dir;
struct dirent *dp;
if ((dir = opendir(path_buf)) == NULL) {
WARNF_perror("opendir(%s)", alloca_str_toprint(path_buf));
return;
}
if (S_ISREG(st.st_mode))
unlink(path_buf);
while ((dp = readdir(dir)) != NULL) {
if (FORMF_SERVAL_RUN_PATH(path_buf, PROC_SUBDIR "/%s", dp->d_name)) {
struct stat st;
if (lstat(path_buf, &st) == -1)
WARNF_perror("stat(%s)", path_buf);
else if (S_ISREG(st.st_mode))
unlink(path_buf);
}
}
closedir(dir);
}
}

View File

@ -27,9 +27,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "strbuf_helpers.h"
#include "socket.h"
/* Form the name of an AF_UNIX (local) socket in the instance directory as an absolute path.
* Under Linux, this will create a socket name in the abstract namespace. This permits us to use
* local sockets on Android despite its lack of a shared writeable directory on a UFS partition.
/* Form the name of an AF_UNIX (local) socket in the /var/run/serval (or instance) directory as an
* absolute path. Under Linux, this will create a socket name in the abstract namespace. This
* permits us to use local sockets on Android despite its lack of a shared writeable directory on a
* UFS partition.
*
* The absolute file name is resolved to its real path using realpath(3), to ensure that name
* comparisons of addresses returned by recvmsg(2) can reliably be used on systems where the
@ -47,7 +48,7 @@ int _make_local_sockaddr(struct __sourceloc __whence, struct socket_address *add
addr->local.sun_family = AF_UNIX;
va_list ap;
va_start(ap, fmt);
int r = vformf_serval_instance_path(__WHENCE__, addr->local.sun_path, sizeof addr->local.sun_path, fmt, ap);
int r = vformf_serval_run_path(addr->local.sun_path, sizeof addr->local.sun_path, fmt, ap);
va_end(ap);
if (!r)
return WHY("socket name overflow");

View File

@ -172,7 +172,7 @@ doc_LogFileDirectoryRelative="Relative log file directory path"
test_LogFileDirectoryRelative() {
executeOk_servald config \
set debug.verbose true \
set log.file.directory_path "blah"
set log.file.directory_path "../blah"
executeOk_servald echo one
assert --message="exactly one log file" [ $(ls -d "$SERVALINSTANCE_PATH/blah/"*.log | wc -l) -eq 1 ]
assertGrep "$SERVALINSTANCE_PATH/blah/"*.log '^DEBUG:.*echo:argv\[1\]="one"$'