From 9dc6d5dd46302302b8683ed88908d7e6a86a3acf Mon Sep 17 00:00:00 2001 From: Brian Aker Date: Thu, 1 Mar 2012 23:05:55 -0800 Subject: [PATCH 1/1] Update for testig memcached_light --- configure.ac | 3 + example/include.am | 4 +- example/memcached_light.cc | 189 +++++++++++++++++++++------------ example/t/include.am | 4 +- example/t/memcached_light.cc | 122 ++++++++++++++------- libtest/memcached.cc | 120 ++++++++++++++++++++- libtest/memcached.h | 2 + libtest/server.cc | 12 ++- libtest/server_container.cc | 13 ++- libtest/unittest.cc | 13 +++ util/include.am | 1 + util/log.hpp | 198 +++++++++++++++++++++++++++++++++++ 12 files changed, 565 insertions(+), 116 deletions(-) create mode 100644 util/log.hpp diff --git a/configure.ac b/configure.ac index 8bb9a60a..caec223b 100644 --- a/configure.ac +++ b/configure.ac @@ -209,6 +209,9 @@ AC_DEFINE([GEARMAND_BLOBSLAP_WORKER], [0], [Support for Gearman Blobslap worker] AC_DEFINE([HAVE_LIBPQ], [0], [Support for Postgres]) AC_DEFINE([HAVE_LIBCURL], [0], [Support for libcurl]) +AC_DEFINE([HAVE_MEMCACHED_LIGHT_BINARY], [1], [Support for memcached_light]) +AC_DEFINE([MEMCACHED_LIGHT_BINARY], ["example/memcached_light"], [Support for memcached_light]) + AC_CHECK_HEADERS_ONCE(winsock2.h poll.h sys/wait.h fnmatch.h) AM_CONDITIONAL(BUILD_POLL, test "x$ac_cv_header_poll_h" = "xno") AM_CONDITIONAL(BUILD_WIN32_WRAPPERS, test "x$ac_cv_header_winsock2_h" = "xyes") diff --git a/example/include.am b/example/include.am index 0abd2c3b..bf620d0c 100644 --- a/example/include.am +++ b/example/include.am @@ -15,7 +15,9 @@ example_memcached_light_SOURCES= \ example/byteorder.cc \ example/interface_v0.cc \ example/interface_v1.cc \ - example/memcached_light.cc + example/memcached_light.cc \ + util/daemon.cc \ + util/pidfile.cc example_memcached_light_LDADD= libmemcached/libmemcachedprotocol.la \ $(LIBEVENT_LDFLAGS) diff --git a/example/memcached_light.cc b/example/memcached_light.cc index 791d259b..4bf82825 100644 --- a/example/memcached_light.cc +++ b/example/memcached_light.cc @@ -31,6 +31,12 @@ #include "example/storage.h" #include "example/memcached_light.h" +#include "util/daemon.hpp" +#include "util/log.hpp" +#include "util/pidfile.hpp" + +using namespace datadifferential; + #include #include @@ -60,14 +66,17 @@ struct connection static int maxconns= 1024; static struct connection *socket_userdata_map; -static struct event_base *event_base; +static struct event_base *event_base= NULL; struct options_st { - char *pid_file; + std::string pid_file; + std::string service; + std::string log_file; bool is_verbose; + bool opt_daemon; options_st() : - pid_file(NULL), + service("9999"), is_verbose(false) { } @@ -185,11 +194,7 @@ static void accept_handler(memcached_socket_t fd, short, void *arg) } } -/** - * Create a socket and bind it to a specific port number - * @param port the port number to bind to - */ -static int server_socket(const char *port) +static bool server_socket(util::log_info_st& log_file, const std::string& service) { struct addrinfo *ai; struct addrinfo hints; @@ -199,19 +204,23 @@ static int server_socket(const char *port) hints.ai_family= AF_UNSPEC; hints.ai_socktype= SOCK_STREAM; - int error= getaddrinfo("127.0.0.1", port, &hints, &ai); + int error= getaddrinfo("127.0.0.1", service.c_str(), &hints, &ai); if (error != 0) { if (error != EAI_SYSTEM) { - std::cerr << "getaddrinfo(): " << gai_strerror(error) << std::endl; + std::string buffer("getaddrinfo: "); + buffer+= gai_strerror(error); + log_file.write(util::VERBOSE_ERROR, buffer.c_str()); } else { - std::cerr << "getaddrinfo(): " << strerror(errno) << std::endl; + std::string buffer("getaddrinfo: "); + buffer+= strerror(errno); + log_file.write(util::VERBOSE_ERROR, buffer.c_str()); } - return 0; + return false; } struct linger ling= {0, 0}; @@ -221,7 +230,9 @@ static int server_socket(const char *port) memcached_socket_t sock= socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sock == INVALID_SOCKET) { - std::cerr << "Failed to create socket: " << strerror(errno) << std::endl; + std::string buffer("Failed to create socket: "); + buffer+= strerror(errno); + log_file.write(util::VERBOSE_ERROR, buffer.c_str()); continue; } @@ -238,7 +249,9 @@ static int server_socket(const char *port) flags= fcntl(sock, F_GETFL, 0); if (flags == -1) { - std::cerr << "Failed to get socket flags: " << strerror(errno) << std::endl; + std::string buffer("Failed to get socket flags: "); + buffer+= strerror(errno); + log_file.write(util::VERBOSE_ERROR, buffer.c_str()); closesocket(sock); continue; } @@ -247,7 +260,9 @@ static int server_socket(const char *port) { if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) { - std::cerr << "Failed to set socket to nonblocking mode: " << strerror(errno) << std::endl; + std::string buffer("Failed to set socket to nonblocking mode: "); + buffer+= strerror(errno); + log_file.write(util::VERBOSE_ERROR, buffer.c_str()); closesocket(sock); continue; } @@ -288,14 +303,18 @@ static int server_socket(const char *port) if (listen(sock, 1024) == SOCKET_ERROR) { - std::cerr << "listen(): " << strerror(errno) << std::endl; + std::string buffer("listen(): "); + buffer+= strerror(errno); + log_file.write(util::VERBOSE_ERROR, buffer.c_str()); closesocket(sock); continue; } if (global_options.is_verbose) { - std::cout << "Listening to " << port << std::endl; + std::string buffer("Listening to: "); + buffer+= global_options.service; + log_file.write(util::VERBOSE_NOTICE, buffer.c_str()); } server_sockets[num_server_sockets++]= sock; @@ -303,7 +322,7 @@ static int server_socket(const char *port) freeaddrinfo(ai); - return (num_server_sockets > 0) ? 0 : 1; + return (num_server_sockets > 0) ? true : false; } /** @@ -414,13 +433,6 @@ int main(int argc, char **argv) { memcached_binary_protocol_callback_st *interface= &interface_v0_impl; - event_base= event_init(); - if (event_base == NULL) - { - std::cerr << "Failed to create an instance of libevent" << std::endl; - return EXIT_FAILURE; - } - /* * We need to initialize the handlers manually due to a bug in the * warnings generated by struct initialization in gcc (all the way up to 4.4) @@ -432,27 +444,31 @@ int main(int argc, char **argv) enum long_option_t { OPT_HELP, OPT_VERBOSE, + OPT_DAEMON, OPT_PROTOCOL_VERSION, OPT_VERSION, OPT_PORT, OPT_MAX_CONNECTIONS, + OPT_LOGFILE, OPT_PIDFILE }; static struct option long_options[]= { - {"help", no_argument, NULL, OPT_HELP}, - {"port", required_argument, NULL, OPT_PORT}, - {"verbose", no_argument, NULL, OPT_VERBOSE}, - {"protocol", required_argument, NULL, OPT_PROTOCOL_VERSION}, - {"version", no_argument, NULL, OPT_VERSION}, - {"max-connections", required_argument, NULL, OPT_MAX_CONNECTIONS}, - {"pid-file", required_argument, NULL, OPT_PIDFILE}, + { "help", no_argument, NULL, OPT_HELP }, + { "port", required_argument, NULL, OPT_PORT }, + { "verbose", no_argument, NULL, OPT_VERBOSE }, + { "daemon", no_argument, NULL, OPT_DAEMON }, + { "protocol", no_argument, NULL, OPT_PROTOCOL_VERSION }, + { "version", no_argument, NULL, OPT_VERSION }, + { "max-connections", required_argument, NULL, OPT_MAX_CONNECTIONS }, + { "pid-file", required_argument, NULL, OPT_PIDFILE }, + { "log-file", required_argument, NULL, OPT_LOGFILE }, {0, 0, 0, 0} }; + bool opt_help= false; int option_index; - bool has_port= false; bool done= false; while (done == false) { @@ -467,16 +483,26 @@ int main(int argc, char **argv) break; case OPT_PIDFILE: - global_options.pid_file= strdup(optarg); + global_options.pid_file= optarg; + break; + + case OPT_LOGFILE: + global_options.log_file= optarg; break; case OPT_VERBOSE: global_options.is_verbose= true; break; + case OPT_VERSION: + break; + + case OPT_DAEMON: + global_options.opt_daemon= true; + break; + case OPT_PORT: - has_port= true; - (void)server_socket(optarg); + global_options.service= optarg; break; case OPT_MAX_CONNECTIONS: @@ -484,12 +510,8 @@ int main(int argc, char **argv) break; case OPT_HELP: /* FALLTHROUGH */ - std::cout << "Usage: " << argv[0] << std::endl; - for (struct option *ptr_option= long_options; ptr_option->name; ptr_option++) - { - std::cout << "\t" << ptr_option->name << std::endl; - } - return EXIT_SUCCESS; + opt_help= true; + break; default: { @@ -499,42 +521,47 @@ int main(int argc, char **argv) } } - if (has_port == false) + if (opt_help) { - (void)server_socket("9999"); + std::cout << "Usage: " << argv[0] << std::endl; + for (struct option *ptr_option= long_options; ptr_option->name; ptr_option++) + { + std::cout << "\t" << ptr_option->name << std::endl; + } + return EXIT_SUCCESS; } } - if (! initialize_storage()) + if (global_options.opt_daemon) + { + util::daemonize(false, true); + } + + if (initialize_storage() == false) { /* Error message already printed */ return EXIT_FAILURE; } - if (global_options.pid_file) - { - FILE *pid_file; - uint32_t pid; + util::Pidfile _pid_file(global_options.pid_file); - pid_file= fopen(global_options.pid_file, "w+"); + if (_pid_file.create() == false) + { + std::cerr << "Failed to create pid-file" << _pid_file.error_message() << std::endl; + return EXIT_FAILURE; + } - if (pid_file == NULL) - { - perror(strerror(get_socket_errno())); - abort(); - } + util::log_info_st log_file(argv[0], global_options.log_file, false); + log_file.write(util::VERBOSE_NOTICE, "starting log"); - pid= (uint32_t)getpid(); - if (global_options.is_verbose) - { - std::cout << "pid:" << pid << std::endl; - } - fclose(pid_file); + if (server_socket(log_file, global_options.service) == false) + { + return EXIT_FAILURE; } if (num_server_sockets == 0) { - std::cerr << "No server sockets are available" << std::endl; + log_file.write(util::VERBOSE_ERROR, "No server sockets are available."); return EXIT_FAILURE; } @@ -550,20 +577,27 @@ int main(int argc, char **argv) struct memcached_protocol_st *protocol_handle; if ((protocol_handle= memcached_protocol_create_instance()) == NULL) { - std::cerr << "Failed to allocate protocol handle" << std::endl; + log_file.write(util::VERBOSE_ERROR, "No server sockets are available."); return EXIT_FAILURE; } socket_userdata_map= (struct connection*)calloc((size_t)(maxconns), sizeof(struct connection)); if (socket_userdata_map == NULL) { - std::cerr << "Failed to allocate room for connections" << std::endl; + log_file.write(util::VERBOSE_ERROR, "Failed to allocate room for connections"); return EXIT_FAILURE; } memcached_binary_protocol_set_callbacks(protocol_handle, interface); memcached_binary_protocol_set_pedantic(protocol_handle, true); + event_base= event_init(); + if (event_base == NULL) + { + std::cerr << "Failed to create an instance of libevent" << std::endl; + return EXIT_FAILURE; + } + for (int xx= 0; xx < num_server_sockets; ++xx) { struct connection *conn= &socket_userdata_map[server_sockets[xx]]; @@ -574,13 +608,36 @@ int main(int argc, char **argv) event_base_set(event_base, &conn->event); if (event_add(&conn->event, 0) == -1) { - std::cerr << "Failed to add event for " << server_sockets[xx] << std::endl; + log_file.write(util::VERBOSE_ERROR, "Failed to add event"); closesocket(server_sockets[xx]); } } + if (global_options.opt_daemon) + { + if (util::daemon_is_ready(true) == false) + { + log_file.write(util::VERBOSE_ERROR, "Failed for util::daemon_is_ready()"); + return EXIT_FAILURE; + } + } + + /* Serve all of the clients */ - event_base_loop(event_base, 0); + switch (event_base_loop(event_base, 0) == -1) + { + case -1: + log_file.write(util::VERBOSE_ERROR, "event_base_loop() failed"); + break; + + case 1: + log_file.write(util::VERBOSE_ERROR, "event_base_loop(), no events were registered"); + break; + + default: + break; + } + log_file.write(util::VERBOSE_NOTICE, "exiting"); /* NOTREACHED */ return EXIT_SUCCESS; diff --git a/example/t/include.am b/example/t/include.am index 82ba9404..3cb5b809 100644 --- a/example/t/include.am +++ b/example/t/include.am @@ -14,8 +14,8 @@ MEMCACHED_LIGHT_TESTS_LDADDS= \ libtest/libtest.la example_t_memcached_light_SOURCES= example/t/memcached_light.cc example_t_memcached_light_CXXFLAGS = $(AM_CXXFLAGS) -example_t_memcached_light_DEPENDENCIES= $(MEMCACHED_LIGHT_TESTS_LDADDS) -example_t_memcached_light_LDADD= $(example_t_memcached_light_DEPENDENCIES) +example_t_memcached_light_DEPENDENCIES= $(MEMCACHED_LIGHT_TESTS_LDADDS) example/memcached_light +example_t_memcached_light_LDADD= $(MEMCACHED_LIGHT_TESTS_LDADDS) check_PROGRAMS+= example/t/memcached_light noinst_PROGRAMS+= example/t/memcached_light diff --git a/example/t/memcached_light.cc b/example/t/memcached_light.cc index ae996518..44f2770c 100644 --- a/example/t/memcached_light.cc +++ b/example/t/memcached_light.cc @@ -1,8 +1,8 @@ /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: * - * Test memslap + * Test memcat * - * Copyright (C) 2012 Data Differential, http://datadifferential.com/ + * Copyright (C) 2011 Data Differential, http://datadifferential.com/ * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -50,85 +50,127 @@ using namespace libtest; #pragma GCC diagnostic ignored "-Wstrict-aliasing" #endif -static std::string executable; +static std::string executable("example/memcached_light"); static test_return_t help_TEST(void *) { - const char *memcached_light_args[]= { "--help", 0 }; - Application memcached_light_app(executable, true); + const char *args[]= { "--help", 0 }; - test_compare(Application::SUCCESS, memcached_light_app.run(memcached_light_args)); - test_compare(Application::SUCCESS, memcached_light_app.wait()); + test_compare(EXIT_SUCCESS, exec_cmdline(executable, args, true)); return TEST_SUCCESS; } -static test_return_t basic_TEST(void *) +static test_return_t verbose_TEST(void *) { - test_skip(false, true); + const char *args[]= { "--help", "--verbose", 0 }; - char port_buffer[1024]; - snprintf(port_buffer, sizeof(port_buffer), "--port=%d", int(libtest::default_port())); - const char *memcached_light_args[]= { port_buffer, 0 }; - Application memcached_light_app(executable, true); + test_compare(EXIT_SUCCESS, exec_cmdline(executable, args, true)); - test_compare(Application::SUCCESS, memcached_light_app.run(memcached_light_args)); + return TEST_SUCCESS; +} - char instance_buffer[1024]; - int instance_buffer_length= snprintf(instance_buffer, sizeof(instance_buffer), "--BINARY --server=localhost:%d", int(libtest::default_port())); - memcached_st *memc= memcached(instance_buffer, instance_buffer_length); +static test_return_t daemon_TEST(void *) +{ + const char *args[]= { "--help", "--daemon", 0 }; -#if 0 - for (size_t x= 0; x < 24; x++) - { - char id_buffer[1024]; - int length= snprintf(id_buffer, sizeof(id_buffer), "%u", uint32_t(x)); - test_compare_hint(MEMCACHED_NOTFOUND, memcached_delete(memc, id_buffer, length, 0), - id_buffer); - } -#endif + test_compare(EXIT_SUCCESS, exec_cmdline(executable, args, true)); - // We fail since we are just outright killing it. - test_compare(Application::FAILURE, memcached_light_app.wait()); + return TEST_SUCCESS; +} + +static test_return_t protocol_TEST(void *) +{ + const char *args[]= { "--help", "--protocol", 0 }; - memcached_free(memc); + test_compare(EXIT_SUCCESS, exec_cmdline(executable, args, true)); return TEST_SUCCESS; } -test_st help_TESTS[] ={ - {"--help", true, help_TEST }, - {0, 0, 0} -}; +static test_return_t version_TEST(void *) +{ + const char *args[]= { "--help", "--version", 0 }; + + test_compare(EXIT_SUCCESS, exec_cmdline(executable, args, true)); + + return TEST_SUCCESS; +} + +static test_return_t port_TEST(void *) +{ + const char *args[]= { "--help", "--port=9090", 0 }; + + test_compare(EXIT_SUCCESS, exec_cmdline(executable, args, true)); + + return TEST_SUCCESS; +} -test_st basic_TESTS[] ={ - {"--port", true, basic_TEST }, +static test_return_t pid_file_TEST(void *) +{ + const char *args[]= { "--help", "--pid-file=/tmp/foo.pid", 0 }; + + test_compare(EXIT_SUCCESS, exec_cmdline(executable, args, true)); + + return TEST_SUCCESS; +} + +static test_return_t log_file_TEST(void *) +{ + const char *args[]= { "--help", "--log-file=/tmp/foo.log", 0 }; + + test_compare(EXIT_SUCCESS, exec_cmdline(executable, args, true)); + + return TEST_SUCCESS; +} + +static test_return_t max_connections_file_TEST(void *) +{ + const char *args[]= { "--help", "--max-connections=/tmp/foo.max_connections", 0 }; + + test_compare(EXIT_SUCCESS, exec_cmdline(executable, args, true)); + + return TEST_SUCCESS; +} + +test_st cmdline_option_TESTS[] ={ + {"--help", true, help_TEST }, + {"--verbose", true, verbose_TEST }, + {"--daemon", true, daemon_TEST }, + {"--protocol", true, protocol_TEST }, + {"--version", true, version_TEST }, + {"--port", true, port_TEST }, + {"--pid-file", true, pid_file_TEST }, + {"--log-file", true, log_file_TEST }, + {"--max-connections", true, max_connections_file_TEST }, {0, 0, 0} }; collection_st collection[] ={ - {"--help", 0, 0, help_TESTS }, - {"basics", 0, 0, basic_TESTS }, + {"command line options", 0, 0, cmdline_option_TESTS }, {0, 0, 0, 0} }; static void *world_create(server_startup_st& servers, test_return_t& error) { - if (HAVE_MEMCACHED_BINARY == 0) + if (HAVE_MEMCACHED_LIGHT_BINARY == 0) { error= TEST_SKIPPED; return NULL; } + if (server_startup(servers, "memcached-light", libtest::default_port(), 0, NULL) == 0) + { + error= TEST_FAILURE; + } + return &servers; } void get_world(Framework *world) { - executable= "./example/memcached_light"; world->collections= collection; world->_create= world_create; } - diff --git a/libtest/memcached.cc b/libtest/memcached.cc index 271e2c3b..6f81dd6c 100644 --- a/libtest/memcached.cc +++ b/libtest/memcached.cc @@ -85,7 +85,7 @@ public: pid_t get_pid(bool error_is_ok) { // Memcached is slow to start, so we need to do this - if (not pid_file().empty()) + if (pid_file().empty() == false) { if (error_is_ok and not wait_for_pidfile()) { @@ -222,6 +222,107 @@ public: bool build(size_t argc, const char *argv[]); }; +class MemcachedLight : public libtest::Server +{ + +public: + MemcachedLight(const std::string& host_arg, const in_port_t port_arg): + libtest::Server(host_arg, port_arg) + { + set_pid_file(); + } + + pid_t get_pid(bool error_is_ok) + { + // Memcached is slow to start, so we need to do this + if (not pid_file().empty()) + { + if (error_is_ok and not wait_for_pidfile()) + { + Error << "Pidfile was not found:" << pid_file(); + return -1; + } + } + + bool success= false; + std::stringstream error_message; + pid_t local_pid= get_pid_from_file(pid_file(), error_message); + if (local_pid > 0) + { + if (::kill(local_pid, 0) > 0) + { + success= true; + } + } + + if (error_is_ok and ((success or not is_pid_valid(local_pid)))) + { + Error << "kill(" << " pid: " << local_pid << " errno:" << strerror(errno) << " for:" << *this; + } + + return local_pid; + } + + bool ping() + { + // Memcached is slow to start, so we need to do this + if (not pid_file().empty()) + { + if (not wait_for_pidfile()) + { + Error << "Pidfile was not found:" << pid_file(); + return false; + } + } + + std::stringstream error_message; + pid_t local_pid= get_pid_from_file(pid_file(), error_message); + if (local_pid > 0) + { + if (::kill(local_pid, 0) == 0) + { + return true; + } + } + + return false; + } + + const char *name() + { + return "memcached_light"; + }; + + const char *executable() + { + return MEMCACHED_LIGHT_BINARY; + } + + const char *daemon_file_option() + { + return "--daemon"; + } + + virtual void port_option(Application& app, in_port_t arg) + { + char buffer[1024]; + snprintf(buffer, sizeof(buffer), "--port=%d", int(arg)); + app.add_option(buffer); + } + + bool has_port_option() const + { + return true; + } + + bool is_libtool() + { + return true; + } + + bool build(size_t argc, const char *argv[]); +}; + class MemcachedSaSL : public Memcached { public: @@ -330,7 +431,17 @@ bool Memcached::build(size_t argc, const char *argv[]) add_option(sasl()); } - for (int x= 1 ; x < argc ; x++) + for (int x= 0 ; x < argc ; x++) + { + add_option(argv[x]); + } + + return true; +} + +bool MemcachedLight::build(size_t argc, const char *argv[]) +{ + for (int x= 0 ; x < argc ; x++) { add_option(argv[x]); } @@ -350,6 +461,11 @@ libtest::Server *build_memcached_socket(const std::string& socket_file, const in return new Memcached(socket_file, try_port, true); } +libtest::Server *build_memcached_light(const std::string& hostname, const in_port_t try_port) +{ + return new MemcachedLight(hostname, try_port); +} + libtest::Server *build_memcached_sasl(const std::string& hostname, const in_port_t try_port, const std::string& username, const std::string &password) { diff --git a/libtest/memcached.h b/libtest/memcached.h index a0899614..4594d5b6 100644 --- a/libtest/memcached.h +++ b/libtest/memcached.h @@ -25,6 +25,8 @@ namespace libtest { libtest::Server *build_memcached(const std::string& hostname, const in_port_t try_port); +libtest::Server *build_memcached_light(const std::string& socket_file, const in_port_t try_port); + libtest::Server *build_memcached_socket(const std::string& socket_file, const in_port_t try_port); libtest::Server *build_memcached_sasl(const std::string& hostname, const in_port_t try_port, const std::string& username, const std::string& password); diff --git a/libtest/server.cc b/libtest/server.cc index 66575e74..8ca6b53e 100644 --- a/libtest/server.cc +++ b/libtest/server.cc @@ -186,10 +186,16 @@ bool Server::start() // If we happen to have a pid file, lets try to kill it if (pid_file().empty() == false) { - Error << "We are going to kill it off"; - kill_file(pid_file()); + if (kill_file(pid_file()) == false) + { + fatal_message("Failed to kill off server after startup occurred, when pinging failed"); + } + Error << "Failed to ping() server started, having pid_file. exec:" << _running; + } + else + { + Error << "Failed to ping() server started. exec:" << _running; } - Error << "Failed to ping() server started with:" << _running; _running.clear(); return false; } diff --git a/libtest/server_container.cc b/libtest/server_container.cc index 244f80e6..6d0c785e 100644 --- a/libtest/server_container.cc +++ b/libtest/server_container.cc @@ -195,11 +195,20 @@ bool server_startup(server_startup_st& construct, const std::string& server_type } } } + else if (server_type.compare("memcached-light") == 0) + { + if (MEMCACHED_LIGHT_BINARY) + { + if (HAVE_LIBMEMCACHED) + { + server= build_memcached_light("localhost", try_port); + } + } + } if (server == NULL) { - Error << "Failure occured while creating server: " << server_type; - return false; + fatal_message("Launching of an unknown server was attempted"); } /* diff --git a/libtest/unittest.cc b/libtest/unittest.cc index 53fb605f..95ab7ce1 100644 --- a/libtest/unittest.cc +++ b/libtest/unittest.cc @@ -241,6 +241,18 @@ static test_return_t gearmand_cycle_test(void *object) return TEST_SUCCESS; } +static test_return_t memcached_light_cycle_TEST(void *object) +{ + server_startup_st *servers= (server_startup_st*)object; + test_true(servers); + + test_skip(true, bool(HAVE_MEMCACHED_LIGHT_BINARY)); + + test_true(server_startup(*servers, "memcached-light", get_free_port(), 0, NULL)); + + return TEST_SUCCESS; +} + static test_return_t memcached_cycle_test(void *object) { server_startup_st *servers= (server_startup_st*)object; @@ -565,6 +577,7 @@ static test_return_t check_for_libmemcached(void *) test_st memcached_tests[] ={ {"memcached startup-shutdown", 0, memcached_cycle_test }, + {"memcached-light startup-shutdown", 0, memcached_light_cycle_TEST }, {"memcached(socket file) startup-shutdown", 0, memcached_socket_cycle_test }, {"memcached_sasl() startup-shutdown", 0, memcached_sasl_test }, {"_compare(memcached_return_t)", 0, _compare_memcached_return_t_test }, diff --git a/util/include.am b/util/include.am index 6f848f38..2dcf1c43 100644 --- a/util/include.am +++ b/util/include.am @@ -14,6 +14,7 @@ noinst_HEADERS+= \ util/daemon.hpp \ util/instance.hpp \ util/logfile.hpp \ + util/log.hpp \ util/operation.hpp \ util/signal.hpp \ util/string.hpp \ diff --git a/util/log.hpp b/util/log.hpp new file mode 100644 index 00000000..b7f257d7 --- /dev/null +++ b/util/log.hpp @@ -0,0 +1,198 @@ +/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab: + * + * libtest + * + * Copyright (C) 2011-2012 Data Differential, http://datadifferential.com/ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#define UTIL_MAX_ERROR_SIZE 2048 + +namespace datadifferential { +namespace util { + +/** Verbosity levels. + */ +enum verbose_t +{ + // Logging this will cause shutdown + VERBOSE_FATAL= LOG_EMERG, // syslog:LOG_EMERG + + VERBOSE_ALERT= LOG_ALERT, // syslog:LOG_ALERT + VERBOSE_CRITICAL= LOG_CRIT, // syslog:LOG_CRIT + + VERBOSE_ERROR= LOG_ERR, // syslog:LOG_ERR + + VERBOSE_WARN= LOG_WARNING, // syslog:LOG_WARNING + + VERBOSE_NOTICE= LOG_NOTICE, // syslog:LOG_NOTICE + + VERBOSE_INFO= LOG_INFO, // syslog:LOG_INFO + + VERBOSE_DEBUG= LOG_DEBUG // syslog:LOG_DEBUG +}; + + +struct log_info_st +{ + std::string name; + std::string filename; + int fd; + bool opt_syslog; + bool opt_file; + bool init_success; + + log_info_st(const std::string& name_arg, const std::string &filename_arg, bool syslog_arg) : + name(name_arg), + filename(filename_arg), + fd(-1), + opt_syslog(syslog_arg), + opt_file(false), + init_success(false) + { + if (opt_syslog) + { + openlog(name.c_str(), LOG_PID | LOG_NDELAY, LOG_USER); + } + + init(); + } + + void init() + { + if (filename.size()) + { + if (filename.compare("stderr") == 0) + { + fd= STDERR_FILENO; + } + else + { + fd= open(filename.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0644); + if (fd == -1) + { + if (opt_syslog) + { + char buffer[1024]; + getcwd(buffer, sizeof(buffer)); + syslog(LOG_ERR, "Could not open log file \"%.*s\", from \"%s\", open failed with (%s)", + int(filename.size()), filename.c_str(), + buffer, + strerror(errno)); + } + std::cerr << "Could not open log file for writing, switching to stderr." << std::endl; + + fd= STDERR_FILENO; + } + } + + opt_file= true; + } + + init_success= true; + } + + bool initialized() const + { + return init_success; + } + + int file() const + { + return fd; + } + + void write(verbose_t verbose, const char *mesg) + { + if (opt_file) + { + char buffer[UTIL_MAX_ERROR_SIZE]; + int buffer_length= snprintf(buffer, sizeof(buffer), "%7s %s\n", verbose_name(verbose), mesg); + if (::write(file(), buffer, buffer_length) == -1) + { + std::cerr << "Could not write to log file." << std::endl; + syslog(LOG_EMERG, "gearmand could not open log file %s, got error %s", filename.c_str(), strerror(errno)); + } + + } + + if (opt_syslog) + { + syslog(int(verbose), "%7s %s", verbose_name(verbose), mesg); + } + } + + ~log_info_st() + { + if (fd != -1 and fd != STDERR_FILENO) + { + close(fd); + } + + if (opt_syslog) + { + closelog(); + } + } + +private: + const char *verbose_name(verbose_t verbose) + { + switch (verbose) + { + case VERBOSE_FATAL: + return "FATAL"; + + case VERBOSE_ALERT: + return "ALERT"; + + case VERBOSE_CRITICAL: + return "CRITICAL"; + + case VERBOSE_ERROR: + return "ERROR"; + + case VERBOSE_WARN: + return "WARNING"; + + case VERBOSE_NOTICE: + return "NOTICE"; + + case VERBOSE_INFO: + return "INFO"; + + case VERBOSE_DEBUG: + return "DEBUG"; + + default: + break; + } + + return "UNKNOWN"; + } +}; + +} // namespace util +} // namespace datadifferential -- 2.30.2