I think I finally found #49. The signal is not SIGSYS; it's SIGPIPE.
That's why dorpauli was getting no core dumps.
Apparently, this is a typical newbie trap for libcurl users.
CURLOPT_NOSIGNAL is incapable of supressing all SIGPIPEs, because some
of them are OS-generated.
I can't believe how dumb SIGPIPE has turned out to be. I/O functions
should return EPIPE; not interrupt the whole program to a handler that
defaults to "die silently."
What the hell, POSIX.
#include "file.h"
#include "log.h"
-#define TA_DEBUG \
- do { if (is_ta) PR_DEBUG; } while (0)
-#define TA_DEBUG_MSG(fmt, ...) \
- do { if (is_ta) PR_DEBUG_MSG(fmt, ##__VA_ARGS__); } while (0)
-
/* HTTP Response Code 200 (OK) */
#define HTTP_OK 200
/* HTTP Response Code 304 (Not Modified) */
CURLcode res, res2;
long unmet = 0;
- TA_DEBUG;
- TA_DEBUG_MSG("%s %d", uri, log_operation);
-
handler->errbuf[0] = 0;
setopt_str(handler->curl, CURLOPT_URL, uri);
setopt_writedata(handler->curl, file);
- TA_DEBUG_MSG("HTTP GET: %s", uri);
res = curl_easy_perform(handler->curl);
- TA_DEBUG_MSG("%d", res);
res2 = curl_easy_getinfo(handler->curl, CURLINFO_RESPONSE_CODE,
response_code);
- TA_DEBUG_MSG("%d", res2);
if (res2 != CURLE_OK) {
return pr_op_err("curl_easy_getinfo(CURLINFO_RESPONSE_CODE) returned %d (%s). I think this is supposed to be illegal, so I'll have to drop URI '%s'.",
res2, curl_err_string(handler, res2), uri);
}
- TA_DEBUG_MSG("%ld", *response_code);
if (res == CURLE_OK) {
- if (*response_code != HTTP_OK) {
- TA_DEBUG;
+ if (*response_code != HTTP_OK)
return 0;
- }
/*
* Scenario: Received an OK code, but the time condition
* unmet: 0
* writefunction called
*/
- TA_DEBUG;
res = curl_easy_getinfo(handler->curl, CURLINFO_CONDITION_UNMET,
&unmet);
if (res == CURLE_OK && unmet == 1)
*cond_met = 0;
- TA_DEBUG_MSG("%ld", unmet);
return 0;
}
if (*response_code >= HTTP_BAD_REQUEST) {
- TA_DEBUG;
return pr_val_err("Error requesting URL %s (received HTTP code %ld): %s",
uri, *response_code, curl_err_string(handler, res));
}
pr_val_err("Error requesting URL %s: %s", uri,
curl_err_string(handler, res));
- TA_DEBUG;
if (log_operation) {
- TA_DEBUG;
pr_op_err("Error requesting URL %s: %s", uri,
curl_err_string(handler, res));
}
*cond_met = 1;
if (!config_get_http_enabled()) {
*response_code = 0; /* Not 200 code, but also not an error */
- TA_DEBUG;
return 0;
}
- TA_DEBUG;
-
original_file = uri_get_local(uri);
tmp_file = strdup(original_file);
- if (tmp_file == NULL) {
- TA_DEBUG;
+ if (tmp_file == NULL)
return pr_enomem();
- }
-
- TA_DEBUG;
tmp = realloc(tmp_file, strlen(tmp_file) + strlen(tmp_suffix) + 1);
if (tmp == NULL) {
- TA_DEBUG;
error = pr_enomem();
goto release_tmp;
}
tmp_file = tmp;
strcat(tmp_file, tmp_suffix);
- TA_DEBUG;
-
error = create_dir_recursive(tmp_file);
- if (error) {
- TA_DEBUG_MSG("%d", error);
+ if (error)
goto release_tmp;
- }
-
- TA_DEBUG;
error = file_write(tmp_file, &out, &stat);
- if (error) {
- TA_DEBUG_MSG("%d", error);
+ if (error)
goto delete_dir;
- }
do {
- TA_DEBUG;
-
error = http_easy_init(&handler);
- if (error) {
- TA_DEBUG_MSG("%d", error);
+ if (error)
goto close_file;
- }
/* Set "If-Modified-Since" header only if a value is specified */
if (ims_value > 0) {
- TA_DEBUG_MSG("%ld", ims_value);
setopt_long(handler.curl, CURLOPT_TIMEVALUE, ims_value);
setopt_long(handler.curl, CURLOPT_TIMECONDITION,
CURL_TIMECOND_IFMODSINCE);
- TA_DEBUG;
}
error = http_fetch(&handler, uri_get_global(uri), response_code,
cond_met, log_operation, out, is_ta);
if (error != EREQFAILED) {
- TA_DEBUG_MSG("%d", error);
break; /* Note: Usually happy path */
}
- TA_DEBUG_MSG("%d", error);
-
if (retries == config_get_http_retry_count()) {
- TA_DEBUG_MSG("%u", retries);
pr_val_warn("Max HTTP retries (%u) reached requesting for '%s', won't retry again.",
retries, uri_get_global(uri));
break;
}
- TA_DEBUG;
pr_val_warn("Retrying HTTP request '%s' in %u seconds, %u attempts remaining.",
uri_get_global(uri),
config_get_http_retry_interval(),
http_easy_cleanup(&handler);
file_close(out);
- if (error) {
- TA_DEBUG_MSG("%d", error);
+ if (error)
goto delete_dir;
- }
-
- TA_DEBUG;
- TA_DEBUG_MSG("%s %s", tmp_file, original_file);
/* Overwrite the original file */
error = rename(tmp_file, original_file);
if (error) {
error = errno;
- TA_DEBUG_MSG("%d", error);
pr_val_errno(error, "Renaming temporal file from '%s' to '%s'",
tmp_file, original_file);
goto delete_dir;
}
- TA_DEBUG;
-
free(tmp_file);
return 0;
close_file:
delete_dir_recursive_bottom_up(tmp_file);
release_tmp:
free(tmp_file);
- TA_DEBUG_MSG("%d", error);
return ENSURE_NEGATIVE(error);
}
}
static void
-signal_handler(int signum)
+sigsegv_handler(int signum)
{
/*
* IMPORTANT: See https://stackoverflow.com/questions/29982643
#define STACK_SIZE 64
void *array[STACK_SIZE];
size_t size;
-// ssize_t trash;
-
- int tmp;
- unsigned char digit;
-
-// /* Don't know why, but casting to void is not silencing the warning. */
-// trash = write(STDERR_FILENO, title, strlen(title));
-// trash++;
-
- tmp = signum;
- while (tmp != 0) {
- digit = tmp % 10 + 48;
- digit = write(STDERR_FILENO, &digit, 1);
- tmp /= 10;
- }
- digit = write(STDERR_FILENO, "\n", 1);
size = backtrace(array, STACK_SIZE);
backtrace_symbols_fd(array, size, STDERR_FILENO);
kill(getpid(), signum);
}
-static void
-register_signal_handler(int signum)
-{
- struct sigaction handler;
-
- handler.sa_handler = signal_handler;
- sigemptyset(&handler.sa_mask);
- handler.sa_flags = 0;
- sigaction(signum, &handler, NULL);
-}
-
/*
- * Register stack trace printer on segmentation fault listener.
+ * Register better handlers for some signals.
+ *
* Remember to enable -rdynamic (See print_stack_trace()).
*/
static void
register_signal_handlers(void)
{
+ struct sigaction action;
void* dummy;
- /* Make sure libgcc is loaded; otherwise the handler might allocate. */
+ /*
+ * Make sure libgcc is loaded; otherwise backtrace() might allocate
+ * during a signal handler. (Which is illegal.)
+ */
dummy = NULL;
backtrace(&dummy, 1);
- register_signal_handler(SIGHUP);
- register_signal_handler(SIGINT);
- register_signal_handler(SIGQUIT);
- register_signal_handler(SIGILL);
- register_signal_handler(SIGTRAP);
- register_signal_handler(SIGABRT);
- register_signal_handler(SIGBUS);
- register_signal_handler(SIGFPE);
- /* register_signal_handler(SIGKILL); */
- register_signal_handler(SIGUSR1);
- register_signal_handler(SIGSEGV);
- register_signal_handler(SIGUSR2);
- register_signal_handler(SIGPIPE);
- register_signal_handler(SIGALRM);
- register_signal_handler(SIGTERM);
- register_signal_handler(SIGSTKFLT);
- register_signal_handler(SIGCHLD);
- register_signal_handler(SIGCONT);
- /* register_signal_handler(SIGSTOP); */
- register_signal_handler(SIGTSTP);
- register_signal_handler(SIGTTIN);
- register_signal_handler(SIGTTOU);
- register_signal_handler(SIGURG);
- register_signal_handler(SIGXCPU);
- register_signal_handler(SIGXFSZ);
- register_signal_handler(SIGVTALRM);
- register_signal_handler(SIGPROF);
- register_signal_handler(SIGWINCH);
- register_signal_handler(SIGIO);
- register_signal_handler(SIGPWR);
- register_signal_handler(SIGSYS);
+ /* Register the segmentation fault handler */
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = sigsegv_handler;
+ sigemptyset(&action.sa_mask);
+ action.sa_flags = 0;
+ if (sigaction(SIGSEGV, &action, NULL) == -1) {
+ /*
+ * Not fatal; it just means we will not print stack traces on
+ * Segmentation Faults.
+ */
+ pr_op_errno(errno, "SIGSEGV handler registration failure");
+ }
+
+ /*
+ * SIGPIPE is sometimes triggered by libcurl:
+ *
+ * > libcurl makes an effort to never cause such SIGPIPEs to trigger,
+ * > but some operating systems have no way to avoid them and even on
+ * > those that have there are some corner cases when they may still
+ * > happen
+ * (Documentation of CURLOPT_NOSIGNAL)
+ *
+ * All SIGPIPE usually means is "the server closed the connection for
+ * some reason, fuck you."
+ * Which is a normal I/O error, and should be handled by the normal
+ * error propagation logic, not by a signal handler.
+ * So, ignore SIGPIPE.
+ *
+ * https://github.com/NICMx/FORT-validator/issues/49
+ */
+ if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
+ /*
+ * Not fatal. It just means that, if a broken pipe happens, we
+ * will die silently.
+ * But they're somewhat rare, so whatever.
+ */
+ pr_op_errno(errno, "SIGPIPE handler registration failure");
+ }
}
int
#include "str_token.h"
#include "thread_var.h"
-#define TA_DEBUG \
- do { if (is_ta) PR_DEBUG; } while (0)
-#define TA_DEBUG_MSG(fmt, ...) \
- do { if (is_ta) PR_DEBUG_MSG(fmt, ##__VA_ARGS__); } while (0)
-
struct uri {
struct rpki_uri *uri;
SLIST_ENTRY(uri) next;
int fork_fds[2][2];
pid_t child_pid;
unsigned int retries;
- unsigned int i;
int child_status;
int error;
if (error)
return error;
- TA_DEBUG_MSG("Executing RSYNC:");
- for (i = 0; i < args_len + 1; i++) {
- TA_DEBUG_MSG(" %s", args[i]);
- }
-
retries = 0;
do {
child_status = 0;
if (error)
goto release_args;
- TA_DEBUG;
-
error = create_pipes(fork_fds);
- if (error) {
- TA_DEBUG_MSG("%d", error);
+ if (error)
goto release_args;
- }
/* Flush output (avoid locks between father and child) */
log_flush();
- TA_DEBUG;
-
/* We need to fork because execvp() magics the thread away. */
child_pid = fork();
if (child_pid == 0) {
goto release_args;
}
- TA_DEBUG;
-
/* This code is run by us. */
error = read_pipes(fork_fds, log_operation);
- if (error) {
- TA_DEBUG;
+ if (error)
kill(child_pid, SIGCHLD); /* Stop the child */
- TA_DEBUG;
- }
error = waitpid(child_pid, &child_status, 0);
do {
if (error == -1) {
error = errno;
- TA_DEBUG_MSG("%d", error);
pr_op_err("The rsync sub-process returned error %d (%s)",
error, strerror(error));
if (child_status > 0)
if (WIFEXITED(child_status)) {
/* Happy path (but also sad path sometimes). */
error = WEXITSTATUS(child_status);
- TA_DEBUG_MSG("%d", error);
pr_val_debug("Child terminated with error code %d.",
error);
if (!error)
release_args(args, args_len);
if (WIFSIGNALED(child_status)) {
- TA_DEBUG_MSG("%d", WTERMSIG(child_status));
switch (WTERMSIG(child_status)) {
case SIGINT:
pr_op_err("RSYNC was user-interrupted. Guess I'll interrupt myself too.");
return -EINTR; /* Meh? */
}
- TA_DEBUG_MSG("%d", WIFSIGNALED(child_status));
pr_op_err("The RSYNC command died in a way I don't have a handler for. Dunno; guess I'll die as well.");
return -EINVAL;
release_args:
- TA_DEBUG_MSG("%d", error);
/* The happy path also falls here */
release_args(args, args_len);
return error;
bool to_op_log;
int error;
- if (!config_get_rsync_enabled()) {
- TA_DEBUG;
+ if (!config_get_rsync_enabled())
return 0;
- }
- TA_DEBUG;
state = state_retrieve();
- if (state == NULL) {
- TA_DEBUG;
+ if (state == NULL)
return -EINVAL;
- }
- TA_DEBUG;
visited_uris = validation_rsync_visited_uris(state);
- TA_DEBUG;
-
if (!force && is_already_downloaded(requested_uri, visited_uris)) {
pr_val_debug("No need to redownload '%s'.",
uri_val_get_printable(requested_uri));
- TA_DEBUG;
return check_ancestor_error(requested_uri);
}
if (!force) {
- TA_DEBUG;
error = get_rsync_uri(requested_uri, is_ta, &rsync_uri);
- TA_DEBUG_MSG("%d", error);
} else {
- TA_DEBUG;
error = check_ancestor_error(requested_uri);
if (error) {
- TA_DEBUG;
return error;
}
- TA_DEBUG;
error = handle_strict_strategy(requested_uri, &rsync_uri);
- TA_DEBUG_MSG("%d", error);
}
- if (error) {
- TA_DEBUG;
+ if (error)
return error;
- }
pr_val_debug("Going to RSYNC '%s'.", uri_val_get_printable(rsync_uri));
- TA_DEBUG;
to_op_log = reqs_errors_log_uri(uri_get_global(rsync_uri));
- TA_DEBUG_MSG("%d", to_op_log);
error = do_rsync(rsync_uri, is_ta, to_op_log);
- TA_DEBUG_MSG("%d", error);
switch(error) {
case 0:
/* Don't store when "force" and if its already downloaded */
- if (!(force && is_already_downloaded(rsync_uri, visited_uris))) {
- TA_DEBUG;
+ if (!(force && is_already_downloaded(rsync_uri, visited_uris)))
error = mark_as_downloaded(rsync_uri, visited_uris);
- TA_DEBUG_MSG("%d", error);
- }
reqs_errors_rem_uri(uri_get_global(rsync_uri));
- TA_DEBUG;
break;
case EREQFAILED:
/* All attempts failed, avoid future requests */
error = reqs_errors_add_uri(uri_get_global(rsync_uri));
- if (error) {
- TA_DEBUG_MSG("%d", error);
+ if (error)
break;
- }
- TA_DEBUG;
error = mark_as_downloaded(rsync_uri, visited_uris);
- TA_DEBUG_MSG("%d", error);
/* Everything went ok? Return the original error */
if (!error)
error = EREQFAILED;
}
uri_refput(rsync_uri);
- TA_DEBUG;
return error;
}