2012-06-21 01:00:08 +00:00
|
|
|
/*
|
|
|
|
Serval Distributed Numbering Architecture (DNA)
|
|
|
|
Copyright (C) 2012 Paul Gardner-Stephen
|
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
2012-07-18 09:46:30 +00:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/wait.h>
|
2012-07-19 08:29:45 +00:00
|
|
|
#include <signal.h>
|
2012-06-21 01:00:08 +00:00
|
|
|
#include "serval.h"
|
2012-07-18 09:46:30 +00:00
|
|
|
#include "strbuf.h"
|
|
|
|
#include "strbuf_helpers.h"
|
2012-06-21 01:00:08 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
The challenge with making an interface for calling an external program to
|
|
|
|
resolve a DID into a URI is that it really should be asynchronous, so that
|
|
|
|
servald can't pause due to delays in looking up DIDs by helper applications.
|
|
|
|
|
|
|
|
This can be partially mitigated by having a cache, so that at least for repeated
|
|
|
|
requests the helper doesn't need to be called each time. This is very important
|
|
|
|
because the DNA protocol relies on pre-emptive retries to ensure reception of
|
|
|
|
a request over a lossy network.
|
|
|
|
|
|
|
|
The second part of the solution is to create an asynchronous queue for requests,
|
|
|
|
by passing them via file descriptor to a single persistent instance of the DNA
|
|
|
|
helper application, and polling the output of that application for results, and
|
|
|
|
then passing them out to their destinations. This ensures that the process is
|
|
|
|
asynchronous and non-blocking, regardless of how much time the helper application
|
|
|
|
requires. Then the helper will just be another file descriptor to poll in the
|
|
|
|
main loop.
|
|
|
|
*/
|
|
|
|
|
2012-06-25 06:53:45 +00:00
|
|
|
int
|
2012-07-20 08:47:43 +00:00
|
|
|
parseDnaReply(const char *bytes, size_t count, unsigned char *sid, char *did, char *name, char *uri)
|
2012-07-18 09:46:30 +00:00
|
|
|
{
|
2012-07-20 08:47:43 +00:00
|
|
|
/* Replies look like: TOKEN|URI|DID|NAME| where TOKEN is a SID in hex format */
|
|
|
|
const char *b = bytes;
|
|
|
|
const char *e = bytes + count;
|
2012-07-18 09:46:30 +00:00
|
|
|
char *p, *q;
|
2012-07-20 08:47:43 +00:00
|
|
|
if (count < SID_STRLEN || fromhex(sid, bytes, SID_SIZE) == -1)
|
|
|
|
return -1;
|
|
|
|
b += SID_STRLEN;
|
2012-07-18 09:46:30 +00:00
|
|
|
if (b == e || *b++ != '|')
|
|
|
|
return -1;
|
|
|
|
for (p = uri, q = uri + 511; b != e && *b != '|' && p != q; ++p, ++b)
|
|
|
|
*p = *b;
|
|
|
|
*p = '\0';
|
|
|
|
if (b == e || *b++ != '|')
|
|
|
|
return -1;
|
|
|
|
for (p = did, q = did + DID_MAXSIZE; b != e && *b != '|' && p != q; ++p, ++b)
|
|
|
|
*p = *b;
|
|
|
|
*p = '\0';
|
|
|
|
if (b == e || *b++ != '|')
|
|
|
|
return -1;
|
|
|
|
for (p = name, q = name + 63; b != e && *b != '|' && p != q; ++p, ++b)
|
|
|
|
*p = *b;
|
|
|
|
*p = '\0';
|
|
|
|
if (b == e || *b != '|')
|
|
|
|
return -1;
|
2012-06-21 02:50:10 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-07-19 08:29:45 +00:00
|
|
|
static pid_t dna_helper_pid = -1;
|
|
|
|
static int dna_helper_stdin = -1;
|
|
|
|
static int dna_helper_stdout = -1;
|
|
|
|
static int dna_helper_stderr = -1;
|
|
|
|
static int dna_helper_started = 0;
|
|
|
|
|
|
|
|
static struct sched_ent sched_requests = STRUCT_SCHED_ENT_UNUSED;
|
|
|
|
static struct sched_ent sched_replies = STRUCT_SCHED_ENT_UNUSED;
|
|
|
|
static struct sched_ent sched_harvester = STRUCT_SCHED_ENT_UNUSED;
|
|
|
|
static struct sched_ent sched_errors = STRUCT_SCHED_ENT_UNUSED;
|
|
|
|
static struct sched_ent schedrestart = STRUCT_SCHED_ENT_UNUSED;
|
|
|
|
|
2012-07-20 08:47:43 +00:00
|
|
|
// This buffer must hold "SID|DID|\n\0"
|
|
|
|
static char request_buffer[SID_STRLEN + DID_MAXSIZE + 4];
|
|
|
|
static char *request_bufptr = NULL;
|
|
|
|
static char *request_bufend = NULL;
|
|
|
|
overlay_mdp_data_frame request_mdp_data;
|
2012-07-16 03:15:43 +00:00
|
|
|
|
2012-07-20 08:47:43 +00:00
|
|
|
static int awaiting_reply = 0;
|
|
|
|
static int discarding_until_nl = 0;
|
|
|
|
static char reply_buffer[2048];
|
|
|
|
static char *reply_bufend = NULL;
|
|
|
|
|
|
|
|
static void monitor_requests(struct sched_ent *alarm);
|
|
|
|
static void monitor_replies(struct sched_ent *alarm);
|
|
|
|
static void monitor_errors(struct sched_ent *alarm);
|
|
|
|
static void harvester(struct sched_ent *alarm);
|
|
|
|
static void restart_delayer(struct sched_ent *alarm);
|
2012-07-19 08:29:45 +00:00
|
|
|
|
|
|
|
static void
|
|
|
|
dna_helper_close_pipes()
|
|
|
|
{
|
|
|
|
if (dna_helper_stdin != -1) {
|
2012-07-20 08:47:43 +00:00
|
|
|
if (debug & DEBUG_DNAHELPER)
|
|
|
|
DEBUGF("Closing DNA helper stdin pipe fd=%d", dna_helper_stdin);
|
2012-07-19 08:29:45 +00:00
|
|
|
close(dna_helper_stdin);
|
|
|
|
dna_helper_stdin = -1;
|
|
|
|
}
|
2012-07-20 08:47:43 +00:00
|
|
|
if (sched_requests.poll.fd != -1) {
|
|
|
|
unwatch(&sched_requests);
|
|
|
|
sched_requests.poll.fd = -1;
|
2012-07-19 08:29:45 +00:00
|
|
|
}
|
|
|
|
if (dna_helper_stdout != -1) {
|
2012-07-20 08:47:43 +00:00
|
|
|
if (debug & DEBUG_DNAHELPER)
|
|
|
|
DEBUGF("Closing DNA helper stdout pipe fd=%d", dna_helper_stdout);
|
2012-07-19 08:29:45 +00:00
|
|
|
close(dna_helper_stdout);
|
|
|
|
dna_helper_stdout = -1;
|
|
|
|
}
|
2012-07-20 08:47:43 +00:00
|
|
|
if (sched_replies.poll.fd != -1) {
|
|
|
|
unwatch(&sched_replies);
|
|
|
|
sched_replies.poll.fd = -1;
|
2012-07-19 08:29:45 +00:00
|
|
|
}
|
|
|
|
if (dna_helper_stderr != -1) {
|
2012-07-20 08:47:43 +00:00
|
|
|
if (debug & DEBUG_DNAHELPER)
|
|
|
|
DEBUGF("Closing DNA helper stderr pipe fd=%d", dna_helper_stderr);
|
2012-07-19 08:29:45 +00:00
|
|
|
close(dna_helper_stderr);
|
|
|
|
dna_helper_stderr = -1;
|
|
|
|
}
|
2012-07-20 08:47:43 +00:00
|
|
|
if (sched_errors.poll.fd != -1) {
|
|
|
|
unwatch(&sched_errors);
|
|
|
|
sched_errors.poll.fd = -1;
|
|
|
|
}
|
2012-07-19 08:29:45 +00:00
|
|
|
}
|
2012-07-16 07:35:13 +00:00
|
|
|
|
2012-06-25 06:53:45 +00:00
|
|
|
static int
|
2012-07-18 09:46:30 +00:00
|
|
|
dna_helper_start(const char *command, const char *arg)
|
|
|
|
{
|
2012-07-19 08:29:45 +00:00
|
|
|
dna_helper_close_pipes();
|
|
|
|
int stdin_fds[2], stdout_fds[2], stderr_fds[2];
|
|
|
|
if (pipe(stdin_fds) == -1)
|
2012-06-25 06:53:45 +00:00
|
|
|
return WHY_perror("pipe");
|
2012-07-19 08:29:45 +00:00
|
|
|
if (pipe(stdout_fds) == -1) {
|
|
|
|
WHY_perror("pipe");
|
2012-06-25 06:53:45 +00:00
|
|
|
close(stdin_fds[0]);
|
2012-07-19 08:29:45 +00:00
|
|
|
close(stdin_fds[1]);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (pipe(stderr_fds) == -1) {
|
|
|
|
WHY_perror("pipe");
|
|
|
|
close(stdin_fds[0]);
|
|
|
|
close(stdin_fds[1]);
|
|
|
|
close(stdout_fds[0]);
|
|
|
|
close(stdout_fds[1]);
|
|
|
|
return -1;
|
2012-06-21 02:12:34 +00:00
|
|
|
}
|
2012-07-18 09:46:30 +00:00
|
|
|
switch (dna_helper_pid = fork()) {
|
|
|
|
case 0:
|
2012-06-21 02:12:34 +00:00
|
|
|
/* Child, should exec() to become helper after installing file descriptors. */
|
2012-07-19 08:29:45 +00:00
|
|
|
set_logging(stderr);
|
|
|
|
signal(SIGTERM, SIG_DFL);
|
|
|
|
close(stdin_fds[1]);
|
|
|
|
close(stdout_fds[0]);
|
|
|
|
close(stderr_fds[0]);
|
|
|
|
if (dup2(stderr_fds[1], 2) == -1 || dup2(stdout_fds[1], 1) == -1 || dup2(stdin_fds[0], 0) == -1) {
|
|
|
|
LOG_perror(LOG_LEVEL_FATAL, "dup2");
|
|
|
|
fflush(stderr);
|
|
|
|
_exit(-1);
|
|
|
|
}
|
2012-06-25 06:53:45 +00:00
|
|
|
execl(command, command, arg, NULL);
|
2012-07-19 08:29:45 +00:00
|
|
|
LOGF_perror(LOG_LEVEL_FATAL, "execl(%s, %s, %s, NULL)", command, command, arg ? arg : "NULL");
|
|
|
|
fflush(stderr);
|
|
|
|
do { _exit(-1); } while (1);
|
2012-07-18 09:46:30 +00:00
|
|
|
break;
|
|
|
|
case -1:
|
|
|
|
/* fork failed */
|
|
|
|
WHY_perror("fork");
|
|
|
|
close(stdin_fds[0]);
|
2012-07-19 08:29:45 +00:00
|
|
|
close(stdin_fds[1]);
|
2012-07-18 09:46:30 +00:00
|
|
|
close(stdout_fds[0]);
|
2012-07-19 08:29:45 +00:00
|
|
|
close(stdout_fds[1]);
|
|
|
|
close(stderr_fds[0]);
|
|
|
|
close(stderr_fds[1]);
|
2012-07-18 09:46:30 +00:00
|
|
|
return -1;
|
|
|
|
default:
|
|
|
|
/* Parent, should put file descriptors into place for use */
|
2012-07-19 08:29:45 +00:00
|
|
|
close(stdin_fds[0]);
|
|
|
|
close(stdout_fds[1]);
|
|
|
|
close(stderr_fds[1]);
|
|
|
|
dna_helper_started = 0;
|
|
|
|
dna_helper_stdin = stdin_fds[1];
|
|
|
|
dna_helper_stdout = stdout_fds[0];
|
|
|
|
dna_helper_stderr = stderr_fds[0];
|
|
|
|
INFOF("STARTED DNA HELPER pid=%u stdin=%d stdout=%d stderr=%d executable=%s arg=%s",
|
|
|
|
dna_helper_pid,
|
|
|
|
dna_helper_stdin,
|
|
|
|
dna_helper_stdout,
|
|
|
|
dna_helper_stderr,
|
|
|
|
command,
|
|
|
|
arg ? arg : "NULL"
|
|
|
|
);
|
|
|
|
sched_requests.function = monitor_requests;
|
|
|
|
sched_requests.context = NULL;
|
2012-07-20 08:47:43 +00:00
|
|
|
sched_requests.poll.fd = -1;
|
2012-07-19 08:29:45 +00:00
|
|
|
sched_requests.poll.events = POLLOUT;
|
|
|
|
sched_requests.stats = NULL;
|
|
|
|
sched_replies.function = monitor_replies;
|
|
|
|
sched_replies.context = NULL;
|
|
|
|
sched_replies.poll.fd = dna_helper_stdout;
|
|
|
|
sched_replies.poll.events = POLLIN;
|
|
|
|
sched_replies.stats = NULL;
|
|
|
|
sched_errors.function = monitor_errors;
|
|
|
|
sched_errors.context = NULL;
|
|
|
|
sched_errors.poll.fd = dna_helper_stderr;
|
|
|
|
sched_errors.poll.events = POLLIN;
|
|
|
|
sched_errors.stats = NULL;
|
|
|
|
sched_harvester.function = harvester;
|
|
|
|
sched_harvester.stats = NULL;
|
|
|
|
sched_harvester.alarm = overlay_gettime_ms() + 1000;
|
2012-07-20 08:47:43 +00:00
|
|
|
reply_bufend = reply_buffer;
|
|
|
|
discarding_until_nl = 0;
|
|
|
|
awaiting_reply = 0;
|
2012-07-19 08:29:45 +00:00
|
|
|
watch(&sched_replies);
|
|
|
|
watch(&sched_errors);
|
|
|
|
schedule(&sched_harvester);
|
2012-07-18 09:46:30 +00:00
|
|
|
return 0;
|
2012-06-21 02:12:34 +00:00
|
|
|
}
|
2012-06-21 01:00:08 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-07-18 09:46:30 +00:00
|
|
|
static int
|
2012-07-20 08:47:43 +00:00
|
|
|
dna_helper_kill()
|
2012-07-18 09:46:30 +00:00
|
|
|
{
|
|
|
|
if (dna_helper_pid > 0) {
|
2012-07-19 08:29:45 +00:00
|
|
|
if (debug & DEBUG_DNAHELPER)
|
|
|
|
DEBUGF("Sending SIGTERM to DNA helper pid=%d", dna_helper_pid);
|
|
|
|
if (kill(dna_helper_pid, SIGTERM) == -1)
|
|
|
|
WHYF_perror("kill(%d, SIGTERM)", dna_helper_pid);
|
|
|
|
// The process is wait()ed for in dna_helper_monitor() so that we do not block here.
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
dna_helper_harvest(int blocking)
|
|
|
|
{
|
|
|
|
if (dna_helper_pid > 0) {
|
|
|
|
if (blocking && (debug & DEBUG_DNAHELPER))
|
|
|
|
DEBUGF("Waiting for DNA helper pid=%d to die", dna_helper_pid);
|
2012-07-18 09:46:30 +00:00
|
|
|
int status;
|
2012-07-19 08:29:45 +00:00
|
|
|
pid_t pid = waitpid(dna_helper_pid, &status, blocking ? 0 : WNOHANG);
|
2012-07-18 09:46:30 +00:00
|
|
|
if (pid == dna_helper_pid) {
|
|
|
|
strbuf b = strbuf_alloca(80);
|
|
|
|
INFOF("DNA helper pid=%u %s", pid, strbuf_str(strbuf_append_exit_status(b, status)));
|
2012-07-19 08:29:45 +00:00
|
|
|
unschedule(&sched_harvester);
|
2012-07-18 09:46:30 +00:00
|
|
|
dna_helper_pid = -1;
|
|
|
|
return 1;
|
|
|
|
} else if (pid == -1) {
|
2012-07-19 08:29:45 +00:00
|
|
|
return WHYF_perror("waitpid(%d, %s)", dna_helper_pid, blocking ? "0" : "WNOHANG");
|
2012-07-18 09:46:30 +00:00
|
|
|
} else if (pid) {
|
2012-07-19 08:29:45 +00:00
|
|
|
return WHYF("waitpid(%d, %s) returned %d", dna_helper_pid, blocking ? "0" : "WNOHANG", pid);
|
2012-07-18 09:46:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-07-19 08:29:45 +00:00
|
|
|
int dna_helper_shutdown()
|
|
|
|
{
|
|
|
|
if (debug & DEBUG_DNAHELPER)
|
|
|
|
DEBUG("Shutting down DNA helper");
|
|
|
|
dna_helper_close_pipes();
|
2012-07-20 08:47:43 +00:00
|
|
|
switch (dna_helper_kill()) {
|
2012-07-19 08:29:45 +00:00
|
|
|
case -1:
|
|
|
|
return -1;
|
|
|
|
case 0:
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
return dna_helper_harvest(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-20 08:47:43 +00:00
|
|
|
static void monitor_requests(struct sched_ent *alarm)
|
2012-07-19 08:29:45 +00:00
|
|
|
{
|
|
|
|
if (debug & DEBUG_DNAHELPER) {
|
|
|
|
DEBUGF("sched_requests.poll.revents=%s",
|
|
|
|
strbuf_str(strbuf_append_poll_events(strbuf_alloca(40), sched_requests.poll.revents))
|
|
|
|
);
|
2012-07-18 09:46:30 +00:00
|
|
|
}
|
2012-07-19 08:29:45 +00:00
|
|
|
if (sched_requests.poll.revents & (POLLHUP | POLLERR)) {
|
2012-07-20 08:47:43 +00:00
|
|
|
if (debug & DEBUG_DNAHELPER)
|
|
|
|
DEBUGF("Closing DNA helper stdin fd=%d", dna_helper_stdin);
|
2012-07-19 08:29:45 +00:00
|
|
|
close(dna_helper_stdin);
|
|
|
|
dna_helper_stdin = -1;
|
|
|
|
unwatch(&sched_requests);
|
|
|
|
sched_requests.poll.fd = -1;
|
2012-07-20 08:47:43 +00:00
|
|
|
dna_helper_kill();
|
2012-07-19 08:29:45 +00:00
|
|
|
}
|
|
|
|
else if (sched_requests.poll.revents & POLLOUT) {
|
2012-07-20 08:47:43 +00:00
|
|
|
if (request_bufptr) {
|
|
|
|
if (request_bufptr < request_bufend) {
|
|
|
|
size_t remaining = request_bufend - request_bufptr;
|
|
|
|
sigPipeFlag = 0;
|
|
|
|
ssize_t written = write_nonblock(dna_helper_stdin, request_bufptr, remaining);
|
|
|
|
if (sigPipeFlag) {
|
|
|
|
/* Broken pipe is probably due to a dead helper, but make sure the helper is dead, just to be
|
|
|
|
sure. It will be harvested at the next harvester() timeout, and restarted on the first
|
|
|
|
request that arrives after a suitable pause has elapsed. Losing the current request is not
|
|
|
|
a big problem, because DNA preemptively retries.
|
|
|
|
*/
|
|
|
|
WARN("Got SIGPIPE from DNA helper -- stopping DNA helper");
|
|
|
|
dna_helper_kill();
|
|
|
|
} else if (written > 0) {
|
|
|
|
request_bufptr += written;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (request_bufptr >= request_bufend) {
|
2012-07-19 08:29:45 +00:00
|
|
|
// Request sent successfully. Start watching for reply.
|
2012-07-20 08:47:43 +00:00
|
|
|
request_bufptr = request_bufend = NULL;
|
|
|
|
awaiting_reply = 1;
|
2012-07-19 08:29:45 +00:00
|
|
|
}
|
2012-07-18 09:46:30 +00:00
|
|
|
}
|
2012-07-20 08:47:43 +00:00
|
|
|
// If no request to send, stop monitoring the helper's stdin pipe.
|
|
|
|
if (!request_bufptr) {
|
|
|
|
unwatch(&sched_requests);
|
|
|
|
sched_requests.poll.fd = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *strnstr(char *haystack, size_t haystack_len, const char *needle)
|
|
|
|
{
|
|
|
|
size_t needle_len = strlen(needle);
|
|
|
|
for (; haystack_len >= needle_len; ++haystack, --haystack_len) {
|
|
|
|
if (strncmp(haystack, needle, needle_len) == 0)
|
|
|
|
return haystack;
|
2012-07-18 09:46:30 +00:00
|
|
|
}
|
2012-07-20 08:47:43 +00:00
|
|
|
return NULL;
|
2012-07-19 08:29:45 +00:00
|
|
|
}
|
|
|
|
|
2012-07-20 08:47:43 +00:00
|
|
|
void handle_reply_line(const char *bufp, size_t len)
|
|
|
|
{
|
|
|
|
if (!dna_helper_started) {
|
|
|
|
if (len == 8 && strncmp(bufp, "STARTED\n", 8) == 0) {
|
|
|
|
if (debug & DEBUG_DNAHELPER)
|
|
|
|
DEBUGF("Got DNA helper STARTED ACK");
|
|
|
|
dna_helper_started = 1;
|
|
|
|
// Start sending request if there is one pending.
|
|
|
|
if (request_bufptr) {
|
|
|
|
sched_requests.poll.fd = dna_helper_stdin;
|
|
|
|
watch(&sched_requests);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
WHYF("Malformed DNA helper start ACK %s", alloca_toprint(-1, bufp, len));
|
|
|
|
dna_helper_kill();
|
|
|
|
}
|
|
|
|
} else if (awaiting_reply) {
|
|
|
|
if (len == 5 && strncmp(bufp, "DONE\n", 5) == 0) {
|
|
|
|
if (debug & DEBUG_DNAHELPER)
|
|
|
|
DEBUG("DNA helper reply DONE");
|
|
|
|
awaiting_reply = 0;
|
|
|
|
// Done
|
|
|
|
} else {
|
|
|
|
unsigned char sid[SID_SIZE];
|
|
|
|
char did[DID_MAXSIZE + 1];
|
|
|
|
char name[64];
|
|
|
|
char uri[512];
|
|
|
|
if (parseDnaReply(bufp, len, sid, did, name, uri) != -1) {
|
|
|
|
if (debug & DEBUG_DNAHELPER)
|
|
|
|
DEBUGF("DNA helper reply %s", alloca_toprint(-1, bufp, len));
|
|
|
|
overlay_mdp_dnalookup_reply(&request_mdp_data, sid, uri, did, name);
|
|
|
|
} else {
|
|
|
|
WARNF("DNA helper invalid reply %s", alloca_toprint(-1, bufp, len));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
WARNF("DNA helper spurious reply %s", alloca_toprint(-1, bufp, len));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void monitor_replies(struct sched_ent *alarm)
|
2012-07-19 08:29:45 +00:00
|
|
|
{
|
|
|
|
if (debug & DEBUG_DNAHELPER) {
|
|
|
|
DEBUGF("sched_replies.poll.revents=%s",
|
|
|
|
strbuf_str(strbuf_append_poll_events(strbuf_alloca(40), sched_replies.poll.revents))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
if (sched_replies.poll.revents & POLLIN) {
|
2012-07-20 08:47:43 +00:00
|
|
|
size_t remaining = reply_buffer + sizeof reply_buffer - reply_bufend;
|
|
|
|
ssize_t nread = read_nonblock(sched_replies.poll.fd, reply_bufend, remaining);
|
|
|
|
if (nread > 0) {
|
|
|
|
char *bufp = reply_buffer;
|
|
|
|
char *readp = reply_bufend;
|
|
|
|
reply_bufend += nread;
|
|
|
|
char *nl;
|
|
|
|
while (nread > 0 && (nl = strnstr(readp, nread, "\n"))) {
|
|
|
|
size_t len = nl - bufp + 1;
|
|
|
|
if (discarding_until_nl) {
|
|
|
|
if (debug & DEBUG_DNAHELPER)
|
|
|
|
DEBUGF("Discarding %s", alloca_toprint(-1, bufp, len));
|
|
|
|
discarding_until_nl = 0;
|
|
|
|
} else {
|
|
|
|
handle_reply_line(bufp, len);
|
2012-07-19 08:29:45 +00:00
|
|
|
}
|
2012-07-20 08:47:43 +00:00
|
|
|
readp = bufp = nl + 1;
|
|
|
|
nread = reply_bufend - readp;
|
|
|
|
}
|
|
|
|
if (bufp != reply_buffer) {
|
|
|
|
size_t len = reply_bufend - bufp;
|
|
|
|
memmove(reply_buffer, bufp, len);
|
|
|
|
reply_bufend = reply_buffer + len;
|
|
|
|
} else if (reply_bufend >= reply_buffer + sizeof reply_buffer) {
|
|
|
|
WHY("DNA helper reply buffer overrun");
|
|
|
|
if (debug & DEBUG_DNAHELPER)
|
|
|
|
DEBUGF("Discarding %s", alloca_toprint(-1, reply_buffer, sizeof reply_buffer));
|
|
|
|
reply_bufend = reply_buffer;
|
|
|
|
discarding_until_nl = 1;
|
2012-07-19 08:29:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (sched_replies.poll.revents & (POLLHUP | POLLERR)) {
|
2012-07-20 08:47:43 +00:00
|
|
|
if (debug & DEBUG_DNAHELPER)
|
|
|
|
DEBUGF("Closing DNA helper stdout fd=%d", dna_helper_stdout);
|
2012-07-19 08:29:45 +00:00
|
|
|
close(dna_helper_stdout);
|
|
|
|
dna_helper_stdout = -1;
|
|
|
|
unwatch(&sched_replies);
|
|
|
|
sched_replies.poll.fd = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-20 08:47:43 +00:00
|
|
|
static void monitor_errors(struct sched_ent *alarm)
|
2012-07-19 08:29:45 +00:00
|
|
|
{
|
|
|
|
if (debug & DEBUG_DNAHELPER) {
|
|
|
|
DEBUGF("sched_errors.poll.revents=%s",
|
|
|
|
strbuf_str(strbuf_append_poll_events(strbuf_alloca(40), sched_errors.poll.revents))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
if (sched_errors.poll.revents & POLLIN) {
|
2012-07-20 08:47:43 +00:00
|
|
|
char buffer[1024];
|
2012-07-19 08:29:45 +00:00
|
|
|
ssize_t nread = read_nonblock(sched_errors.poll.fd, buffer, sizeof buffer);
|
|
|
|
if (nread > 0 && (debug & DEBUG_DNAHELPER))
|
|
|
|
DEBUGF("DNA helper stderr %s", alloca_toprint(-1, buffer, nread));
|
|
|
|
}
|
|
|
|
if (sched_errors.poll.revents & (POLLHUP | POLLERR)) {
|
2012-07-20 08:47:43 +00:00
|
|
|
if (debug & DEBUG_DNAHELPER)
|
|
|
|
DEBUGF("Closing DNA helper stderr fd=%d", dna_helper_stderr);
|
|
|
|
close(dna_helper_stderr);
|
|
|
|
dna_helper_stderr = -1;
|
2012-07-19 08:29:45 +00:00
|
|
|
unwatch(&sched_errors);
|
|
|
|
sched_errors.poll.fd = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-20 08:47:43 +00:00
|
|
|
static void harvester(struct sched_ent *alarm)
|
2012-07-19 08:29:45 +00:00
|
|
|
{
|
|
|
|
// While the helper process appears to still be running, keep calling this function.
|
|
|
|
// Otherwise, wait a while before re-starting the helper.
|
|
|
|
if (dna_helper_harvest(0) <= 0) {
|
|
|
|
sched_harvester.alarm = overlay_gettime_ms() + 1000;
|
|
|
|
schedule(&sched_harvester);
|
|
|
|
} else {
|
2012-07-20 08:47:43 +00:00
|
|
|
const int delay_ms = 500;
|
2012-07-19 08:29:45 +00:00
|
|
|
if (debug & DEBUG_DNAHELPER)
|
|
|
|
DEBUGF("DNA helper has died, pausing %d ms before restart", delay_ms);
|
2012-07-20 08:47:43 +00:00
|
|
|
dna_helper_pid = 0; // Will be set to -1 after delay
|
|
|
|
schedrestart.function = restart_delayer;
|
2012-07-19 08:29:45 +00:00
|
|
|
schedrestart.alarm = overlay_gettime_ms() + delay_ms;
|
|
|
|
schedule(&schedrestart);
|
2012-07-18 09:46:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-20 08:47:43 +00:00
|
|
|
static void restart_delayer(struct sched_ent *alarm)
|
|
|
|
{
|
|
|
|
if (dna_helper_pid == 0) {
|
|
|
|
if (debug & DEBUG_DNAHELPER)
|
|
|
|
DEBUG("Re-enable DNA helper restart");
|
|
|
|
dna_helper_pid = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-25 06:53:45 +00:00
|
|
|
int
|
2012-07-20 08:47:43 +00:00
|
|
|
dna_helper_enqueue(overlay_mdp_frame *mdp, const char *did, const unsigned char *requestorSid)
|
2012-07-18 09:46:30 +00:00
|
|
|
{
|
2012-07-20 08:47:43 +00:00
|
|
|
if (debug & DEBUG_DNAHELPER)
|
|
|
|
DEBUGF("DNA helper request did=%s sid=%s", did, alloca_tohex_sid(requestorSid));
|
2012-07-18 09:46:30 +00:00
|
|
|
if (dna_helper_pid == 0)
|
2012-07-20 08:47:43 +00:00
|
|
|
return 0;
|
|
|
|
// Only try to restart a DNA helper process if the previous one is well and truly gone.
|
|
|
|
if (dna_helper_pid == -1 && dna_helper_stdin == -1 && dna_helper_stdout == -1 && dna_helper_stderr == -1) {
|
2012-07-18 09:46:30 +00:00
|
|
|
const char *dna_helper_executable = confValueGet("dna.helper.executable", NULL);
|
|
|
|
const char *dna_helper_arg1 = confValueGet("dna.helper.argv.1", NULL);
|
|
|
|
if (!dna_helper_executable || !dna_helper_executable[0]) {
|
2012-06-25 06:53:45 +00:00
|
|
|
/* Check if we have a helper configured. If not, then set
|
2012-07-18 09:46:30 +00:00
|
|
|
dna_helper_pid to magic value of 0 so that we don't waste time
|
2012-06-25 06:53:45 +00:00
|
|
|
in future looking up the dna helper configuration value. */
|
2012-07-18 09:46:30 +00:00
|
|
|
INFO("No DNA helper configured");
|
2012-07-19 08:29:45 +00:00
|
|
|
dna_helper_pid = 0;
|
2012-07-20 08:47:43 +00:00
|
|
|
return 0;
|
2012-06-21 01:00:08 +00:00
|
|
|
}
|
2012-07-20 08:47:43 +00:00
|
|
|
if (dna_helper_start(dna_helper_executable, dna_helper_arg1) == -1) {
|
2012-06-25 06:53:45 +00:00
|
|
|
/* Something broke, bail out */
|
2012-07-19 08:29:45 +00:00
|
|
|
WHY("Failed to start DNA helper");
|
2012-06-21 01:00:08 +00:00
|
|
|
return -1;
|
2012-07-16 01:38:51 +00:00
|
|
|
}
|
2012-06-21 01:00:08 +00:00
|
|
|
}
|
|
|
|
/* Write request to dna helper.
|
2012-06-25 06:53:45 +00:00
|
|
|
Request takes form: SID-of-Requestor|DID|\n
|
2012-06-21 01:00:08 +00:00
|
|
|
By passing the requestor's SID to the helper, we don't need to maintain
|
|
|
|
any state, as all we have to do is wait for responses from the helper,
|
|
|
|
which will include the requestor's SID.
|
|
|
|
*/
|
2012-07-20 08:47:43 +00:00
|
|
|
if (dna_helper_stdin == -1)
|
|
|
|
return 0;
|
|
|
|
if (request_bufptr && request_bufptr != request_buffer) {
|
|
|
|
WARNF("Partially sent DNA helper request %s -- dropping new request", request_buffer);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
char buffer[sizeof request_buffer];
|
|
|
|
strbuf b = strbuf_local(request_bufptr == request_buffer ? buffer : request_buffer, sizeof buffer);
|
|
|
|
strbuf_tohex(b, requestorSid, SID_SIZE);
|
|
|
|
strbuf_putc(b, '|');
|
|
|
|
strbuf_puts(b, did);
|
|
|
|
strbuf_putc(b, '|');
|
|
|
|
strbuf_putc(b, '\n');
|
|
|
|
if (strbuf_overrun(b)) {
|
|
|
|
WHYF("DNA helper request buffer overrun: %s -- request not sent", strbuf_str(b));
|
|
|
|
request_bufptr = request_bufend = NULL;
|
2012-07-19 08:29:45 +00:00
|
|
|
} else {
|
2012-07-20 08:47:43 +00:00
|
|
|
if (strbuf_str(b) != request_buffer) {
|
|
|
|
if (strcmp(strbuf_str(b), request_buffer) != 0)
|
|
|
|
WARNF("Overwriting unsent DNA helper request %s", request_buffer);
|
|
|
|
strcpy(request_buffer, strbuf_str(b));
|
2012-07-19 08:29:45 +00:00
|
|
|
}
|
2012-07-20 08:47:43 +00:00
|
|
|
request_bufptr = request_buffer;
|
|
|
|
request_bufend = request_buffer + strbuf_len(b);
|
|
|
|
request_mdp_data = mdp->out;
|
2012-06-21 01:00:08 +00:00
|
|
|
}
|
2012-07-20 08:47:43 +00:00
|
|
|
if (dna_helper_started) {
|
|
|
|
sched_requests.poll.fd = dna_helper_stdin;
|
|
|
|
watch(&sched_requests);
|
|
|
|
}
|
|
|
|
return 1;
|
2012-06-21 01:00:08 +00:00
|
|
|
}
|