|
Packit |
c22fc9 |
/*
|
|
Packit |
c22fc9 |
* Soft: Keepalived is a failover program for the LVS project
|
|
Packit |
c22fc9 |
* <www.linuxvirtualserver.org>. It monitor & manipulate
|
|
Packit |
c22fc9 |
* a loadbalanced server pool using multi-layer checks.
|
|
Packit |
c22fc9 |
*
|
|
Packit |
c22fc9 |
* Part: Scheduling framework. This code is highly inspired from
|
|
Packit |
c22fc9 |
* the thread management routine (thread.c) present in the
|
|
Packit |
c22fc9 |
* very nice zebra project (http://www.zebra.org).
|
|
Packit |
c22fc9 |
*
|
|
Packit |
c22fc9 |
* Author: Alexandre Cassen, <acassen@linux-vs.org>
|
|
Packit |
c22fc9 |
*
|
|
Packit |
c22fc9 |
* This program is distributed in the hope that it will be useful,
|
|
Packit |
c22fc9 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
c22fc9 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
Packit |
c22fc9 |
* See the GNU General Public License for more details.
|
|
Packit |
c22fc9 |
*
|
|
Packit |
c22fc9 |
* This program is free software; you can redistribute it and/or
|
|
Packit |
c22fc9 |
* modify it under the terms of the GNU General Public License
|
|
Packit |
c22fc9 |
* as published by the Free Software Foundation; either version
|
|
Packit |
c22fc9 |
* 2 of the License, or (at your option) any later version.
|
|
Packit |
c22fc9 |
*
|
|
Packit |
c22fc9 |
* Copyright (C) 2001-2017 Alexandre Cassen, <acassen@gmail.com>
|
|
Packit |
c22fc9 |
*/
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#include "config.h"
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* SNMP should be included first: it redefines "FREE" */
|
|
Packit |
c22fc9 |
#ifdef _WITH_SNMP_
|
|
Packit |
c22fc9 |
#include <net-snmp/net-snmp-config.h>
|
|
Packit |
c22fc9 |
#include <net-snmp/net-snmp-includes.h>
|
|
Packit |
c22fc9 |
#include <net-snmp/agent/net-snmp-agent-includes.h>
|
|
Packit |
c22fc9 |
#undef FREE
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifndef _DEBUG_
|
|
Packit |
c22fc9 |
#define NDEBUG
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
#include <assert.h>
|
|
Packit |
c22fc9 |
#include <errno.h>
|
|
Packit |
c22fc9 |
#include <sys/wait.h>
|
|
Packit |
c22fc9 |
#include <sys/timerfd.h>
|
|
Packit |
c22fc9 |
#include <sys/epoll.h>
|
|
Packit |
c22fc9 |
#include <unistd.h>
|
|
Packit |
c22fc9 |
#ifdef HAVE_SIGNALFD
|
|
Packit |
c22fc9 |
#include <sys/signalfd.h>
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
#include <sys/utsname.h>
|
|
Packit |
c22fc9 |
#include <linux/version.h>
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#include "scheduler.h"
|
|
Packit |
c22fc9 |
#include "memory.h"
|
|
Packit |
c22fc9 |
#include "rbtree.h"
|
|
Packit |
c22fc9 |
#include "utils.h"
|
|
Packit |
c22fc9 |
#include "signals.h"
|
|
Packit |
c22fc9 |
#include "logger.h"
|
|
Packit |
c22fc9 |
#include "bitops.h"
|
|
Packit |
c22fc9 |
#include "git-commit.h"
|
|
Packit |
c22fc9 |
#include "timer.h"
|
|
Packit |
c22fc9 |
#if !HAVE_EPOLL_CREATE1 || !defined TFD_NONBLOCK
|
|
Packit |
c22fc9 |
#include "old_socket.h"
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef THREAD_DUMP
|
|
Packit |
c22fc9 |
typedef struct _func_det {
|
|
Packit |
c22fc9 |
const char *name;
|
|
Packit |
c22fc9 |
int (*func)(thread_t *);
|
|
Packit |
c22fc9 |
rb_node_t n;
|
|
Packit |
c22fc9 |
} func_det_t;
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* global vars */
|
|
Packit |
c22fc9 |
thread_master_t *master = NULL;
|
|
Packit |
c22fc9 |
#ifndef _DEBUG_
|
|
Packit |
c22fc9 |
prog_type_t prog_type; /* Parent/VRRP/Checker process */
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
#ifdef _WITH_SNMP_
|
|
Packit |
c22fc9 |
bool snmp_running; /* True if this process is running SNMP */
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* local variables */
|
|
Packit |
c22fc9 |
static bool shutting_down;
|
|
Packit |
c22fc9 |
static int sav_argc;
|
|
Packit |
c22fc9 |
static char **sav_argv;
|
|
Packit |
c22fc9 |
#ifdef _EPOLL_DEBUG_
|
|
Packit |
c22fc9 |
bool do_epoll_debug;
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
#ifdef _EPOLL_THREAD_DUMP_
|
|
Packit |
c22fc9 |
bool do_epoll_thread_dump;
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
#ifdef THREAD_DUMP
|
|
Packit |
c22fc9 |
static rb_root_t funcs = RB_ROOT;
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
#ifdef _VRRP_FD_DEBUG_
|
|
Packit |
c22fc9 |
static void (*extra_threads_debug)(void);
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Function that returns prog_name if pid is a known child */
|
|
Packit |
c22fc9 |
static char const * (*child_finder_name)(pid_t);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef THREAD_DUMP
|
|
Packit |
c22fc9 |
static const char *
|
|
Packit |
c22fc9 |
get_thread_type_str(thread_type_t id)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
if (id == THREAD_READ) return "READ";
|
|
Packit |
c22fc9 |
if (id == THREAD_WRITE) return "WRITE";
|
|
Packit |
c22fc9 |
if (id == THREAD_TIMER) return "TIMER";
|
|
Packit |
c22fc9 |
if (id == THREAD_TIMER_SHUTDOWN) return "TIMER_SHUTDOWN";
|
|
Packit |
c22fc9 |
if (id == THREAD_EVENT) return "EVENT";
|
|
Packit |
c22fc9 |
if (id == THREAD_CHILD) return "CHILD";
|
|
Packit |
c22fc9 |
if (id == THREAD_READY) return "READY";
|
|
Packit |
c22fc9 |
if (id == THREAD_UNUSED) return "UNUSED";
|
|
Packit |
c22fc9 |
if (id == THREAD_WRITE_TIMEOUT) return "WRITE_TIMEOUT";
|
|
Packit |
c22fc9 |
if (id == THREAD_READ_TIMEOUT) return "READ_TIMEOUT";
|
|
Packit |
c22fc9 |
if (id == THREAD_CHILD_TIMEOUT) return "CHILD_TIMEOUT";
|
|
Packit |
c22fc9 |
if (id == THREAD_CHILD_TERMINATED) return "CHILD_TERMINATED";
|
|
Packit |
c22fc9 |
if (id == THREAD_TERMINATE_START) return "TERMINATE_START";
|
|
Packit |
c22fc9 |
if (id == THREAD_TERMINATE) return "TERMINATE";
|
|
Packit |
c22fc9 |
if (id == THREAD_READY_FD) return "READY_FD";
|
|
Packit |
c22fc9 |
if (id == THREAD_READ_ERROR) return "READ_ERROR";
|
|
Packit |
c22fc9 |
if (id == THREAD_WRITE_ERROR) return "WRITE_ERROR";
|
|
Packit |
c22fc9 |
#ifdef USE_SIGNAL_THREADS
|
|
Packit |
c22fc9 |
if (id == THREAD_SIGNAL) return "SIGNAL";
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return "unknown";
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
static inline int
|
|
Packit |
c22fc9 |
function_cmp(const func_det_t *func1, const func_det_t *func2)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
if (func1->func < func2->func)
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
if (func1->func > func2->func)
|
|
Packit |
c22fc9 |
return 1;
|
|
Packit |
c22fc9 |
return 0;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
static const char *
|
|
Packit |
c22fc9 |
get_function_name(int (*func)(thread_t *))
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
func_det_t func_det = { .func = func };
|
|
Packit |
c22fc9 |
func_det_t *match;
|
|
Packit |
c22fc9 |
static char address[19];
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (!RB_EMPTY_ROOT(&funcs)) {
|
|
Packit |
c22fc9 |
match = rb_search(&funcs, &func_det, n, function_cmp);
|
|
Packit |
c22fc9 |
if (match)
|
|
Packit |
c22fc9 |
return match->name;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
snprintf(address, sizeof address, "%p", func);
|
|
Packit |
c22fc9 |
return address;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
const char *
|
|
Packit |
c22fc9 |
get_signal_function_name(void (*func)(void *, int))
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
/* The cast should really be (int (*)(thread_t *))func, but gcc 8.1 produces
|
|
Packit |
c22fc9 |
* a warning with -Wcast-function-type, that the cast is to an incompatible
|
|
Packit |
c22fc9 |
* function type. Since we don't actually call the function, but merely use
|
|
Packit |
c22fc9 |
* it to compare function addresses, what we cast it do doesn't really matter */
|
|
Packit |
c22fc9 |
return get_function_name((void *)func);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
void
|
|
Packit |
c22fc9 |
register_thread_address(const char *func_name, int (*func)(thread_t *))
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
func_det_t *func_det;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
func_det = (func_det_t *) MALLOC(sizeof(func_det_t));
|
|
Packit |
c22fc9 |
if (!func_det)
|
|
Packit |
c22fc9 |
return;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
func_det->name = func_name;
|
|
Packit |
c22fc9 |
func_det->func = func;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
rb_insert_sort(&funcs, func_det, n, function_cmp);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
void
|
|
Packit |
c22fc9 |
register_signal_handler_address(const char *func_name, void (*func)(void *, int))
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
/* See comment in get_signal_function_name() above */
|
|
Packit |
c22fc9 |
register_thread_address(func_name, (void *)func);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
void
|
|
Packit |
c22fc9 |
deregister_thread_addresses(void)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
func_det_t *func_det, *func_det_tmp;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (RB_EMPTY_ROOT(&funcs))
|
|
Packit |
c22fc9 |
return;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
rb_for_each_entry_safe(func_det, func_det_tmp, &funcs, n) {
|
|
Packit |
c22fc9 |
rb_erase(&func_det->n, &funcs);
|
|
Packit |
c22fc9 |
FREE(func_det);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef _VRRP_FD_DEBUG_
|
|
Packit |
c22fc9 |
void
|
|
Packit |
c22fc9 |
set_extra_threads_debug(void (*func)(void))
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
extra_threads_debug = func;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Move ready thread into ready queue */
|
|
Packit |
c22fc9 |
static int
|
|
Packit |
c22fc9 |
thread_move_ready(thread_master_t *m, rb_root_cached_t *root, thread_t *thread, int type)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
rb_erase_cached(&thread->n, root);
|
|
Packit |
c22fc9 |
INIT_LIST_HEAD(&thread->next);
|
|
Packit |
c22fc9 |
list_add_tail(&thread->next, &m->ready);
|
|
Packit |
c22fc9 |
if (thread->type != THREAD_TIMER_SHUTDOWN)
|
|
Packit |
c22fc9 |
thread->type = type;
|
|
Packit |
c22fc9 |
return 0;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Move ready thread into ready queue */
|
|
Packit |
c22fc9 |
static void
|
|
Packit |
c22fc9 |
thread_rb_move_ready(thread_master_t *m, rb_root_cached_t *root, int type)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_t *thread, *thread_tmp;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
rb_for_each_entry_safe_cached(thread, thread_tmp, root, n) {
|
|
Packit |
c22fc9 |
if (thread->sands.tv_sec == TIMER_DISABLED || timercmp(&time_now, &thread->sands, <))
|
|
Packit |
c22fc9 |
break;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (type == THREAD_READ_TIMEOUT)
|
|
Packit |
c22fc9 |
thread->event->read = NULL;
|
|
Packit |
c22fc9 |
else if (type == THREAD_WRITE_TIMEOUT)
|
|
Packit |
c22fc9 |
thread->event->write = NULL;
|
|
Packit |
c22fc9 |
thread_move_ready(m, root, thread, type);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Update timer value */
|
|
Packit |
c22fc9 |
static void
|
|
Packit |
c22fc9 |
thread_update_timer(rb_root_cached_t *root, timeval_t *timer_min)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_t *first;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (!root->rb_root.rb_node)
|
|
Packit |
c22fc9 |
return;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
first = rb_entry(rb_first_cached(root), thread_t, n);
|
|
Packit |
c22fc9 |
if (!first)
|
|
Packit |
c22fc9 |
return;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (first->sands.tv_sec == TIMER_DISABLED)
|
|
Packit |
c22fc9 |
return;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (!timerisset(timer_min) ||
|
|
Packit |
c22fc9 |
timercmp(&first->sands, timer_min, <=))
|
|
Packit |
c22fc9 |
*timer_min = first->sands;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Compute the wait timer. Take care of timeouted fd */
|
|
Packit |
c22fc9 |
static void
|
|
Packit |
c22fc9 |
thread_set_timer(thread_master_t *m)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
timeval_t timer_wait;
|
|
Packit |
c22fc9 |
struct itimerspec its;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Prepare timer */
|
|
Packit |
c22fc9 |
timerclear(&timer_wait);
|
|
Packit |
c22fc9 |
thread_update_timer(&m->timer, &timer_wait);
|
|
Packit |
c22fc9 |
thread_update_timer(&m->write, &timer_wait);
|
|
Packit |
c22fc9 |
thread_update_timer(&m->read, &timer_wait);
|
|
Packit |
c22fc9 |
thread_update_timer(&m->child, &timer_wait);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (timerisset(&timer_wait)) {
|
|
Packit |
c22fc9 |
/* Re-read the current time to get the maximum accuracy */
|
|
Packit |
c22fc9 |
set_time_now();
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Take care about monotonic clock */
|
|
Packit |
c22fc9 |
timersub(&timer_wait, &time_now, &timer_wait);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (timer_wait.tv_sec < 0) {
|
|
Packit |
c22fc9 |
/* This will disable the timerfd */
|
|
Packit |
c22fc9 |
timerclear(&timer_wait);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
} else {
|
|
Packit |
c22fc9 |
/* set timer to a VERY long time */
|
|
Packit |
c22fc9 |
timer_wait.tv_sec = LONG_MAX;
|
|
Packit |
c22fc9 |
timer_wait.tv_usec = 0;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
its.it_value.tv_sec = timer_wait.tv_sec;
|
|
Packit |
c22fc9 |
if (!timerisset(&timer_wait)) {
|
|
Packit |
c22fc9 |
/* We could try to avoid doing the epoll_wait since
|
|
Packit |
c22fc9 |
* testing shows it takes about 4 microseconds
|
|
Packit |
c22fc9 |
* for the timer to expire. */
|
|
Packit |
c22fc9 |
its.it_value.tv_nsec = 1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
else
|
|
Packit |
c22fc9 |
its.it_value.tv_nsec = timer_wait.tv_usec * 1000;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* We don't want periodic timer expiry */
|
|
Packit |
c22fc9 |
its.it_interval.tv_sec = its.it_interval.tv_nsec = 0;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
timerfd_settime(m->timer_fd, 0, &its, NULL);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef _EPOLL_DEBUG_
|
|
Packit |
c22fc9 |
if (do_epoll_debug)
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "Setting timer_fd %lu.%9.9ld", its.it_value.tv_sec, its.it_value.tv_nsec);
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
static int
|
|
Packit |
c22fc9 |
thread_timerfd_handler(thread_t *thread)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_master_t *m = thread->master;
|
|
Packit |
c22fc9 |
uint64_t expired;
|
|
Packit |
c22fc9 |
ssize_t len;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
len = read(m->timer_fd, &expired, sizeof(expired));
|
|
Packit |
c22fc9 |
if (len < 0)
|
|
Packit |
c22fc9 |
log_message(LOG_ERR, "scheduler: Error reading on timerfd fd:%d (%m)", m->timer_fd);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Read, Write, Timer, Child thread. */
|
|
Packit |
c22fc9 |
thread_rb_move_ready(m, &m->read, THREAD_READ_TIMEOUT);
|
|
Packit |
c22fc9 |
thread_rb_move_ready(m, &m->write, THREAD_WRITE_TIMEOUT);
|
|
Packit |
c22fc9 |
thread_rb_move_ready(m, &m->timer, THREAD_READY);
|
|
Packit |
c22fc9 |
thread_rb_move_ready(m, &m->child, THREAD_CHILD_TIMEOUT);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Register next timerfd thread */
|
|
Packit |
c22fc9 |
m->timer_thread = thread_add_read(m, thread_timerfd_handler, NULL, m->timer_fd, TIMER_NEVER);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return 0;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Child PID cmp helper */
|
|
Packit |
c22fc9 |
static inline int
|
|
Packit |
c22fc9 |
thread_child_pid_cmp(thread_t *t1, thread_t *t2)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
return t1->u.c.pid - t2->u.c.pid;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
void
|
|
Packit |
c22fc9 |
set_child_finder_name(char const * (*func)(pid_t))
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
child_finder_name = func;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
void
|
|
Packit |
c22fc9 |
save_cmd_line_options(int argc, char **argv)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
sav_argc = argc;
|
|
Packit |
c22fc9 |
sav_argv = argv;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifndef _DEBUG_
|
|
Packit |
c22fc9 |
static const char *
|
|
Packit |
c22fc9 |
get_end(const char *str, size_t max_len)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
size_t len = strlen(str);
|
|
Packit |
c22fc9 |
const char *end;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (len <= max_len)
|
|
Packit |
c22fc9 |
return str + len;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
end = str + max_len;
|
|
Packit |
c22fc9 |
if (*end == ' ')
|
|
Packit |
c22fc9 |
return end;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
while (end > str && *--end != ' ');
|
|
Packit |
c22fc9 |
if (end > str)
|
|
Packit |
c22fc9 |
return end;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return str + max_len;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
static void
|
|
Packit |
c22fc9 |
log_options(const char *option, const char *option_str, unsigned indent)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
const char *p = option_str;
|
|
Packit |
c22fc9 |
size_t opt_len = strlen(option);
|
|
Packit |
c22fc9 |
const char *end;
|
|
Packit |
c22fc9 |
bool first_line = true;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
while (*p) {
|
|
Packit |
c22fc9 |
/* Skip leading spaces */
|
|
Packit |
c22fc9 |
while (*p == ' ')
|
|
Packit |
c22fc9 |
p++;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
end = get_end(p, 100 - opt_len);
|
|
Packit |
c22fc9 |
if (first_line) {
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "%*s%s: %.*s", indent, "", option, (int)(end - p), p);
|
|
Packit |
c22fc9 |
first_line = false;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
else
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "%*s%.*s", (int)(indent + opt_len + 2), "", (int)(end - p), p);
|
|
Packit |
c22fc9 |
p = end;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
void
|
|
Packit |
c22fc9 |
log_command_line(unsigned indent)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
size_t len = 0;
|
|
Packit |
c22fc9 |
char *log_str;
|
|
Packit |
c22fc9 |
char *p;
|
|
Packit |
c22fc9 |
int i;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (!sav_argv)
|
|
Packit |
c22fc9 |
return;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
for (i = 0; i < sav_argc; i++)
|
|
Packit |
c22fc9 |
len += strlen(sav_argv[i]) + 3; /* Add opening and closing 's, and following space or '\0' */
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
log_str = MALLOC(len);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
for (i = 0, p = log_str; i < sav_argc; i++)
|
|
Packit |
c22fc9 |
p += sprintf(p, "%s'%s'", i ? " " : "", sav_argv[i]);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
log_options("Command line", log_str, indent);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
FREE(log_str);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* report_child_status returns true if the exit is a hard error, so unable to continue */
|
|
Packit |
c22fc9 |
bool
|
|
Packit |
c22fc9 |
report_child_status(int status, pid_t pid, char const *prog_name)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
char const *prog_id = NULL;
|
|
Packit |
c22fc9 |
char pid_buf[12]; /* "pid 4194303" + '\0' - see definition of PID_MAX_LIMIT in include/linux/threads.h */
|
|
Packit |
c22fc9 |
int exit_status ;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (prog_name)
|
|
Packit |
c22fc9 |
prog_id = prog_name;
|
|
Packit |
c22fc9 |
else if (child_finder_name)
|
|
Packit |
c22fc9 |
prog_id = child_finder_name(pid);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (!prog_id) {
|
|
Packit |
c22fc9 |
snprintf(pid_buf, sizeof(pid_buf), "pid %d", pid);
|
|
Packit |
c22fc9 |
prog_id = pid_buf;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (WIFEXITED(status)) {
|
|
Packit |
c22fc9 |
exit_status = WEXITSTATUS(status);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Handle exit codes of vrrp or checker child */
|
|
Packit |
c22fc9 |
if (exit_status == KEEPALIVED_EXIT_FATAL ||
|
|
Packit |
c22fc9 |
exit_status == KEEPALIVED_EXIT_CONFIG) {
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "%s exited with permanent error %s. Terminating", prog_id, exit_status == KEEPALIVED_EXIT_CONFIG ? "CONFIG" : "FATAL" );
|
|
Packit |
c22fc9 |
return true;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (exit_status != EXIT_SUCCESS)
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "%s exited with status %d", prog_id, exit_status);
|
|
Packit |
c22fc9 |
} else if (WIFSIGNALED(status)) {
|
|
Packit |
c22fc9 |
if (WTERMSIG(status) == SIGSEGV) {
|
|
Packit |
c22fc9 |
struct utsname uname_buf;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "%s exited due to segmentation fault (SIGSEGV).", prog_id);
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, " %s", "Please report a bug at https://github.com/acassen/keepalived/issues");
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, " %s", "and include this log from when keepalived started, a description");
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, " %s", "of what happened before the crash, your configuration file and the details below.");
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, " %s", "Also provide the output of keepalived -v, what Linux distro and version");
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, " %s", "you are running on, and whether keepalived is being run in a container or VM.");
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, " %s", "A failure to provide all this information may mean the crash cannot be investigated.");
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, " %s", "If you are able to provide a stack backtrace with gdb that would really help.");
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, " Source version %s %s%s", PACKAGE_VERSION,
|
|
Packit |
c22fc9 |
#ifdef GIT_COMMIT
|
|
Packit |
c22fc9 |
", git commit ", GIT_COMMIT
|
|
Packit |
c22fc9 |
#else
|
|
Packit |
c22fc9 |
"", ""
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
);
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, " Built with kernel headers for Linux %d.%d.%d",
|
|
Packit |
c22fc9 |
(LINUX_VERSION_CODE >> 16) & 0xff,
|
|
Packit |
c22fc9 |
(LINUX_VERSION_CODE >> 8) & 0xff,
|
|
Packit |
c22fc9 |
(LINUX_VERSION_CODE ) & 0xff);
|
|
Packit |
c22fc9 |
uname(&uname_buf);
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, " Running on %s %s %s", uname_buf.sysname, uname_buf.release, uname_buf.version);
|
|
Packit |
c22fc9 |
log_command_line(2);
|
|
Packit |
c22fc9 |
log_options("configure options", KEEPALIVED_CONFIGURE_OPTIONS, 2);
|
|
Packit |
c22fc9 |
log_options("Config options", CONFIGURATION_OPTIONS, 2);
|
|
Packit |
c22fc9 |
log_options("System options", SYSTEM_OPTIONS, 2);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
// if (__test_bit(DONT_RESPAWN_BIT, &debug))
|
|
Packit |
c22fc9 |
// segv_termination = true;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
else
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "%s exited due to signal %d", prog_id, WTERMSIG(status));
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return false;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* epoll related */
|
|
Packit |
c22fc9 |
static int
|
|
Packit |
c22fc9 |
thread_events_resize(thread_master_t *m, int delta)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
unsigned int new_size;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
m->epoll_count += delta;
|
|
Packit |
c22fc9 |
if (m->epoll_count < m->epoll_size)
|
|
Packit |
c22fc9 |
return 0;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
new_size = ((m->epoll_count / THREAD_EPOLL_REALLOC_THRESH) + 1);
|
|
Packit |
c22fc9 |
new_size *= THREAD_EPOLL_REALLOC_THRESH;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (m->epoll_events)
|
|
Packit |
c22fc9 |
FREE(m->epoll_events);
|
|
Packit |
c22fc9 |
m->epoll_events = MALLOC(new_size * sizeof(struct epoll_event));
|
|
Packit |
c22fc9 |
if (!m->epoll_events) {
|
|
Packit |
c22fc9 |
m->epoll_size = 0;
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
m->epoll_size = new_size;
|
|
Packit |
c22fc9 |
return 0;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
static inline int
|
|
Packit |
c22fc9 |
thread_event_cmp(const thread_event_t *event1, const thread_event_t *event2)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
if (event1->fd < event2->fd)
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
if (event1->fd > event2->fd)
|
|
Packit |
c22fc9 |
return 1;
|
|
Packit |
c22fc9 |
return 0;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
static thread_event_t *
|
|
Packit |
c22fc9 |
thread_event_new(thread_master_t *m, int fd)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_event_t *event;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
event = (thread_event_t *) MALLOC(sizeof(thread_event_t));
|
|
Packit |
c22fc9 |
if (!event)
|
|
Packit |
c22fc9 |
return NULL;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (thread_events_resize(m, 1) < 0) {
|
|
Packit |
c22fc9 |
FREE(event);
|
|
Packit |
c22fc9 |
return NULL;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
event->fd = fd;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
rb_insert_sort(&m->io_events, event, n, thread_event_cmp);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return event;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
static thread_event_t *
|
|
Packit |
c22fc9 |
thread_event_get(thread_master_t *m, int fd)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_event_t event = { .fd = fd };
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return rb_search(&m->io_events, &event, n, thread_event_cmp);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
static int
|
|
Packit |
c22fc9 |
thread_event_set(thread_t *thread)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_event_t *event = thread->event;
|
|
Packit |
c22fc9 |
thread_master_t *m = thread->master;
|
|
Packit |
c22fc9 |
struct epoll_event ev;
|
|
Packit |
c22fc9 |
int op;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
memset(&ev, 0, sizeof(struct epoll_event));
|
|
Packit |
c22fc9 |
ev.data.ptr = event;
|
|
Packit |
c22fc9 |
if (__test_bit(THREAD_FL_READ_BIT, &event->flags))
|
|
Packit |
c22fc9 |
ev.events |= EPOLLIN;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (__test_bit(THREAD_FL_WRITE_BIT, &event->flags))
|
|
Packit |
c22fc9 |
ev.events |= EPOLLOUT;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (__test_bit(THREAD_FL_EPOLL_BIT, &event->flags))
|
|
Packit |
c22fc9 |
op = EPOLL_CTL_MOD;
|
|
Packit |
c22fc9 |
else
|
|
Packit |
c22fc9 |
op = EPOLL_CTL_ADD;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (epoll_ctl(m->epoll_fd, op, event->fd, &ev) < 0) {
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "scheduler: Error performing control on EPOLL instance (%m)");
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
__set_bit(THREAD_FL_EPOLL_BIT, &event->flags);
|
|
Packit |
c22fc9 |
return 0;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
static int
|
|
Packit |
c22fc9 |
thread_event_cancel(thread_t *thread)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_event_t *event = thread->event;
|
|
Packit |
c22fc9 |
thread_master_t *m = thread->master;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (!event) {
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "scheduler: Error performing epoll_ctl DEL op no event linked?!");
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (m->epoll_fd != -1 &&
|
|
Packit |
c22fc9 |
epoll_ctl(m->epoll_fd, EPOLL_CTL_DEL, event->fd, NULL) < 0 &&
|
|
Packit |
c22fc9 |
errno != EBADF) {
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "scheduler: Error performing epoll_ctl DEL op for fd:%d (%m)", event->fd);
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
rb_erase(&event->n, &m->io_events);
|
|
Packit |
c22fc9 |
if (event == m->current_event)
|
|
Packit |
c22fc9 |
m->current_event = NULL;
|
|
Packit |
c22fc9 |
FREE(thread->event);
|
|
Packit |
c22fc9 |
return 0;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
static int
|
|
Packit |
c22fc9 |
thread_event_del(thread_t *thread, unsigned flag)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_event_t *event = thread->event;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (!__test_bit(flag, &event->flags))
|
|
Packit |
c22fc9 |
return 0;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (flag == THREAD_FL_EPOLL_READ_BIT) {
|
|
Packit |
c22fc9 |
__clear_bit(THREAD_FL_READ_BIT, &event->flags);
|
|
Packit |
c22fc9 |
if (!__test_bit(THREAD_FL_EPOLL_WRITE_BIT, &event->flags))
|
|
Packit |
c22fc9 |
return thread_event_cancel(thread);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
event->read = NULL;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
else if (flag == THREAD_FL_EPOLL_WRITE_BIT) {
|
|
Packit |
c22fc9 |
__clear_bit(THREAD_FL_WRITE_BIT, &event->flags);
|
|
Packit |
c22fc9 |
if (!__test_bit(THREAD_FL_EPOLL_READ_BIT, &event->flags))
|
|
Packit |
c22fc9 |
return thread_event_cancel(thread);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
event->write = NULL;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (thread_event_set(thread) < 0)
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
__clear_bit(flag, &event->flags);
|
|
Packit |
c22fc9 |
return 0;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Make thread master. */
|
|
Packit |
c22fc9 |
thread_master_t *
|
|
Packit |
c22fc9 |
thread_make_master(void)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_master_t *new;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
new = (thread_master_t *) MALLOC(sizeof (thread_master_t));
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#if HAVE_EPOLL_CREATE1
|
|
Packit |
c22fc9 |
new->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
|
|
Packit |
c22fc9 |
#else
|
|
Packit |
c22fc9 |
new->epoll_fd = epoll_create(1);
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
if (new->epoll_fd < 0) {
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "scheduler: Error creating EPOLL instance (%m)");
|
|
Packit |
c22fc9 |
FREE(new);
|
|
Packit |
c22fc9 |
return NULL;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#if !HAVE_EPOLL_CREATE1
|
|
Packit |
c22fc9 |
if (set_sock_flags(new->epoll_fd, F_SETFD, FD_CLOEXEC))
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "Unable to set CLOEXEC on epoll_fd - %s (%d)", strerror(errno), errno);
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
new->read = RB_ROOT_CACHED;
|
|
Packit |
c22fc9 |
new->write = RB_ROOT_CACHED;
|
|
Packit |
c22fc9 |
new->timer = RB_ROOT_CACHED;
|
|
Packit |
c22fc9 |
new->child = RB_ROOT_CACHED;
|
|
Packit |
c22fc9 |
new->io_events = RB_ROOT;
|
|
Packit |
c22fc9 |
new->child_pid = RB_ROOT;
|
|
Packit |
c22fc9 |
INIT_LIST_HEAD(&new->event);
|
|
Packit |
c22fc9 |
#ifdef USE_SIGNAL_THREADS
|
|
Packit |
c22fc9 |
INIT_LIST_HEAD(&new->signal);
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
INIT_LIST_HEAD(&new->ready);
|
|
Packit |
c22fc9 |
INIT_LIST_HEAD(&new->unuse);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Register timerfd thread */
|
|
Packit |
c22fc9 |
new->timer_fd = timerfd_create(CLOCK_MONOTONIC,
|
|
Packit |
c22fc9 |
#ifdef TFD_NONBLOCK /* Since Linux 2.6.27 */
|
|
Packit |
c22fc9 |
TFD_NONBLOCK | TFD_CLOEXEC
|
|
Packit |
c22fc9 |
#else
|
|
Packit |
c22fc9 |
0
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
);
|
|
Packit |
c22fc9 |
if (new->timer_fd < 0) {
|
|
Packit |
c22fc9 |
log_message(LOG_ERR, "scheduler: Cant create timerfd (%m)");
|
|
Packit |
c22fc9 |
FREE(new);
|
|
Packit |
c22fc9 |
return NULL;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifndef TFD_NONBLOCK
|
|
Packit |
c22fc9 |
if (set_sock_flags(new->timer_fd, F_SETFL, O_NONBLOCK))
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "Unable to set NONBLOCK on timer_fd - %s (%d)", strerror(errno), errno);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (set_sock_flags(new->timer_fd, F_SETFD, FD_CLOEXEC))
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "Unable to set CLOEXEC on timer_fd - %s (%d)", strerror(errno), errno);
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
new->signal_fd = signal_handler_init();
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
new->timer_thread = thread_add_read(new, thread_timerfd_handler, NULL, new->timer_fd, TIMER_NEVER);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
add_signal_read_thread(new);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return new;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef THREAD_DUMP
|
|
Packit |
c22fc9 |
static char *
|
|
Packit |
c22fc9 |
timer_delay(timeval_t sands)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
static char str[42];
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (sands.tv_sec == TIMER_DISABLED)
|
|
Packit |
c22fc9 |
return "NEVER";
|
|
Packit |
c22fc9 |
if (sands.tv_sec == 0 && sands.tv_usec == 0)
|
|
Packit |
c22fc9 |
return "UNSET";
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (timercmp(&sands, &time_now, >=)) {
|
|
Packit |
c22fc9 |
sands = timer_sub_now(sands);
|
|
Packit |
c22fc9 |
snprintf(str, sizeof str, "%lu.%6.6ld", sands.tv_sec, sands.tv_usec);
|
|
Packit |
c22fc9 |
} else {
|
|
Packit |
c22fc9 |
timersub(&time_now, &sands, &sands);
|
|
Packit |
c22fc9 |
snprintf(str, sizeof str, "-%lu.%6.6ld", sands.tv_sec, sands.tv_usec);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return str;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Dump rbtree */
|
|
Packit |
c22fc9 |
static void
|
|
Packit |
c22fc9 |
thread_rb_dump(rb_root_cached_t *root, const char *tree, FILE *fp)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_t *thread;
|
|
Packit |
c22fc9 |
int i = 1;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
conf_write(fp, "----[ Begin rb_dump %s ]----", tree);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
rb_for_each_entry_cached(thread, root, n)
|
|
Packit |
c22fc9 |
conf_write(fp, "#%.2d Thread type %s, event_fd %d, val/fd/pid %d, timer: %s, func %s(), id %ld", i++, get_thread_type_str(thread->type), thread->event ? thread->event->fd: -2, thread->u.val, timer_delay(thread->sands), get_function_name(thread->func), thread->id);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
conf_write(fp, "----[ End rb_dump ]----");
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
static void
|
|
Packit |
c22fc9 |
thread_list_dump(list_head_t *l, const char *list, FILE *fp)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_t *thread;
|
|
Packit |
c22fc9 |
int i = 1;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
conf_write(fp, "----[ Begin list_dump %s ]----", list);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
list_for_each_entry(thread, l, next)
|
|
Packit |
c22fc9 |
conf_write(fp, "#%.2d Thread:%p type %s func %s() id %ld",
|
|
Packit |
c22fc9 |
i++, thread, get_thread_type_str(thread->type), get_function_name(thread->func), thread->id);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
conf_write(fp, "----[ End list_dump ]----");
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
static void
|
|
Packit |
c22fc9 |
event_rb_dump(rb_root_t *root, const char *tree, FILE *fp)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_event_t *event;
|
|
Packit |
c22fc9 |
int i = 1;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
conf_write(fp, "----[ Begin rb_dump %s ]----", tree);
|
|
Packit |
c22fc9 |
rb_for_each_entry(event, root, n)
|
|
Packit |
c22fc9 |
conf_write(fp, "#%.2d event %p fd %d, flags: 0x%lx, read %p, write %p", i++, event, event->fd, event->flags, event->read, event->write);
|
|
Packit |
c22fc9 |
conf_write(fp, "----[ End rb_dump ]----");
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
void
|
|
Packit |
c22fc9 |
dump_thread_data(thread_master_t *m, FILE *fp)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_rb_dump(&m->read, "read", fp);
|
|
Packit |
c22fc9 |
thread_rb_dump(&m->write, "write", fp);
|
|
Packit |
c22fc9 |
thread_rb_dump(&m->child, "child", fp);
|
|
Packit |
c22fc9 |
thread_rb_dump(&m->timer, "timer", fp);
|
|
Packit |
c22fc9 |
thread_list_dump(&m->event, "event", fp);
|
|
Packit |
c22fc9 |
thread_list_dump(&m->ready, "ready", fp);
|
|
Packit |
c22fc9 |
#ifdef USE_SIGNAL_THREADS
|
|
Packit |
c22fc9 |
thread_list_dump(&m->signal, "signal", fp);
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
thread_list_dump(&m->unuse, "unuse", fp);
|
|
Packit |
c22fc9 |
event_rb_dump(&m->io_events, "io_events", fp);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* declare thread_timer_cmp() for rbtree compares */
|
|
Packit |
c22fc9 |
RB_TIMER_CMP(thread);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Free all unused thread. */
|
|
Packit |
c22fc9 |
static void
|
|
Packit |
c22fc9 |
thread_clean_unuse(thread_master_t * m)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_t *thread, *thread_tmp;
|
|
Packit |
c22fc9 |
list_head_t *l = &m->unuse;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
list_for_each_entry_safe(thread, thread_tmp, l, next) {
|
|
Packit |
c22fc9 |
list_head_del(&thread->next);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* free the thread */
|
|
Packit |
c22fc9 |
FREE(thread);
|
|
Packit |
c22fc9 |
m->alloc--;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
INIT_LIST_HEAD(l);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Move thread to unuse list. */
|
|
Packit |
c22fc9 |
static void
|
|
Packit |
c22fc9 |
thread_add_unuse(thread_master_t *m, thread_t *thread)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
assert(m != NULL);
|
|
Packit |
c22fc9 |
thread->type = THREAD_UNUSED;
|
|
Packit |
c22fc9 |
thread->event = NULL;
|
|
Packit |
c22fc9 |
INIT_LIST_HEAD(&thread->next);
|
|
Packit |
c22fc9 |
list_add_tail(&thread->next, &m->unuse);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Move list element to unuse queue */
|
|
Packit |
c22fc9 |
static void
|
|
Packit |
c22fc9 |
thread_destroy_list(thread_master_t *m, list_head_t *l)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_t *thread, *thread_tmp;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
list_for_each_entry_safe(thread, thread_tmp, l, next) {
|
|
Packit |
c22fc9 |
if (thread->event) {
|
|
Packit |
c22fc9 |
thread_del_read(thread);
|
|
Packit |
c22fc9 |
thread_del_write(thread);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
list_head_del(&thread->next);
|
|
Packit |
c22fc9 |
thread_add_unuse(m, thread);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
static void
|
|
Packit |
c22fc9 |
thread_destroy_rb(thread_master_t *m, rb_root_cached_t *root)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_t *thread, *thread_tmp;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
rb_for_each_entry_safe_cached(thread, thread_tmp, root, n) {
|
|
Packit |
c22fc9 |
rb_erase_cached(&thread->n, root);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Do we have a thread_event, and does it need deleting? */
|
|
Packit |
c22fc9 |
if (thread->type == THREAD_READ)
|
|
Packit |
c22fc9 |
thread_del_read(thread);
|
|
Packit |
c22fc9 |
else if (thread->type == THREAD_WRITE)
|
|
Packit |
c22fc9 |
thread_del_write(thread);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
thread_add_unuse(m, thread);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Cleanup master */
|
|
Packit |
c22fc9 |
void
|
|
Packit |
c22fc9 |
thread_cleanup_master(thread_master_t * m)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
/* Unuse current thread lists */
|
|
Packit |
c22fc9 |
thread_destroy_rb(m, &m->read);
|
|
Packit |
c22fc9 |
thread_destroy_rb(m, &m->write);
|
|
Packit |
c22fc9 |
thread_destroy_rb(m, &m->timer);
|
|
Packit |
c22fc9 |
thread_destroy_rb(m, &m->child);
|
|
Packit |
c22fc9 |
thread_destroy_list(m, &m->event);
|
|
Packit |
c22fc9 |
#ifdef USE_SIGNAL_THREADS
|
|
Packit |
c22fc9 |
thread_destroy_list(m, &m->signal);
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
thread_destroy_list(m, &m->ready);
|
|
Packit |
c22fc9 |
m->child_pid = RB_ROOT;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Clean garbage */
|
|
Packit |
c22fc9 |
thread_clean_unuse(m);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
FREE(m->epoll_events);
|
|
Packit |
c22fc9 |
m->epoll_size = 0;
|
|
Packit |
c22fc9 |
m->epoll_count = 0;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
m->timer_thread = NULL;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef _WITH_SNMP_
|
|
Packit |
c22fc9 |
m->snmp_timer_thread = NULL;
|
|
Packit |
c22fc9 |
FD_ZERO(&m->snmp_fdset);
|
|
Packit |
c22fc9 |
m->snmp_fdsetsize = 0;
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Stop thread scheduler. */
|
|
Packit |
c22fc9 |
void
|
|
Packit |
c22fc9 |
thread_destroy_master(thread_master_t * m)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
if (m->epoll_fd != -1) {
|
|
Packit |
c22fc9 |
close(m->epoll_fd);
|
|
Packit |
c22fc9 |
m->epoll_fd = -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (m->timer_fd != -1)
|
|
Packit |
c22fc9 |
close(m->timer_fd);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (m->signal_fd != -1)
|
|
Packit |
c22fc9 |
signal_handler_destroy();
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
thread_cleanup_master(m);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
FREE(m);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Delete top of the list and return it. */
|
|
Packit |
c22fc9 |
static thread_t *
|
|
Packit |
c22fc9 |
thread_trim_head(list_head_t *l)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_t *thread;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (list_empty(l))
|
|
Packit |
c22fc9 |
return NULL;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
thread = list_first_entry(l, thread_t, next);
|
|
Packit |
c22fc9 |
list_del_init(&thread->next);
|
|
Packit |
c22fc9 |
return thread;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Make unique thread id for non pthread version of thread manager. */
|
|
Packit |
c22fc9 |
static inline unsigned long
|
|
Packit |
c22fc9 |
thread_get_id(thread_master_t *m)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
return m->id++;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Make new thread. */
|
|
Packit |
c22fc9 |
static thread_t *
|
|
Packit |
c22fc9 |
thread_new(thread_master_t *m)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_t *new;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* If one thread is already allocated return it */
|
|
Packit |
c22fc9 |
new = thread_trim_head(&m->unuse);
|
|
Packit |
c22fc9 |
if (!new) {
|
|
Packit |
c22fc9 |
new = (thread_t *)MALLOC(sizeof(thread_t));
|
|
Packit |
c22fc9 |
m->alloc++;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
INIT_LIST_HEAD(&new->next);
|
|
Packit |
c22fc9 |
new->id = thread_get_id(m);
|
|
Packit |
c22fc9 |
return new;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Add new read thread. */
|
|
Packit |
c22fc9 |
thread_t *
|
|
Packit |
c22fc9 |
thread_add_read_sands(thread_master_t *m, int (*func) (thread_t *), void *arg, int fd, timeval_t *sands)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_event_t *event;
|
|
Packit |
c22fc9 |
thread_t *thread;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
assert(m != NULL);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* I feel lucky ! :D */
|
|
Packit |
c22fc9 |
if (m->current_event && m->current_event->fd == fd)
|
|
Packit |
c22fc9 |
event = m->current_event;
|
|
Packit |
c22fc9 |
else
|
|
Packit |
c22fc9 |
event = thread_event_get(m, fd);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (!event) {
|
|
Packit |
c22fc9 |
if (!(event = thread_event_new(m, fd))) {
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "scheduler: Cant allocate read event for fd [%d](%m)", fd);
|
|
Packit |
c22fc9 |
return NULL;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
else if (__test_bit(THREAD_FL_READ_BIT, &event->flags) && event->read) {
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "scheduler: There is already read event %p (read %p) registered on fd [%d]", event, event->read, fd);
|
|
Packit |
c22fc9 |
return NULL;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
thread = thread_new(m);
|
|
Packit |
c22fc9 |
thread->type = THREAD_READ;
|
|
Packit |
c22fc9 |
thread->master = m;
|
|
Packit |
c22fc9 |
thread->func = func;
|
|
Packit |
c22fc9 |
thread->arg = arg;
|
|
Packit |
c22fc9 |
thread->u.fd = fd;
|
|
Packit |
c22fc9 |
thread->event = event;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Set & flag event */
|
|
Packit |
c22fc9 |
__set_bit(THREAD_FL_READ_BIT, &event->flags);
|
|
Packit |
c22fc9 |
event->read = thread;
|
|
Packit |
c22fc9 |
if (!__test_bit(THREAD_FL_EPOLL_READ_BIT, &event->flags)) {
|
|
Packit |
c22fc9 |
if (thread_event_set(thread) < 0) {
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "scheduler: Cant register read event for fd [%d](%m)", fd);
|
|
Packit |
c22fc9 |
thread_add_unuse(m, thread);
|
|
Packit |
c22fc9 |
return NULL;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
__set_bit(THREAD_FL_EPOLL_READ_BIT, &event->flags);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
thread->sands = *sands;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Sort the thread. */
|
|
Packit |
c22fc9 |
rb_insert_sort_cached(&m->read, thread, n, thread_timer_cmp);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return thread;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
thread_t *
|
|
Packit |
c22fc9 |
thread_add_read(thread_master_t *m, int (*func) (thread_t *), void *arg, int fd, unsigned long timer)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
timeval_t sands;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Compute read timeout value */
|
|
Packit |
c22fc9 |
if (timer == TIMER_NEVER)
|
|
Packit |
c22fc9 |
sands.tv_sec = TIMER_DISABLED;
|
|
Packit |
c22fc9 |
else {
|
|
Packit |
c22fc9 |
set_time_now();
|
|
Packit |
c22fc9 |
sands = timer_add_long(time_now, timer);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return thread_add_read_sands(m, func, arg, fd, &sands);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
int
|
|
Packit |
c22fc9 |
thread_del_read(thread_t *thread)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
if (!thread || !thread->event)
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (thread_event_del(thread, THREAD_FL_EPOLL_READ_BIT) < 0)
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return 0;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef _WITH_SNMP_
|
|
Packit |
c22fc9 |
static void
|
|
Packit |
c22fc9 |
thread_del_read_fd(thread_master_t *m, int fd)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_event_t *event;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
event = thread_event_get(m, fd);
|
|
Packit |
c22fc9 |
if (!event || !event->read)
|
|
Packit |
c22fc9 |
return;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
thread_cancel(event->read);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
static void
|
|
Packit |
c22fc9 |
thread_read_requeue(thread_master_t *m, int fd, const timeval_t *new_sands)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_t *thread;
|
|
Packit |
c22fc9 |
thread_event_t *event;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
event = thread_event_get(m, fd);
|
|
Packit |
c22fc9 |
if (!event || !event->read)
|
|
Packit |
c22fc9 |
return;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
thread = event->read;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (thread->type != THREAD_READ) {
|
|
Packit |
c22fc9 |
/* If the thread is not on the read list, don't touch it */
|
|
Packit |
c22fc9 |
return;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
thread->sands = *new_sands;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
rb_move_cached(&thread->master->read, thread, n, thread_timer_cmp);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
void
|
|
Packit |
c22fc9 |
thread_requeue_read(thread_master_t *m, int fd, const timeval_t *sands)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_read_requeue(m, fd, sands);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Add new write thread. */
|
|
Packit |
c22fc9 |
thread_t *
|
|
Packit |
c22fc9 |
thread_add_write(thread_master_t *m, int (*func) (thread_t *), void *arg, int fd, unsigned long timer)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_event_t *event;
|
|
Packit |
c22fc9 |
thread_t *thread;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
assert(m != NULL);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* I feel lucky ! :D */
|
|
Packit |
c22fc9 |
if (m->current_event && m->current_event->fd == fd)
|
|
Packit |
c22fc9 |
event = m->current_event;
|
|
Packit |
c22fc9 |
else
|
|
Packit |
c22fc9 |
event = thread_event_get(m, fd);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (!event) {
|
|
Packit |
c22fc9 |
if (!(event = thread_event_new(m, fd))) {
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "scheduler: Cant allocate write event for fd [%d](%m)", fd);
|
|
Packit |
c22fc9 |
return NULL;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
else if (__test_bit(THREAD_FL_WRITE_BIT, &event->flags) && event->write) {
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "scheduler: There is already write event registered on fd [%d]", fd);
|
|
Packit |
c22fc9 |
return NULL;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
thread = thread_new(m);
|
|
Packit |
c22fc9 |
thread->type = THREAD_WRITE;
|
|
Packit |
c22fc9 |
thread->master = m;
|
|
Packit |
c22fc9 |
thread->func = func;
|
|
Packit |
c22fc9 |
thread->arg = arg;
|
|
Packit |
c22fc9 |
thread->u.fd = fd;
|
|
Packit |
c22fc9 |
thread->event = event;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Set & flag event */
|
|
Packit |
c22fc9 |
__set_bit(THREAD_FL_WRITE_BIT, &event->flags);
|
|
Packit |
c22fc9 |
event->write = thread;
|
|
Packit |
c22fc9 |
if (!__test_bit(THREAD_FL_EPOLL_WRITE_BIT, &event->flags)) {
|
|
Packit |
c22fc9 |
if (thread_event_set(thread) < 0) {
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "scheduler: Cant register write event for fd [%d](%m)" , fd);
|
|
Packit |
c22fc9 |
thread_add_unuse(m, thread);
|
|
Packit |
c22fc9 |
return NULL;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
__set_bit(THREAD_FL_EPOLL_WRITE_BIT, &event->flags);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Compute write timeout value */
|
|
Packit |
c22fc9 |
if (timer == TIMER_NEVER)
|
|
Packit |
c22fc9 |
thread->sands.tv_sec = TIMER_DISABLED;
|
|
Packit |
c22fc9 |
else {
|
|
Packit |
c22fc9 |
set_time_now();
|
|
Packit |
c22fc9 |
thread->sands = timer_add_long(time_now, timer);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Sort the thread. */
|
|
Packit |
c22fc9 |
rb_insert_sort_cached(&m->write, thread, n, thread_timer_cmp);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return thread;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
int
|
|
Packit |
c22fc9 |
thread_del_write(thread_t *thread)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
if (!thread || !thread->event)
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (thread_event_del(thread, THREAD_FL_EPOLL_WRITE_BIT) < 0)
|
|
Packit |
c22fc9 |
return -1;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return 0;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
void
|
|
Packit |
c22fc9 |
thread_close_fd(thread_t *thread)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
if (thread->u.fd == -1)
|
|
Packit |
c22fc9 |
return;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (thread->event)
|
|
Packit |
c22fc9 |
thread_event_cancel(thread);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
close(thread->u.fd);
|
|
Packit |
c22fc9 |
thread->u.fd = -1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Add timer event thread. */
|
|
Packit |
c22fc9 |
thread_t *
|
|
Packit |
c22fc9 |
thread_add_timer(thread_master_t *m, int (*func) (thread_t *), void *arg, unsigned long timer)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_t *thread;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
assert(m != NULL);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
thread = thread_new(m);
|
|
Packit |
c22fc9 |
thread->type = THREAD_TIMER;
|
|
Packit |
c22fc9 |
thread->master = m;
|
|
Packit |
c22fc9 |
thread->func = func;
|
|
Packit |
c22fc9 |
thread->arg = arg;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Do we need jitter here? */
|
|
Packit |
c22fc9 |
if (timer == TIMER_NEVER)
|
|
Packit |
c22fc9 |
thread->sands.tv_sec = TIMER_DISABLED;
|
|
Packit |
c22fc9 |
else {
|
|
Packit |
c22fc9 |
set_time_now();
|
|
Packit |
c22fc9 |
thread->sands = timer_add_long(time_now, timer);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Sort by timeval. */
|
|
Packit |
c22fc9 |
rb_insert_sort_cached(&m->timer, thread, n, thread_timer_cmp);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return thread;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
void
|
|
Packit |
c22fc9 |
timer_thread_update_timeout(thread_t *thread, unsigned long timer)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
timeval_t sands;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (thread->type > THREAD_MAX_WAITING) {
|
|
Packit |
c22fc9 |
/* It is probably on the ready list, so we'd better just let it run */
|
|
Packit |
c22fc9 |
return;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
set_time_now();
|
|
Packit |
c22fc9 |
sands = timer_add_long(time_now, timer);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (timercmp(&thread->sands, &sands, ==))
|
|
Packit |
c22fc9 |
return;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
thread->sands = sands;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
rb_move_cached(&thread->master->timer, thread, n, thread_timer_cmp);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
thread_t *
|
|
Packit |
c22fc9 |
thread_add_timer_shutdown(thread_master_t *m, int(*func)(thread_t *), void *arg, unsigned long timer)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_t *thread = thread_add_timer(m, func, arg, timer);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
thread->type = THREAD_TIMER_SHUTDOWN;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return thread;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Add a child thread. */
|
|
Packit |
c22fc9 |
thread_t *
|
|
Packit |
c22fc9 |
thread_add_child(thread_master_t * m, int (*func) (thread_t *), void * arg, pid_t pid, unsigned long timer)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_t *thread;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
assert(m != NULL);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
thread = thread_new(m);
|
|
Packit |
c22fc9 |
thread->type = THREAD_CHILD;
|
|
Packit |
c22fc9 |
thread->master = m;
|
|
Packit |
c22fc9 |
thread->func = func;
|
|
Packit |
c22fc9 |
thread->arg = arg;
|
|
Packit |
c22fc9 |
thread->u.c.pid = pid;
|
|
Packit |
c22fc9 |
thread->u.c.status = 0;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Compute child timeout value */
|
|
Packit |
c22fc9 |
if (timer == TIMER_NEVER)
|
|
Packit |
c22fc9 |
thread->sands.tv_sec = TIMER_DISABLED;
|
|
Packit |
c22fc9 |
else {
|
|
Packit |
c22fc9 |
set_time_now();
|
|
Packit |
c22fc9 |
thread->sands = timer_add_long(time_now, timer);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Sort by timeval. */
|
|
Packit |
c22fc9 |
rb_insert_sort_cached(&m->child, thread, n, thread_timer_cmp);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Sort by PID */
|
|
Packit |
c22fc9 |
rb_insert_sort(&m->child_pid, thread, rb_data, thread_child_pid_cmp);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return thread;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
void
|
|
Packit |
c22fc9 |
thread_children_reschedule(thread_master_t *m, int (*func)(thread_t *), unsigned long timer)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_t *thread;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
// What is this used for ??
|
|
Packit |
c22fc9 |
set_time_now();
|
|
Packit |
c22fc9 |
rb_for_each_entry_cached(thread, &m->child, n) {
|
|
Packit |
c22fc9 |
thread->func = func;
|
|
Packit |
c22fc9 |
thread->sands = timer_add_long(time_now, timer);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Add simple event thread. */
|
|
Packit |
c22fc9 |
thread_t *
|
|
Packit |
c22fc9 |
thread_add_event(thread_master_t * m, int (*func) (thread_t *), void *arg, int val)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_t *thread;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
assert(m != NULL);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
thread = thread_new(m);
|
|
Packit |
c22fc9 |
thread->type = THREAD_EVENT;
|
|
Packit |
c22fc9 |
thread->master = m;
|
|
Packit |
c22fc9 |
thread->func = func;
|
|
Packit |
c22fc9 |
thread->arg = arg;
|
|
Packit |
c22fc9 |
thread->u.val = val;
|
|
Packit |
c22fc9 |
INIT_LIST_HEAD(&thread->next);
|
|
Packit |
c22fc9 |
list_add_tail(&thread->next, &m->event);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return thread;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Add terminate event thread. */
|
|
Packit |
c22fc9 |
static thread_t *
|
|
Packit |
c22fc9 |
thread_add_generic_terminate_event(thread_master_t * m, thread_type_t type, int (*func)(thread_t *))
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_t *thread;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
assert(m != NULL);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
thread = thread_new(m);
|
|
Packit |
c22fc9 |
thread->type = type;
|
|
Packit |
c22fc9 |
thread->master = m;
|
|
Packit |
c22fc9 |
thread->func = func;
|
|
Packit |
c22fc9 |
thread->arg = NULL;
|
|
Packit |
c22fc9 |
thread->u.val = 0;
|
|
Packit |
c22fc9 |
INIT_LIST_HEAD(&thread->next);
|
|
Packit |
c22fc9 |
list_add_tail(&thread->next, &m->event);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return thread;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
thread_t *
|
|
Packit |
c22fc9 |
thread_add_terminate_event(thread_master_t *m)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
return thread_add_generic_terminate_event(m, THREAD_TERMINATE, NULL);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
thread_t *
|
|
Packit |
c22fc9 |
thread_add_start_terminate_event(thread_master_t *m, int(*func)(thread_t *))
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
return thread_add_generic_terminate_event(m, THREAD_TERMINATE_START, func);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef USE_SIGNAL_THREADS
|
|
Packit |
c22fc9 |
/* Add signal thread. */
|
|
Packit |
c22fc9 |
thread_t *
|
|
Packit |
c22fc9 |
thread_add_signal(thread_master_t *m, int (*func) (thread_t *), void *arg, int signum)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_t *thread;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
assert(m != NULL);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
thread = thread_new(m);
|
|
Packit |
c22fc9 |
thread->type = THREAD_SIGNAL;
|
|
Packit |
c22fc9 |
thread->master = m;
|
|
Packit |
c22fc9 |
thread->func = func;
|
|
Packit |
c22fc9 |
thread->arg = arg;
|
|
Packit |
c22fc9 |
thread->u.val = signum;
|
|
Packit |
c22fc9 |
INIT_LIST_HEAD(&thread->next);
|
|
Packit |
c22fc9 |
list_add_tail(&thread->next, &m->signal);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Update signalfd accordingly */
|
|
Packit |
c22fc9 |
if (sigismember(&m->signal_mask, signum))
|
|
Packit |
c22fc9 |
return thread;
|
|
Packit |
c22fc9 |
sigaddset(&m->signal_mask, signum);
|
|
Packit |
c22fc9 |
signalfd(m->signal_fd, &m->signal_mask, 0);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return thread;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Cancel thread from scheduler. */
|
|
Packit |
c22fc9 |
void
|
|
Packit |
c22fc9 |
thread_cancel(thread_t *thread)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_master_t *m;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (!thread || thread->type == THREAD_UNUSED)
|
|
Packit |
c22fc9 |
return;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
m = thread->master;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
switch (thread->type) {
|
|
Packit |
c22fc9 |
case THREAD_READ:
|
|
Packit |
c22fc9 |
thread_event_del(thread, THREAD_FL_EPOLL_READ_BIT);
|
|
Packit |
c22fc9 |
rb_erase_cached(&thread->n, &m->read);
|
|
Packit |
c22fc9 |
break;
|
|
Packit |
c22fc9 |
case THREAD_WRITE:
|
|
Packit |
c22fc9 |
thread_event_del(thread, THREAD_FL_EPOLL_WRITE_BIT);
|
|
Packit |
c22fc9 |
rb_erase_cached(&thread->n, &m->write);
|
|
Packit |
c22fc9 |
break;
|
|
Packit |
c22fc9 |
case THREAD_TIMER:
|
|
Packit |
c22fc9 |
rb_erase_cached(&thread->n, &m->timer);
|
|
Packit |
c22fc9 |
break;
|
|
Packit |
c22fc9 |
case THREAD_CHILD:
|
|
Packit |
c22fc9 |
/* Does this need to kill the child, or is that the
|
|
Packit |
c22fc9 |
* caller's job?
|
|
Packit |
c22fc9 |
* This function is currently unused, so leave it for now.
|
|
Packit |
c22fc9 |
*/
|
|
Packit |
c22fc9 |
rb_erase_cached(&thread->n, &m->child);
|
|
Packit |
c22fc9 |
rb_erase(&thread->rb_data, &m->child_pid);
|
|
Packit |
c22fc9 |
break;
|
|
Packit |
c22fc9 |
case THREAD_READY_FD:
|
|
Packit |
c22fc9 |
case THREAD_READ_TIMEOUT:
|
|
Packit |
c22fc9 |
case THREAD_WRITE_TIMEOUT:
|
|
Packit |
c22fc9 |
if (thread->event) {
|
|
Packit |
c22fc9 |
rb_erase(&thread->event->n, &m->io_events);
|
|
Packit |
c22fc9 |
FREE(thread->event);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
/* ... falls through ... */
|
|
Packit |
c22fc9 |
case THREAD_EVENT:
|
|
Packit |
c22fc9 |
case THREAD_READY:
|
|
Packit |
c22fc9 |
#ifdef USE_SIGNAL_THREADS
|
|
Packit |
c22fc9 |
case THREAD_SIGNAL:
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
case THREAD_CHILD_TIMEOUT:
|
|
Packit |
c22fc9 |
case THREAD_CHILD_TERMINATED:
|
|
Packit |
c22fc9 |
list_head_del(&thread->next);
|
|
Packit |
c22fc9 |
break;
|
|
Packit |
c22fc9 |
default:
|
|
Packit |
c22fc9 |
break;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
thread_add_unuse(m, thread);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
void
|
|
Packit |
c22fc9 |
thread_cancel_read(thread_master_t *m, int fd)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_t *thread, *thread_tmp;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
rb_for_each_entry_safe_cached(thread, thread_tmp, &m->read, n) {
|
|
Packit |
c22fc9 |
if (thread->u.fd == fd) {
|
|
Packit |
c22fc9 |
if (thread->event->write) {
|
|
Packit |
c22fc9 |
thread_cancel(thread->event->write);
|
|
Packit |
c22fc9 |
thread->event->write = NULL;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
thread_cancel(thread);
|
|
Packit |
c22fc9 |
break;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef _INCLUDE_UNUSED_CODE_
|
|
Packit |
c22fc9 |
/* Delete all events which has argument value arg. */
|
|
Packit |
c22fc9 |
void
|
|
Packit |
c22fc9 |
thread_cancel_event(thread_master_t *m, void *arg)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_t *thread, *thread_tmp;
|
|
Packit |
c22fc9 |
list_head_t *l = &m->event;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
// Why doesn't this use thread_cancel() above
|
|
Packit |
c22fc9 |
list_for_each_entry_safe(thread, thread_tmp, l, next) {
|
|
Packit |
c22fc9 |
if (thread->arg == arg) {
|
|
Packit |
c22fc9 |
list_head_del(&thread->next);
|
|
Packit |
c22fc9 |
thread_add_unuse(m, thread);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef _WITH_SNMP_
|
|
Packit |
c22fc9 |
static int
|
|
Packit |
c22fc9 |
snmp_read_thread(thread_t *thread)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
fd_set snmp_fdset;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
FD_ZERO(&snmp_fdset);
|
|
Packit |
c22fc9 |
FD_SET(thread->u.fd, &snmp_fdset);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
snmp_read(&snmp_fdset);
|
|
Packit |
c22fc9 |
netsnmp_check_outstanding_agent_requests();
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
thread_add_read(thread->master, snmp_read_thread, thread->arg, thread->u.fd, TIMER_NEVER);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return 0;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
int
|
|
Packit |
c22fc9 |
snmp_timeout_thread(thread_t *thread)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
snmp_timeout();
|
|
Packit |
c22fc9 |
run_alarms();
|
|
Packit |
c22fc9 |
netsnmp_check_outstanding_agent_requests();
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
thread->master->snmp_timer_thread = thread_add_timer(thread->master, snmp_timeout_thread, thread->arg, TIMER_NEVER);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return 0;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
// See https://vincent.bernat.im/en/blog/2012-snmp-event-loop
|
|
Packit |
c22fc9 |
static void
|
|
Packit |
c22fc9 |
snmp_epoll_info(thread_master_t *m)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
fd_set snmp_fdset;
|
|
Packit |
c22fc9 |
int fdsetsize = 0;
|
|
Packit |
c22fc9 |
int max_fdsetsize;
|
|
Packit |
c22fc9 |
struct timeval snmp_timer_wait = { .tv_sec = TIMER_DISABLED };
|
|
Packit |
c22fc9 |
int snmpblock = true;
|
|
Packit |
c22fc9 |
unsigned long *old_set, *new_set; // Must be unsigned for ffsl() to work for us
|
|
Packit |
c22fc9 |
unsigned long diff;
|
|
Packit |
c22fc9 |
int i;
|
|
Packit |
c22fc9 |
int fd;
|
|
Packit |
c22fc9 |
int bit;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#if 0
|
|
Packit |
c22fc9 |
// TODO
|
|
Packit |
c22fc9 |
#if sizeof fd_mask != sizeof diff
|
|
Packit |
c22fc9 |
#error "snmp_epoll_info sizeof(fd_mask) does not match old_set/new_set/diff"
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
FD_ZERO(&snmp_fdset);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* When SNMP is enabled, we may have to select() on additional
|
|
Packit |
c22fc9 |
* FD. snmp_select_info() will add them to `readfd'. The trick
|
|
Packit |
c22fc9 |
* with this function is its last argument. We need to set it
|
|
Packit |
c22fc9 |
* true to set its own timer that we then compare against ours. */
|
|
Packit |
c22fc9 |
snmp_select_info(&fdsetsize, &snmp_fdset, &snmp_timer_wait, &snmpblock);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (snmpblock)
|
|
Packit |
c22fc9 |
snmp_timer_wait.tv_sec = TIMER_DISABLED;
|
|
Packit |
c22fc9 |
timer_thread_update_timeout(m->snmp_timer_thread, timer_long(snmp_timer_wait));
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
max_fdsetsize = m->snmp_fdsetsize > fdsetsize ? m->snmp_fdsetsize : fdsetsize;
|
|
Packit |
c22fc9 |
if (!max_fdsetsize)
|
|
Packit |
c22fc9 |
return;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
for (i = 0, old_set = (unsigned long *)&m->snmp_fdset, new_set = (unsigned long *)&snmp_fdset; i <= max_fdsetsize / (int)sizeof(*new_set); i++, old_set++, new_set++) {
|
|
Packit |
c22fc9 |
if (*old_set == *new_set)
|
|
Packit |
c22fc9 |
continue;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
diff = *old_set ^ *new_set;
|
|
Packit |
c22fc9 |
fd = i * sizeof(*old_set) * CHAR_BIT - 1;
|
|
Packit |
c22fc9 |
do {
|
|
Packit |
c22fc9 |
bit = ffsl(diff);
|
|
Packit |
c22fc9 |
diff >>= bit;
|
|
Packit |
c22fc9 |
fd += bit;
|
|
Packit |
c22fc9 |
if (FD_ISSET(fd, &snmp_fdset)) {
|
|
Packit |
c22fc9 |
/* Add the fd */
|
|
Packit |
c22fc9 |
thread_add_read(m, snmp_read_thread, 0, fd, TIMER_NEVER);
|
|
Packit |
c22fc9 |
FD_SET(fd, &m->snmp_fdset);
|
|
Packit |
c22fc9 |
} else {
|
|
Packit |
c22fc9 |
/* Remove the fd */
|
|
Packit |
c22fc9 |
thread_del_read_fd(m, fd);
|
|
Packit |
c22fc9 |
FD_CLR(fd, &m->snmp_fdset);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
} while (diff);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
m->snmp_fdsetsize = fdsetsize;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Fetch next ready thread. */
|
|
Packit |
c22fc9 |
static list_head_t *
|
|
Packit |
c22fc9 |
thread_fetch_next_queue(thread_master_t *m)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
int sav_errno;
|
|
Packit |
c22fc9 |
int last_epoll_errno = 0;
|
|
Packit |
c22fc9 |
int ret;
|
|
Packit |
c22fc9 |
int i;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
assert(m != NULL);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* If there is event process it first. */
|
|
Packit |
c22fc9 |
if (m->event.next != &m->event)
|
|
Packit |
c22fc9 |
return &m->event;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* If there are ready threads process them */
|
|
Packit |
c22fc9 |
if (m->ready.next != &m->ready)
|
|
Packit |
c22fc9 |
return &m->ready;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
do {
|
|
Packit |
c22fc9 |
#ifdef _WITH_SNMP_
|
|
Packit |
c22fc9 |
if (snmp_running)
|
|
Packit |
c22fc9 |
snmp_epoll_info(m);
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Calculate and set wait timer. Take care of timeouted fd. */
|
|
Packit |
c22fc9 |
thread_set_timer(m);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef _VRRP_FD_DEBUG_
|
|
Packit |
c22fc9 |
if (extra_threads_debug)
|
|
Packit |
c22fc9 |
extra_threads_debug();
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef _EPOLL_THREAD_DUMP_
|
|
Packit |
c22fc9 |
if (do_epoll_thread_dump)
|
|
Packit |
c22fc9 |
dump_thread_data(m, NULL);
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef _EPOLL_DEBUG_
|
|
Packit |
c22fc9 |
if (do_epoll_debug)
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "calling epoll_wait");
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Call epoll function. */
|
|
Packit |
c22fc9 |
ret = epoll_wait(m->epoll_fd, m->epoll_events, m->epoll_count, -1);
|
|
Packit |
c22fc9 |
sav_errno = errno;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef _EPOLL_DEBUG_
|
|
Packit |
c22fc9 |
if (do_epoll_debug) {
|
|
Packit |
c22fc9 |
if (ret == -1)
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "epoll_wait returned %d, errno %d", ret, sav_errno);
|
|
Packit |
c22fc9 |
else
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "epoll_wait returned %d fds", ret);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (ret < 0) {
|
|
Packit |
c22fc9 |
if (sav_errno == EINTR)
|
|
Packit |
c22fc9 |
continue;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Real error. */
|
|
Packit |
c22fc9 |
if (sav_errno != last_epoll_errno) {
|
|
Packit |
c22fc9 |
/* Log the error first time only */
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "scheduler: epoll_wait error: %s", strerror(sav_errno));
|
|
Packit |
c22fc9 |
last_epoll_errno = sav_errno;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Make sure we don't sit it a tight loop */
|
|
Packit |
c22fc9 |
if (sav_errno == EBADF || sav_errno == EFAULT || sav_errno == EINVAL)
|
|
Packit |
c22fc9 |
sleep(1);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
continue;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Handle epoll events */
|
|
Packit |
c22fc9 |
for (i = 0; i < ret; i++) {
|
|
Packit |
c22fc9 |
struct epoll_event *ep_ev;
|
|
Packit |
c22fc9 |
thread_event_t *ev;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
ep_ev = &m->epoll_events[i];
|
|
Packit |
c22fc9 |
ev = ep_ev->data.ptr;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Error */
|
|
Packit |
c22fc9 |
// TODO - no thread processing function handles THREAD_READ_ERROR/THREAD_WRITE_ERROR yet
|
|
Packit |
c22fc9 |
if (ep_ev->events & (EPOLLHUP | EPOLLERR | EPOLLRDHUP)) {
|
|
Packit |
c22fc9 |
if (ev->read) {
|
|
Packit |
c22fc9 |
thread_move_ready(m, &m->read, ev->read, THREAD_READ_ERROR);
|
|
Packit |
c22fc9 |
ev->read = NULL;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (ev->write) {
|
|
Packit |
c22fc9 |
thread_move_ready(m, &m->write, ev->write, THREAD_WRITE_ERROR);
|
|
Packit |
c22fc9 |
ev->write = NULL;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
continue;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* READ */
|
|
Packit |
c22fc9 |
if (ep_ev->events & EPOLLIN) {
|
|
Packit |
c22fc9 |
if (!ev->read) {
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "scheduler: No read thread bound on fd:%d (fl:0x%.4X)"
|
|
Packit |
c22fc9 |
, ev->fd, ep_ev->events);
|
|
Packit |
c22fc9 |
assert(0);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
thread_move_ready(m, &m->read, ev->read, THREAD_READY_FD);
|
|
Packit |
c22fc9 |
ev->read = NULL;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* WRITE */
|
|
Packit |
c22fc9 |
if (ep_ev->events & EPOLLOUT) {
|
|
Packit |
c22fc9 |
if (!ev->write) {
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "scheduler: No write thread bound on fd:%d (fl:0x%.4X)"
|
|
Packit |
c22fc9 |
, ev->fd, ep_ev->events);
|
|
Packit |
c22fc9 |
assert(0);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
thread_move_ready(m, &m->write, ev->write, THREAD_READY_FD);
|
|
Packit |
c22fc9 |
ev->write = NULL;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (ep_ev->events & EPOLLHUP) {
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "Received EPOLLHUP for fd %d", ev->fd);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (ep_ev->events & EPOLLERR) {
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "Received EPOLLERR for fd %d", ev->fd);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Update current time */
|
|
Packit |
c22fc9 |
set_time_now();
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* If there is a ready thread, return it. */
|
|
Packit |
c22fc9 |
if (m->ready.next != &m->ready)
|
|
Packit |
c22fc9 |
return &m->ready;
|
|
Packit |
c22fc9 |
} while (true);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Call thread ! */
|
|
Packit |
c22fc9 |
static inline void
|
|
Packit |
c22fc9 |
thread_call(thread_t * thread)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
#ifdef _EPOLL_DEBUG_
|
|
Packit |
c22fc9 |
if (do_epoll_debug)
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "Calling thread function %s(), type %s, val/fd/pid %d, status %d id %lu", get_function_name(thread->func), get_thread_type_str(thread->type), thread->u.val, thread->u.c.status, thread->id);
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
(*thread->func) (thread);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
void
|
|
Packit |
c22fc9 |
process_threads(thread_master_t *m)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_t* thread;
|
|
Packit |
c22fc9 |
list_head_t *thread_list;
|
|
Packit |
c22fc9 |
int thread_type;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/*
|
|
Packit |
c22fc9 |
* Processing the master thread queues,
|
|
Packit |
c22fc9 |
* return and execute one ready thread.
|
|
Packit |
c22fc9 |
*/
|
|
Packit |
c22fc9 |
while ((thread_list = thread_fetch_next_queue(m))) {
|
|
Packit |
c22fc9 |
/* Run until error, used for debuging only */
|
|
Packit |
c22fc9 |
#if defined _DEBUG_ && defined _MEM_CHECK_
|
|
Packit |
c22fc9 |
if (__test_bit(MEM_ERR_DETECT_BIT, &debug)
|
|
Packit |
c22fc9 |
#ifdef _WITH_VRRP_
|
|
Packit |
c22fc9 |
&& __test_bit(DONT_RELEASE_VRRP_BIT, &debug)
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
) {
|
|
Packit |
c22fc9 |
__clear_bit(MEM_ERR_DETECT_BIT, &debug);
|
|
Packit |
c22fc9 |
#ifdef _WITH_VRRP_
|
|
Packit |
c22fc9 |
__clear_bit(DONT_RELEASE_VRRP_BIT, &debug);
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
thread_add_terminate_event(master);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* If we are shutting down, only process relevant thread types.
|
|
Packit |
c22fc9 |
* We only want timer and signal fd, and don't want inotify, vrrp socket,
|
|
Packit |
c22fc9 |
* snmp_read, bfd_receiver, bfd pipe in vrrp/check, dbus pipe or netlink fds. */
|
|
Packit |
c22fc9 |
thread = thread_trim_head(thread_list);
|
|
Packit |
c22fc9 |
if (!shutting_down ||
|
|
Packit |
c22fc9 |
(thread->type == THREAD_READY_FD &&
|
|
Packit |
c22fc9 |
(thread->u.fd == m->timer_fd || thread->u.fd == m->signal_fd)) ||
|
|
Packit |
c22fc9 |
thread->type == THREAD_CHILD ||
|
|
Packit |
c22fc9 |
thread->type == THREAD_CHILD_TIMEOUT ||
|
|
Packit |
c22fc9 |
thread->type == THREAD_CHILD_TERMINATED ||
|
|
Packit |
c22fc9 |
thread->type == THREAD_TIMER_SHUTDOWN ||
|
|
Packit |
c22fc9 |
thread->type == THREAD_TERMINATE) {
|
|
Packit |
c22fc9 |
if (thread->func)
|
|
Packit |
c22fc9 |
thread_call(thread);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (thread->type == THREAD_TERMINATE_START)
|
|
Packit |
c22fc9 |
shutting_down = true;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
m->current_event = (thread->type == THREAD_READY_FD) ? thread->event : NULL;
|
|
Packit |
c22fc9 |
thread_type = thread->type;
|
|
Packit |
c22fc9 |
thread_add_unuse(master, thread);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* If we are shutting down, and the shutdown timer is not running and
|
|
Packit |
c22fc9 |
* all children have terminated, then we can terminate */
|
|
Packit |
c22fc9 |
if (shutting_down && !m->shutdown_timer_running && !m->child.rb_root.rb_node)
|
|
Packit |
c22fc9 |
break;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* If daemon hanging event is received stop processing */
|
|
Packit |
c22fc9 |
if (thread_type == THREAD_TERMINATE)
|
|
Packit |
c22fc9 |
break;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
static void
|
|
Packit |
c22fc9 |
process_child_termination(pid_t pid, int status)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
thread_master_t * m = master;
|
|
Packit |
c22fc9 |
thread_t th = { .u.c.pid = pid };
|
|
Packit |
c22fc9 |
thread_t *thread;
|
|
Packit |
c22fc9 |
bool permanent_vrrp_checker_error = false;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifndef _DEBUG_
|
|
Packit |
c22fc9 |
if (prog_type == PROG_TYPE_PARENT)
|
|
Packit |
c22fc9 |
permanent_vrrp_checker_error = report_child_status(status, pid, NULL);
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
thread = rb_search(&master->child_pid, &th, rb_data, thread_child_pid_cmp);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef _EPOLL_DEBUG_
|
|
Packit |
c22fc9 |
if (do_epoll_debug)
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "Child %d terminated with status 0x%x, thread_id %lu", pid, status, thread ? thread->id : 0);
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (!thread)
|
|
Packit |
c22fc9 |
return;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
rb_erase(&thread->rb_data, &master->child_pid);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
thread->u.c.status = status;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (permanent_vrrp_checker_error)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
/* The child had a permanant error, so no point in respawning */
|
|
Packit |
c22fc9 |
rb_erase_cached(&thread->n, &m->child);
|
|
Packit |
c22fc9 |
thread_add_unuse(m, thread);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
thread_add_terminate_event(m);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
else
|
|
Packit |
c22fc9 |
thread_move_ready(m, &m->child, thread, THREAD_CHILD_TERMINATED);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Synchronous signal handler to reap child processes */
|
|
Packit |
c22fc9 |
void
|
|
Packit |
c22fc9 |
thread_child_handler(__attribute__((unused)) void *v, __attribute__((unused)) int unused)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
pid_t pid;
|
|
Packit |
c22fc9 |
int status;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
while ((pid = waitpid(-1, &status, WNOHANG))) {
|
|
Packit |
c22fc9 |
if (pid == -1) {
|
|
Packit |
c22fc9 |
if (errno == ECHILD)
|
|
Packit |
c22fc9 |
return;
|
|
Packit |
c22fc9 |
DBG("waitpid error: %s", strerror(errno));
|
|
Packit |
c22fc9 |
assert(0);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
process_child_termination(pid, status);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
void
|
|
Packit |
c22fc9 |
thread_add_base_threads(thread_master_t *m)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
m->timer_thread = thread_add_read(m, thread_timerfd_handler, NULL, m->timer_fd, TIMER_NEVER);
|
|
Packit |
c22fc9 |
add_signal_read_thread(m);
|
|
Packit |
c22fc9 |
#ifdef _WITH_SNMP_
|
|
Packit |
c22fc9 |
m->snmp_timer_thread = thread_add_timer(m, snmp_timeout_thread, 0, TIMER_NEVER);
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Our infinite scheduling loop */
|
|
Packit |
c22fc9 |
void
|
|
Packit |
c22fc9 |
launch_thread_scheduler(thread_master_t *m)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
// TODO - do this somewhere better
|
|
Packit |
c22fc9 |
signal_set(SIGCHLD, thread_child_handler, m);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
process_threads(m);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#ifdef THREAD_DUMP
|
|
Packit |
c22fc9 |
void
|
|
Packit |
c22fc9 |
register_scheduler_addresses(void)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
#ifdef _WITH_SNMP_
|
|
Packit |
c22fc9 |
register_thread_address("snmp_timeout_thread", snmp_timeout_thread);
|
|
Packit |
c22fc9 |
register_thread_address("snmp_read_thread", snmp_read_thread);
|
|
Packit |
c22fc9 |
#endif
|
|
Packit |
c22fc9 |
register_thread_address("thread_timerfd_handler", thread_timerfd_handler);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
register_signal_handler_address("thread_child_handler", thread_child_handler);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
#endif
|