diff --git a/keepalived/check/check_pop3.c b/keepalived/check/check_pop3.c
new file mode 100644
index 0000000..1f8c56e
--- /dev/null
+++ b/keepalived/check/check_pop3.c
@@ -0,0 +1,829 @@
+/*
+ * Soft: Keepalived is a failover program for the LVS project
+ * <www.linuxvirtualserver.org>. It monitor & manipulate
+ * a loadbalanced server pool using multi-layer checks.
+ *
+ * Part: POP3 CHECK. Check an POP3-server.
+ *
+ * Authors: Jeremy Rumpf, <jrumpf <at> heavyload.net>
+ * Alexandre Cassen, <acassen <at> linux-vs.org>
+ * Jakub Jankowski, <jakub.jankowski <at> superhost.pl>
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Copyright (C) 2001-2011 Alexandre Cassen, <acassen <at> linux-vs.org>
+ */
+
+#include "check_pop3.h"
+#include "check_api.h"
+#include "logger.h"
+#include "memory.h"
+#include "ipwrapper.h"
+#include "utils.h"
+#include "parser.h"
+#include "daemon.h"
+
+int pop3_connect_thread(thread_t *);
+
+/* module variables */
+static pop3_host_t *default_host = NULL;
+
+/*
+ * Used as a callback from free_list() to free all
+ * the list elements in pop3_checker->host before we
+ * free pop3_checker itself.
+ */
+void
+pop3_free_host(void *data)
+{
+ FREE(data);
+}
+
+/* Used as a callback from the checker api, queue_checker(),
+ * to free up a checker entry and all its associated data.
+ */
+void
+free_pop3_check(void *data)
+{
+ pop3_checker_t *pop3_checker = CHECKER_DATA(data);
+ free_list(pop3_checker->host);
+ FREE(pop3_checker->command_name);
+ FREE(pop3_checker);
+ FREE(data);
+}
+
+/*
+ * Used as a callback from dump_list() to print out all
+ * the list elements in pop3_checker->host.
+ */
+void
+pop3_dump_host(void *data)
+{
+ pop3_host_t *pop3_host = data;
+ log_message(LOG_INFO, " Checked ip = %s", inet_sockaddrtos(&pop3_host->dst));
+ log_message(LOG_INFO, " port = %d", ntohs(inet_sockaddrport(&pop3_host->dst)));
+ if (pop3_host->bindto.ss_family)
+ log_message(LOG_INFO, " bindto = %s", inet_sockaddrtos(&pop3_host->bindto));
+}
+
+/*
+ * Callback for whenever we've been requested to dump our
+ * configuration.
+ */
+void
+dump_pop3_check(void *data)
+{
+ pop3_checker_t *pop3_checker = CHECKER_DATA(data);
+ log_message(LOG_INFO, " Keepalive method = POP3_CHECK");
+ log_message(LOG_INFO, " command = %s", pop3_checker->command_name);
+ log_message(LOG_INFO, " timeout = %ld", pop3_checker->timeout/TIMER_HZ);
+ log_message(LOG_INFO, " retry = %d", pop3_checker->retry);
+ log_message(LOG_INFO, " delay before retry = %ld", pop3_checker->db_retry/TIMER_HZ);
+ dump_list(pop3_checker->host);
+}
+
+/* Allocates a default host structure */
+pop3_host_t *
+pop3_alloc_host(void)
+{
+ pop3_host_t *new;
+
+ /* Allocate the new host data structure */
+ new = (pop3_host_t *)MALLOC(sizeof(pop3_host_t));
+
+ /*
+ * By default we set the ip to connect to as the same ip as the current real server
+ * in the rs config. This might be overridden later on by a "connect_ip" keyword.
+ */
+ checker_set_dst(&new->dst);
+ checker_set_dst_port(&new->dst, htons(POP3_DEFAULT_PORT));
+ return new;
+}
+
+/*
+ * Callback for whenever an POP3_CHECK keyword is encountered
+ * in the config file.
+ */
+void
+pop3_check_handler(vector strvec)
+{
+ pop3_checker_t *pop3_checker = (pop3_checker_t *)MALLOC(sizeof(pop3_checker_t));
+
+ /*
+ * Set something sane for the default command sent to the server
+ * May be overridden by a "command_name" keyword later.
+ */
+ pop3_checker->command_name = (char *)MALLOC(strlen(POP3_DEFAULT_COMMAND) + 1);
+ memcpy(pop3_checker->command_name, POP3_DEFAULT_COMMAND, strlen(POP3_DEFAULT_COMMAND) + 1);
+
+ /* some other sane values */
+ pop3_checker->timeout = 5 * TIMER_HZ;
+ pop3_checker->db_retry = 1 * TIMER_HZ;
+ pop3_checker->retry = 1;
+
+ /*
+ * Have the checker queue code put our checker into the checkers_queue
+ * list.
+ *
+ * queue_checker(void (*free) (void *), void (*dump) (void *),
+ * int (*launch) (thread_t *),
+ * void *data)
+ */
+ queue_checker(free_pop3_check, dump_pop3_check, pop3_connect_thread,
+ pop3_checker);
+
+ /*
+ * Last, allocate/setup the list that will hold all the per host
+ * configuration structures. We'll set a "default host", which
+ * is the same ip as the real server. If there are additional "host"
+ * sections in the config, the default will be deleted and overridden.
+ * If the default is still set by a previous "POP3_CHECK" section,
+ * we must simply overwrite the old value:
+ * - it must not be reused, because it was probably located in a
+ * different "real_server" section and
+ * - it must not be freed, because it is still referenced
+ * by some other pop3_checker->host.
+ * This must come after queue_checker()!
+ */
+ pop3_checker->host = alloc_list(pop3_free_host, pop3_dump_host);
+ default_host = pop3_alloc_host();
+ list_add(pop3_checker->host, default_host);
+}
+
+/*
+ * Callback for whenever the "host" keyword is encountered
+ * in the config file.
+ */
+void
+pop3_host_handler(vector strvec)
+{
+ pop3_checker_t *pop3_checker = CHECKER_GET();
+
+ /*
+ * If the default host is still allocated, delete it
+ * before we stick user defined hosts in the list.
+ */
+ if (default_host) {
+ list_del(pop3_checker->host, default_host);
+ FREE(default_host);
+ default_host = NULL;
+ }
+
+ /* add an empty host to the list, pop3_checker->host */
+ list_add(pop3_checker->host, pop3_alloc_host());
+}
+
+/* "connect_ip" keyword */
+void
+pop3_ip_handler(vector strvec)
+{
+ pop3_checker_t *pop3_checker = CHECKER_GET();
+ pop3_host_t *pop3_host = LIST_TAIL_DATA(pop3_checker->host);
+ inet_stosockaddr(VECTOR_SLOT(strvec, 1), NULL, &pop3_host->dst);
+}
+
+/* "connect_port" keyword */
+void
+pop3_port_handler(vector strvec)
+{
+ pop3_checker_t *pop3_checker = CHECKER_GET();
+ pop3_host_t *pop3_host = LIST_TAIL_DATA(pop3_checker->host);
+ checker_set_dst_port(&pop3_host->dst, htons(CHECKER_VALUE_INT(strvec)));
+}
+
+/* "command_name" keyword */
+void
+pop3_command_name_handler(vector strvec)
+{
+ pop3_checker_t *pop3_checker = CHECKER_GET();
+ pop3_checker->command_name = CHECKER_VALUE_STRING(strvec);
+}
+
+/* "connect_timeout" keyword */
+void
+pop3_timeout_handler(vector strvec)
+{
+ pop3_checker_t *pop3_checker = CHECKER_GET();
+ pop3_checker->timeout = CHECKER_VALUE_INT(strvec) * TIMER_HZ;
+}
+
+/* "retry" keyword */
+void
+pop3_retry_handler(vector strvec)
+{
+ pop3_checker_t *pop3_checker = CHECKER_GET();
+ pop3_checker->retry = CHECKER_VALUE_INT(strvec);
+}
+
+/* "delay_before_retry" keyword */
+void
+pop3_db_retry_handler(vector strvec)
+{
+ pop3_checker_t *pop3_checker = CHECKER_GET();
+ pop3_checker->db_retry = CHECKER_VALUE_INT(strvec) * TIMER_HZ;
+}
+
+/* "bindto" keyword */
+void
+pop3_bindto_handler(vector strvec)
+{
+ pop3_checker_t *pop3_checker = CHECKER_GET();
+ pop3_host_t *pop3_host = LIST_TAIL_DATA(pop3_checker->host);
+ inet_stosockaddr(VECTOR_SLOT(strvec, 1), 0, &pop3_host->bindto);
+}
+
+/* Config callback installer */
+void
+install_pop3_check_keyword(void)
+{
+ /*
+ * Notify the config log parser that we need to be notified via
+ * callbacks when the following keywords are encountered in the
+ * keepalive.conf file.
+ */
+ install_keyword("POP3_CHECK", &pop3_check_handler);
+ install_sublevel();
+ install_keyword("command_name", &pop3_command_name_handler);
+ install_keyword("connect_timeout", &pop3_timeout_handler);
+ install_keyword("delay_before_retry", &pop3_db_retry_handler);
+ install_keyword("retry", &pop3_retry_handler);
+ install_keyword("host", &pop3_host_handler);
+ install_sublevel();
+ install_keyword("connect_ip", &pop3_ip_handler);
+ install_keyword("connect_port", &pop3_port_handler);
+ install_keyword("bindto", &pop3_bindto_handler);
+ install_sublevel_end();
+ install_sublevel_end();
+}
+
+/*
+ * Final handler. Determines if we need a retry or not.
+ * Also has to make a decision if we need to bring the resulting
+ * service down in case of error.
+ */
+int
+pop3_final(thread_t *thread, int error, const char *format, ...)
+{
+ checker_t *checker = THREAD_ARG(thread);
+ pop3_checker_t *pop3_checker = CHECKER_ARG(checker);
+ char error_buff[512];
+ char smtp_buff[542];
+ va_list varg_list;
+
+ /* Error or no error we should always have to close the socket */
+ close(thread->u.fd);
+
+ /* If we're here, an attempt HAS been made already for the current host */
+ pop3_checker->attempts++;
+
+ if (error) {
+ /* Always syslog the error when the real server is up */
+ if (svr_checker_up(checker->id, checker->rs)) {
+ if (format != NULL) {
+ memcpy(error_buff, "POP3_CHECK ", 11);
+ va_start(varg_list, format);
+ vsnprintf(error_buff + 11, 512 - 11, format, varg_list);
+ va_end(varg_list);
+ error_buff[512 - 1] = '\0';
+
+ log_message(LOG_INFO, error_buff);
+ } else {
+ log_message(LOG_INFO, "POP3_CHECK Unknown error");
+ }
+ }
+
+ /*
+ * If we still have retries left, try this host again by
+ * scheduling the main thread to check it again after the
+ * configured backoff delay. Otherwise down the RS.
+ */
+ if (pop3_checker->attempts < pop3_checker->retry) {
+ thread_add_timer(thread->master, pop3_connect_thread, checker,
+ pop3_checker->db_retry);
+ return 0;
+ }
+
+ /*
+ * No more retries, pull the real server from the virtual server.
+ * Only smtp_alert if it wasn't previously down. It should
+ * be noted that smtp_alert makes a copy of the string arguments, so
+ * we don't have to keep them statically allocated.
+ */
+ if (svr_checker_up(checker->id, checker->rs)) {
+ if (format != NULL) {
+ snprintf(smtp_buff, 542, "=> CHECK failed on service : %s <=",
+ error_buff + 11);
+ } else {
+ snprintf(smtp_buff, 542, "=> CHECK failed on service <=");
+ }
+
+ smtp_buff[542 - 1] = '\0';
+ smtp_alert(checker->rs, NULL, NULL, "DOWN", smtp_buff);
+ update_svr_checker_state(DOWN, checker->id, checker->vs, checker->rs);
+ }
+
+ /* Reset everything back to the first host in the list */
+ pop3_checker->attempts = 0;
+ pop3_checker->host_ctr = 0;
+
+ /* Reschedule the main thread using the configured delay loop */;
+ thread_add_timer(thread->master, pop3_connect_thread, checker, checker->vs->delay_loop);
+
+ return 0;
+ }
+
+ /*
+ * Ok this host was successful, increment to the next host in the list
+ * and reset the attempts counter. We'll then reschedule the main thread again.
+ * If host_ctr exceeds the number of hosts in the list, http_main_thread will
+ * take note and bring up the real server as well as inject the delay_loop.
+ */
+ pop3_checker->attempts = 0;
+ pop3_checker->host_ctr++;
+
+ thread_add_timer(thread->master, pop3_connect_thread, checker, 1);
+ return 0;
+}
+
+/*
+ * Zeros out the rx/tx buffer
+ */
+void
+pop3_clear_buff(thread_t *thread)
+{
+ checker_t *checker = THREAD_ARG(thread);
+ pop3_checker_t *pop3_checker = CHECKER_ARG(checker);
+ memset(pop3_checker->buff, 0, POP3_BUFF_MAX);
+ pop3_checker->buff_ctr = 0;
+}
+
+/*
+ * One thing to note here is we do a very cheap check for a newline.
+ * We could receive two lines (with two newline characters) in a
+ * single packet, but we don't care. We are only looking at the
+ * POP3 response codes at the beginning anyway.
+ */
+int
+pop3_get_line_cb(thread_t *thread)
+{
+ checker_t *checker = THREAD_ARG(thread);
+ pop3_checker_t *pop3_checker = CHECKER_ARG(checker);
+ pop3_host_t *pop3_host = pop3_checker->host_ptr;
+ int f, r, x;
+
+ /* Handle read timeout */
+ if (thread->type == THREAD_READ_TIMEOUT) {
+ pop3_final(thread, 1, "Read timeout from server [%s]:%d"
+ , inet_sockaddrtos(&pop3_host->dst)
+ , ntohs(inet_sockaddrport(&pop3_host->dst)));
+ return 0;
+ }
+
+ /* wrap the buffer, if full, by clearing it */
+ if (POP3_BUFF_MAX - pop3_checker->buff_ctr <= 0) {
+ log_message(LOG_INFO, "POP3_CHECK Buffer overflow reading from server [%s]:%d. "
+ "Increase POP3_BUFF_MAX in check_pop3.h"
+ , inet_sockaddrtos(&pop3_host->dst)
+ , ntohs(inet_sockaddrport(&pop3_host->dst)));
+ pop3_clear_buff(thread);
+ }
+
+ /* Set descriptor non blocking */
+ f = fcntl(thread->u.fd, F_GETFL, 0);
+ fcntl(thread->u.fd, F_SETFL, f | O_NONBLOCK);
+
+ /* read the data */
+ r = read(thread->u.fd, pop3_checker->buff + pop3_checker->buff_ctr,
+ POP3_BUFF_MAX - pop3_checker->buff_ctr);
+
+ if (r == -1 && (errno == EAGAIN || errno == EINTR)) {
+ thread_add_read(thread->master, pop3_get_line_cb, checker,
+ thread->u.fd, pop3_checker->timeout);
+ fcntl(thread->u.fd, F_SETFL, f);
+ return 0;
+ } else if (r > 0)
+ pop3_checker->buff_ctr += r;
+
+ /* restore descriptor flags */
+ fcntl(thread->u.fd, F_SETFL, f);
+
+ /* check if we have a newline, if so, callback */
+ for (x = 0; x < POP3_BUFF_MAX; x++) {
+ if (pop3_checker->buff[x] == '\n') {
+ pop3_checker->buff[POP3_BUFF_MAX - 1] = '\0';
+
+ DBG("POP3_CHECK [%s]:%d < %s"
+ , inet_sockaddrtos(&pop3_host->dst)
+ , ntohs(inet_sockaddrport(&pop3_host->dst))
+ , pop3_checker->buff);
+
+ (pop3_checker->buff_cb)(thread);
+
+ return 0;
+ }
+ }
+
+ /*
+ * If the connection was closed or there was
+ * some sort of error, notify pop3_final()
+ */
+ if (r <= 0) {
+ pop3_final(thread, 1, "Read failure from server [%s]:%d"
+ , inet_sockaddrtos(&pop3_host->dst)
+ , ntohs(inet_sockaddrport(&pop3_host->dst)));
+ return 0;
+ }
+
+ /*
+ * Last case, we haven't read enough data yet
+ * to pull a newline. Schedule ourselves for
+ * another round.
+ */
+ thread_add_read(thread->master, pop3_get_line_cb, checker,
+ thread->u.fd, pop3_checker->timeout);
+ return 0;
+}
+
+/*
+ * Ok a caller has asked us to asyncronously schedule a single line
+ * to be received from the server. They have also passed us a call back
+ * function that we'll call once we have the newline. If something bad
+ * happens, the caller assumes we'll pass the error off to pop3_final(),
+ * which will either down the real server or schedule a retry. The
+ * function pop3_get_line_cb is what does the dirty work since the
+ * sceduler can only accept a single *thread argument.
+ */
+void
+pop3_get_line(thread_t *thread, int (*callback) (thread_t *))
+{
+ checker_t *checker = THREAD_ARG(thread);
+ pop3_checker_t *pop3_checker = CHECKER_ARG(checker);
+
+ /* clear the buffer */
+ pop3_clear_buff(thread);
+
+ /* set the callback */
+ pop3_checker->buff_cb = callback;
+
+ /* schedule the I/O with our helper function */
+ thread_add_read(thread->master, pop3_get_line_cb, checker,
+ thread->u.fd, pop3_checker->timeout);
+ return;
+}
+
+/*
+ * The scheduler function that puts the data out on the wire.
+ * All our data will fit into one packet, so we only check if
+ * the current write would block or not. If it wants to block,
+ * we'll return to the scheduler and try again later.
+ */
+int
+pop3_put_line_cb(thread_t *thread)
+{
+ checker_t *checker = THREAD_ARG(thread);
+ pop3_checker_t *pop3_checker = CHECKER_ARG(checker);
+ pop3_host_t *pop3_host = pop3_checker->host_ptr;
+ int f, w;
+
+
+ /* Handle read timeout */
+ if (thread->type == THREAD_WRITE_TIMEOUT) {
+ pop3_final(thread, 1, "Write timeout to server [%s]:%d"
+ , inet_sockaddrtos(&pop3_host->dst)
+ , ntohs(inet_sockaddrport(&pop3_host->dst)));
+ return 0;
+ }
+
+ /* Set descriptor non blocking */
+ f = fcntl(thread->u.fd, F_GETFL, 0);
+ fcntl(thread->u.fd, F_SETFL, f | O_NONBLOCK);
+
+ /* write the data */
+ w = write(thread->u.fd, pop3_checker->buff, pop3_checker->buff_ctr);
+
+ if (w == -1 && (errno == EAGAIN || errno == EINTR)) {
+ thread_add_write(thread->master, pop3_put_line_cb, checker,
+ thread->u.fd, pop3_checker->timeout);
+ fcntl(thread->u.fd, F_SETFL, f);
+ return 0;
+ }
+
+ /* restore descriptor flags */
+ fcntl(thread->u.fd, F_SETFL, f);
+
+ DBG("POP3_CHECK [%s]:%d > %s"
+ , inet_sockaddrtos(&pop3_host->dst)
+ , ntohs(inet_sockaddrport(&pop3_host->dst))
+ , pop3_checker->buff);
+
+ /*
+ * If the connection was closed or there was
+ * some sort of error, notify pop3_final()
+ */
+ if (w <= 0) {
+ pop3_final(thread, 1, "Write failure to server [%s]:%d"
+ , inet_sockaddrtos(&pop3_host->dst)
+ , ntohs(inet_sockaddrport(&pop3_host->dst)));
+ return 0;
+ }
+
+ /* Execute the callback */
+ (pop3_checker->buff_cb)(thread);
+ return 0;
+}
+
+/*
+ * This is the same as pop3_get_line() except that we're sending a
+ * line of data instead of receiving one.
+ */
+void
+pop3_put_line(thread_t *thread, int (*callback) (thread_t *))
+{
+ checker_t *checker = THREAD_ARG(thread);
+ pop3_checker_t *pop3_checker = CHECKER_ARG(checker);
+
+ pop3_checker->buff[POP3_BUFF_MAX - 1] = '\0';
+ pop3_checker->buff_ctr = strlen(pop3_checker->buff);
+
+ /* set the callback */
+ pop3_checker->buff_cb = callback;
+
+ /* schedule the I/O with our helper function */
+ thread_add_write(thread->master, pop3_put_line_cb, checker,
+ thread->u.fd, pop3_checker->timeout);
+ return;
+}
+
+/*
+ * Ok, our goal here is to snag the status code out of the
+ * buffer and return POP3_RESPONSE_OK if it is okay. If it's not legible,
+ * return -1.
+ */
+int
+pop3_get_status(thread_t *thread)
+{
+ checker_t *checker = THREAD_ARG(thread);
+ pop3_checker_t *pop3_checker = CHECKER_ARG(checker);
+ char *buff = pop3_checker->buff;
+
+ /* Check for +OK */
+ if (buff[0] == '+' &&
+ buff[1] == 'O' && buff[2] == 'K') {
+ /* We like +OK */
+ return POP3_RESPONSE_OK;
+ }
+
+ return -1;
+}
+
+/*
+ * We have a connected socket and are ready to begin
+ * the conversation. This function schedules itself to
+ * be called via callbacks and tracking state in
+ * pop3_checker->state. Upon first calling, pop3_checker->state
+ * should be set to POP3_START.
+ */
+int
+pop3_engine_thread(thread_t *thread)
+{
+ checker_t *checker = THREAD_ARG(thread);
+ pop3_checker_t *pop3_checker = CHECKER_ARG(checker);
+ pop3_host_t *pop3_host = pop3_checker->host_ptr;
+
+ switch (pop3_checker->state) {
+
+ /* First step, schedule to receive the greeting banner */
+ case POP3_START:
+ /*
+ * Ok, if pop3_get_line schedules us back, we will
+ * have data to analyze. Otherwise, pop3_get_line
+ * will defer directly to pop3_final.
+ */
+ pop3_checker->state = POP3_HAVE_BANNER;
+ pop3_get_line(thread, pop3_engine_thread);
+ return 0;
+ break;
+
+ /* Second step, analyze banner, send our command */
+ case POP3_HAVE_BANNER:
+ /* Check for "+OK" in the greeting */
+ if (pop3_get_status(thread) != POP3_RESPONSE_OK) {
+ pop3_final(thread, 1, "Bad greeting banner from server [%s]:%d"
+ , inet_sockaddrtos(&pop3_host->dst)
+ , ntohs(inet_sockaddrport(&pop3_host->dst)));
+
+ return 0;
+ }
+
+ /*
+ * Schedule to send command, pop3_put_line will
+ * defer directly to pop3_final on error.
+ */
+ pop3_checker->state = POP3_SENT_COMMAND;
+ snprintf(pop3_checker->buff, POP3_BUFF_MAX, "%s\r\n",
+ pop3_checker->command_name);
+ pop3_put_line(thread, pop3_engine_thread);
+ return 0;
+ break;
+
+ /* Third step, schedule to read the command response */
+ case POP3_SENT_COMMAND:
+ pop3_checker->state = POP3_RECV_COMMAND;
+ pop3_get_line(thread, pop3_engine_thread);
+ return 0;
+ break;
+
+ /* Fourth step, analyze command return, send QUIT */
+ case POP3_RECV_COMMAND:
+ /* Check for "+OK" */
+ if (pop3_get_status(thread) != POP3_RESPONSE_OK) {
+ pop3_final(thread, 1, "Bad command response from server [%s]:%d"
+ , inet_sockaddrtos(&pop3_host->dst)
+ , ntohs(inet_sockaddrport(&pop3_host->dst)));
+
+ return 0;
+ }
+
+ pop3_checker->state = POP3_SENT_QUIT;
+ snprintf(pop3_checker->buff, POP3_BUFF_MAX, "QUIT\r\n");
+ pop3_put_line(thread, pop3_engine_thread);
+ return 0;
+ break;
+
+ /* Fifth step, schedule to receive QUIT confirmation */
+ case POP3_SENT_QUIT:
+ pop3_checker->state = POP3_RECV_QUIT;
+ pop3_get_line(thread, pop3_engine_thread);
+ return 0;
+ break;
+
+ /* Sixth step, wrap up success to pop3_final */
+ case POP3_RECV_QUIT:
+ pop3_final(thread, 0, NULL);
+ return 0;
+ break;
+ }
+
+ /* We shouldn't be here */
+ pop3_final(thread, 1, "Unknown pop3 engine state encountered");
+ return 0;
+}
+
+/*
+ * Second step in the process. Here we'll see if the connection
+ * to the host we're checking was successful or not.
+ */
+int
+pop3_check_thread(thread_t *thread)
+{
+ checker_t *checker = THREAD_ARG(thread);
+ pop3_checker_t *pop3_checker = CHECKER_ARG(checker);
+ pop3_host_t *pop3_host = pop3_checker->host_ptr;
+ int status;
+
+ status = tcp_socket_state(thread->u.fd, thread, pop3_check_thread);
+ switch (status) {
+ case connect_error:
+ pop3_final(thread, 1, "Error connecting to server [%s]:%d"
+ , inet_sockaddrtos(&pop3_host->dst)
+ , ntohs(inet_sockaddrport(&pop3_host->dst)));
+ return 0;
+ break;
+
+ case connect_timeout:
+ pop3_final(thread, 1, "Connection timeout to server [%s]:%d"
+ , inet_sockaddrtos(&pop3_host->dst)
+ , ntohs(inet_sockaddrport(&pop3_host->dst)));
+ return 0;
+ break;
+
+ case connect_success:
+ DBG("POP3_CHECK Remote POP3 server [%s]:%d connected"
+ , inet_sockaddrtos(&pop3_host->dst)
+ , ntohs(inet_sockaddrport(&pop3_host->dst)));
+
+ /* Enter the engine at POP3_START */
+ pop3_checker->state = POP3_START;
+ pop3_engine_thread(thread);
+ return 0;
+ break;
+ }
+
+ /* we shouldn't be here */
+ pop3_final(thread, 1, "Unknown connection error to server [%s]:%d"
+ , inet_sockaddrtos(&pop3_host->dst)
+ , ntohs(inet_sockaddrport(&pop3_host->dst)));
+ return 0;
+}
+
+/*
+ * This is the main thread, where all the action starts.
+ * When the check daemon comes up, it goes down the checkers_queue
+ * and launches a thread for each checker that got registered.
+ * This is the callback/event function for that initial thread.
+ *
+ * It should be noted that we ARE responsible for sceduling
+ * ourselves to run again. It doesn't have to be right here,
+ * but eventually has to happen.
+ */
+int
+pop3_connect_thread(thread_t *thread)
+{
+ checker_t *checker = THREAD_ARG(thread);
+ pop3_checker_t *pop3_checker = CHECKER_ARG(checker);
+ pop3_host_t *pop3_host;
+ enum connect_result status;
+ int sd;
+
+ /* Let's review our data structures.
+ *
+ * Thread is the structure used by the sceduler
+ * for sceduling many types of events. thread->arg in this
+ * case points to a checker structure. The checker
+ * structure holds data about the vs and rs configurations
+ * as well as the delay loop, etc. Each real server
+ * defined in the keepalived.conf will more than likely have
+ * a checker structure assigned to it. Each checker structure
+ * has a data element that is meant to hold per checker
+ * configurations. So thread->arg(checker)->data points to
+ * a pop3_checker structure. In the pop3_checker structure
+ * we hold global configuration data for the pop3 check.
+ * pop3_checker has a list of per host (pop3_host) configuration
+ * data in pop3_checker->host.
+ *
+ * So this whole thing looks like this:
+ * thread->arg(checker)->data(pop3_checker)->host(pop3_host)
+ *
+ * To make life simple, we'll break the structures out so
+ * that "checker" always points to the current checker structure,
+ * "pop3_checker" points to the current pop3_checker structure,
+ * and "pop3_host" points to the current pop3_host structure.
+ */
+
+ /*
+ * If we're disabled, we'll do nothing at all.
+ * But we still have to register ourselves again so
+ * we don't fall of the face of the earth.
+ */
+ if (!CHECKER_ENABLED(checker)) {
+ thread_add_timer(thread->master, pop3_connect_thread, checker,
+ checker->vs->delay_loop);
+ return 0;
+ }
+
+ /*
+ * Set the internal host pointer to the host that well be
+ * working on. If it's NULL, we've successfully tested all hosts.
+ * We'll bring the service up (if it's not already), reset the host list,
+ * and insert the delay loop. When we get scheduled again the host list
+ * will be reset and we will continue on checking them one by one.
+ */
+ if ((pop3_checker->host_ptr = list_element(pop3_checker->host, pop3_checker->host_ctr)) ==
NULL) {
+ if (!svr_checker_up(checker->id, checker->rs)) {
+ log_message(LOG_INFO, "Remote POP3 server [%s]:%d succeed on service."
+ , inet_sockaddrtos(&checker->rs->addr)
+ , ntohs(inet_sockaddrport(&checker->rs->addr)));
+
+ smtp_alert(checker->rs, NULL, NULL, "UP",
+ "=> CHECK succeed on service <=");
+ update_svr_checker_state(UP, checker->id, checker->vs, checker->rs);
+ }
+
+ pop3_checker->attempts = 0;
+ pop3_checker->host_ctr = 0;
+ pop3_checker->host_ptr = list_element(pop3_checker->host, 0);
+
+ thread_add_timer(thread->master, pop3_connect_thread, checker, checker->vs->delay_loop);
+ return 0;
+ }
+
+ pop3_host = pop3_checker->host_ptr;
+
+ /* Create the socket, failling here should be an oddity */
+ if ((sd = socket(pop3_host->dst.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) {
+ log_message(LOG_INFO, "POP3_CHECK connection failed to create socket.");
+ thread_add_timer(thread->master, pop3_connect_thread, checker,
+ checker->vs->delay_loop);
+ return 0;
+ }
+
+ status = tcp_bind_connect(sd, &pop3_host->dst, &pop3_host->bindto);
+
+ /* handle tcp connection status & register callback the next step in the process */
+ if (tcp_connection_state(sd, status, thread, pop3_check_thread, pop3_checker->timeout)) {
+ close(sd);
+ log_message(LOG_INFO, "POP3_CHECK socket bind failed. Rescheduling.");
+ thread_add_timer(thread->master, pop3_connect_thread, checker,
+ checker->vs->delay_loop);
+ }
+
+ return 0;
+}
diff --git a/keepalived/include/check_pop3.h b/keepalived/include/check_pop3.h
new file mode 100644
index 0000000..bed3f33
--- /dev/null
+++ b/keepalived/include/check_pop3.h
@@ -0,0 +1,81 @@
+/*
+ * Soft: Keepalived is a failover program for the LVS project
+ * <www.linuxvirtualserver.org>. It monitor & manipulate
+ * a loadbalanced server pool using multi-layer checks.
+ *
+ * Part: check_pop3.c include file.
+ *
+ * Author: Alexandre Cassen, <acassen <at> linux-vs.org>
+ * Jeremy Rumpf, <jrumpf <at> heavyload.net>
+ * Jakub Jankowski, <jakub.jankowski <at> superhost.pl>
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Copyright (C) 2001-2011 Alexandre Cassen, <acassen <at> linux-vs.org>
+ */
+
+#ifndef _CHECK_POP3_H
+#define _CHECK_POP3_H
+
+/* system includes */
+#include <stdlib.h>
+
+/* local includes */
+#include "check_data.h"
+#include "scheduler.h"
+#include "list.h"
+
+#define POP3_BUFF_MAX 512
+
+#define POP3_START 1
+#define POP3_HAVE_BANNER 2
+#define POP3_SENT_COMMAND 3
+#define POP3_RECV_COMMAND 4
+#define POP3_SENT_QUIT 5
+#define POP3_RECV_QUIT 6
+
+#define POP3_DEFAULT_COMMAND "CAPA"
+#define POP3_DEFAULT_PORT 110
+
+#define POP3_RESPONSE_OK 0
+
+/* Per host configuration structure */
+typedef struct _pop3_host {
+ struct sockaddr_storage dst;
+ struct sockaddr_storage bindto;
+} pop3_host_t;
+
+/* Checker argument structure */
+typedef struct _pop3_checker {
+ /* non per host config data goes here */
+ char *command_name;
+ long timeout;
+ long db_retry;
+ int retry;
+ int attempts;
+ int host_ctr;
+ pop3_host_t *host_ptr;
+
+ /* data buffer */
+ char buff[POP3_BUFF_MAX];
+ int buff_ctr;
+ int (*buff_cb) (thread_t *);
+
+ int state;
+
+ /* list holding the host config data */
+ list host;
+} pop3_checker_t;
+
+/* Prototypes defs */
+extern void install_pop3_check_keyword(void);
+
+#endif
转自: http://hi.baidu.com/lijunyi0198/item/8647be1645dbe0cd38cb300f