Add server watchdog and config reload test

This commit is contained in:
Andrew Bettison 2014-04-16 18:06:27 +09:30
parent c7d6ce71d5
commit 2716228074
5 changed files with 142 additions and 9 deletions

View File

@ -279,6 +279,7 @@ ATOM(bool_t, vomp, 0, boolean,, "")
ATOM(bool_t, trace, 0, boolean,, "")
ATOM(bool_t, profiling, 0, boolean,, "")
ATOM(bool_t, linkstate, 0, boolean,, "")
ATOM(bool_t, watchdog, 0, boolean,, "")
END_STRUCT
#define LOG_FORMAT_OPTIONS \
@ -320,10 +321,16 @@ STRUCT_DEFAULT(log_format, android)
ATOM_DEFAULT(show_pid, 0)
END_STRUCT_DEFAULT
STRUCT(watchdog)
STRING(256, executable, "", absolute_path,, "Absolute path of watchdog executable")
ATOM(uint32_t, interval_ms, 60000, uint32_nonzero,, "Time interval between watchdog invocations, in milliseconds")
END_STRUCT
STRUCT(server)
STRING(256, chdir, "/", absolute_path,, "Absolute path of chdir(2) for server process")
STRING(256, interface_path, "", str_nonempty,, "Path of directory containing interface files, either absolute or relative to instance directory")
ATOM(bool_t, respawn_on_crash, 0, boolean,, "If true, server will exec(2) itself on fatal signals, eg SEGV")
SUB_STRUCT(watchdog, watchdog,)
END_STRUCT
STRUCT(monitor)

View File

@ -112,14 +112,15 @@ int overlayServerMode()
and smaller values would affect CPU and energy use, and make the simulation less realistic. */
#define SCHEDULE(X, Y, D) { \
static struct profile_total _stats_##X={.name="" #X "",}; \
static struct sched_ent _sched_##X={\
.stats = &_stats_##X, \
.function=X,\
}; \
_sched_##X.alarm=(gettime_ms()+Y);\
_sched_##X.deadline=(gettime_ms()+Y+D);\
schedule(&_sched_##X); }
static struct profile_total _stats_##X = {.name="" #X "",}; \
static struct sched_ent _sched_##X = { \
.stats = &_stats_##X, \
.function=X, \
}; \
_sched_##X.alarm = gettime_ms() + (Y);\
_sched_##X.deadline = _sched_##X.alarm + (D);\
schedule(&_sched_##X); \
}
/* Periodically check for server shut down */
SCHEDULE(server_shutdown_check, 0, 100);
@ -154,6 +155,9 @@ schedule(&_sched_##X); }
/* Calculate (and possibly show) CPU usage stats periodically */
SCHEDULE(fd_periodicstats, 3000, 500);
/* Invoke the watchdog executable periodically */
SCHEDULE(server_watchdog, config.server.watchdog.interval_ms, 100);
#undef SCHEDULE
// log message used by tests to wait for the server to start

View File

@ -233,6 +233,7 @@ int server_get_proc_state(const char *path, char *buff, size_t buff_len);
int server_create_stopfile();
int server_remove_stopfile();
int server_check_stopfile();
void server_watchdog(struct sched_ent *alarm);
void overlay_mdp_clean_socket_files();
void serverCleanUp();

View File

@ -65,7 +65,6 @@ int server_pid()
return -1;
const char *p = strrchr(ppath, '/');
assert(p != NULL);
FILE *f = fopen(ppath, "r");
if (f == NULL) {
if (errno != ENOENT)
@ -226,6 +225,66 @@ void server_config_reload(struct sched_ent *alarm)
}
}
/* Called periodically by the server process in its main loop.
*/
void server_watchdog(struct sched_ent *alarm)
{
if (config.server.watchdog.executable[0]) {
const char *argv[2];
argv[0] = config.server.watchdog.executable;
argv[1] = NULL;
strbuf argv_sb = strbuf_append_argv(strbuf_alloca(1024), 1, argv);
switch (fork()) {
case 0: {
/* Child, should fork() again to create orphan process. */
pid_t watchdog_pid;
switch (watchdog_pid = fork()) {
case 0:
/* Grandchild, should exec() watchdog. */
close_log_file();
signal(SIGTERM, SIG_DFL);
close(0);
close(1);
close(2);
execv(config.server.watchdog.executable, (char **)argv);
// Don't use FATALF_perror() because we want to use _exit(2) not exit(2).
LOGF_perror(LOG_LEVEL_FATAL, "execv(%s, [%s])",
alloca_str_toprint(config.server.watchdog.executable),
strbuf_str(argv_sb)
);
break;
case -1:
/* grandchild fork failed */
WHY_perror("fork");
break;
default:
/* Child, report grandchild's PID. */
if (config.debug.watchdog)
LOGF(LOG_LEVEL_DEBUG, "STARTED WATCHDOG pid=%u executable=%s argv=[%s]",
watchdog_pid,
alloca_str_toprint(config.server.watchdog.executable),
strbuf_str(argv_sb)
);
do { _exit(0); } while (1);
break;
}
do { _exit(-1); } while (1);
break;
}
case -1:
/* child fork failed */
WHY_perror("fork");
break;
}
}
if (alarm) {
time_ms_t now = gettime_ms();
alarm->alarm = now + config.server.watchdog.interval_ms;
alarm->deadline = alarm->alarm + 100;
schedule(alarm);
}
}
/* Called periodically by the server process in its main loop.
*/
void server_shutdown_check(struct sched_ent *alarm)

View File

@ -142,4 +142,66 @@ teardown_NoZombie() {
assert_no_servald_processes
}
doc_ServerWatchdog="Server process invokes watchdog periodically"
setup_ServerWatchdog() {
cat >watchdog <<EOF
#!/bin/sh
date >> $PWD/trace
EOF
chmod 0550 watchdog
>trace
setup
executeOk_servald config \
set log.console.level debug \
set log.console.show_pid true \
set debug.watchdog on \
set server.watchdog.executable "$PWD/watchdog" \
set server.watchdog.interval_ms 100
start_servald_server
}
test_ServerWatchdog() {
wait_until --sleep=0.1 --timeout=15 line_count_at_least trace 10
stop_servald_server
assert_servald_server_no_errors
}
line_count_at_least() {
local file="$1"
local lines="$2"
[ $(wc -l <"$file") -ge "$lines" ]
}
doc_ReloadConfigAuto="Server automatically reloads configuration"
setup_ReloadConfigAuto() {
cat >watchdog1 <<EOF
#!/bin/sh
date >> $PWD/trace1
EOF
cat >watchdog2 <<EOF
#!/bin/sh
date >> $PWD/trace2
EOF
chmod 0550 watchdog1 watchdog2
>trace1
>trace2
setup
executeOk_servald config \
set log.console.level debug \
set log.console.show_time true \
set log.console.show_pid true \
set debug.watchdog on \
set server.watchdog.executable "$PWD/watchdog1" \
set server.watchdog.interval_ms 100
start_servald_server
}
test_ReloadConfigAuto() {
wait_until --sleep=0.5 --timeout=15 line_count_at_least trace1 3
assert [ $(wc -l <trace2) -eq 0 ]
executeOk_servald config \
set server.watchdog.executable "$PWD/watchdog2"
tfw_cat --stderr
wait_until --sleep=0.5 --timeout=15 line_count_at_least trace2 3
stop_servald_server
assert_servald_server_no_errors
}
runTests "$@"