当前位置: 首页 > 工具软件 > GNU pop3d > 使用案例 >

keepalived 添加pop3_check模块(二)

缪远
2023-12-01
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
 类似资料: