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")
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)
#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 <event.h>
#include <cassert>
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)
{
}
}
}
-/**
- * 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;
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};
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;
}
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;
}
{
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;
}
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;
freeaddrinfo(ai);
- return (num_server_sockets > 0) ? 0 : 1;
+ return (num_server_sockets > 0) ? true : false;
}
/**
{
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)
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)
{
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:
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:
{
}
}
- 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;
}
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]];
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;
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
/* 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
#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;
}
-
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())
{
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:
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]);
}
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)
{
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);
// 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;
}
}
}
}
+ 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");
}
/*
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;
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 },
util/daemon.hpp \
util/instance.hpp \
util/logfile.hpp \
+ util/log.hpp \
util/operation.hpp \
util/signal.hpp \
util/string.hpp \
--- /dev/null
+/* 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 <cerrno>
+#include <cstdio>
+#include <fcntl.h>
+#include <iostream>
+#include <string>
+#include <syslog.h>
+
+#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