/*
CTDB event daemon
Copyright (C) Amitay Isaacs 2018
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 3 of the License, or
(at your option) any later version.
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.
You should have received a copy of the GNU General Public License
along with this program; if not, see .
*/
#include "replace.h"
#include "system/filesys.h"
#include
#include
#include
#include "lib/util/tevent_unix.h"
#include "common/logging.h"
#include "common/path.h"
#include "common/sock_daemon.h"
#include "event/event_private.h"
struct event_daemon_state {
TALLOC_CTX *mem_ctx;
char *socket;
char *pidfile;
struct tevent_context *ev;
struct event_config *config;
struct sock_daemon_context *sockd;
struct event_context *eventd;
};
static int event_daemon_startup(void *private_data)
{
struct event_daemon_state *e_state = talloc_get_type_abort(
private_data, struct event_daemon_state);
int ret;
ret = event_context_init(e_state,
e_state->ev,
e_state->config,
&e_state->eventd);
if (ret != 0) {
D_ERR("Failed to initialize event context\n");
return ret;
}
return 0;
}
static int event_daemon_reconfigure(void *private_data)
{
struct event_daemon_state *e_state = talloc_get_type_abort(
private_data, struct event_daemon_state);
int ret;
ret = event_config_reload(e_state->config);
if (ret != 0) {
D_WARNING("Configuration reload failed\n");
}
return 0;
}
static void event_daemon_shutdown(void *private_data)
{
struct event_daemon_state *e_state = talloc_get_type_abort(
private_data, struct event_daemon_state);
TALLOC_FREE(e_state->eventd);
}
static bool event_client_connect(struct sock_client_context *client,
pid_t pid,
void *private_data)
{
struct event_daemon_state *e_state = talloc_get_type_abort(
private_data, struct event_daemon_state);
int ret;
ret = eventd_client_add(e_state->eventd, client);
if (ret != 0) {
D_ERR("Failed to register client, ret=%d\n", ret);
return false;
}
return true;
}
static void event_client_disconnect(struct sock_client_context *client,
void *private_data)
{
struct event_daemon_state *e_state = talloc_get_type_abort(
private_data, struct event_daemon_state);
eventd_client_del(e_state->eventd, client);
}
struct event_client_state {
struct tevent_context *ev;
struct event_context *eventd;
struct sock_client_context *client;
uint8_t *buf;
size_t buflen;
};
static void event_client_request_done(struct tevent_req *subreq);
static void event_client_reply_done(struct tevent_req *subreq);
static struct tevent_req *event_client_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct sock_client_context *client,
uint8_t *buf,
size_t buflen,
void *private_data)
{
struct event_daemon_state *e_state = talloc_get_type_abort(
private_data, struct event_daemon_state);
struct tevent_req *req, *subreq;
struct event_client_state *state;
req = tevent_req_create(mem_ctx, &state, struct event_client_state);
if (req == NULL) {
return NULL;
}
state->ev = ev;
state->eventd = e_state->eventd;
state->client = client;
subreq = event_pkt_send(state, ev, e_state->eventd, buf, buflen);
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
}
tevent_req_set_callback(subreq, event_client_request_done, req);
return req;
}
static void event_client_request_done(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
struct event_client_state *state = tevent_req_data(
req, struct event_client_state);
int ret = 0;
bool ok;
ok = event_pkt_recv(subreq, &ret, state, &state->buf, &state->buflen);
TALLOC_FREE(subreq);
if (!ok) {
tevent_req_error(req, ret);
return;
}
ok = eventd_client_exists(state->eventd, state->client);
if (!ok) {
/* Client has already disconnected */
talloc_free(state->buf);
tevent_req_done(req);
return;
}
subreq = sock_socket_write_send(state,
state->ev,
state->client,
state->buf,
state->buflen);
if (tevent_req_nomem(subreq, req)) {
talloc_free(state->buf);
return;
}
tevent_req_set_callback(subreq, event_client_reply_done, req);
}
static void event_client_reply_done(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
struct event_client_state *state = tevent_req_data(
req, struct event_client_state);
int ret = 0;
bool ok;
talloc_free(state->buf);
ok = sock_socket_write_recv(subreq, &ret);
TALLOC_FREE(subreq);
if (!ok) {
D_ERR("Sending reply failed\n");
tevent_req_error(req, ret);
return;
}
tevent_req_done(req);
}
static bool event_client_recv(struct tevent_req *req, int *perr)
{
if (tevent_req_is_unix_error(req, perr)) {
return false;
}
return true;
}
static struct {
int pid;
int startup_fd;
} options = {
.pid = -1,
.startup_fd = -1,
};
struct poptOption cmdline_options[] = {
POPT_AUTOHELP
{ "pid", 'P', POPT_ARG_INT, &options.pid, 0,
"pid to wait for", "PID" },
{ "startup-fd", 'S', POPT_ARG_INT, &options.startup_fd, 0,
"file descriptor to notify of successful start", "FD" },
POPT_TABLEEND
};
int main(int argc, const char **argv)
{
poptContext pc;
struct event_daemon_state *e_state;
struct sock_daemon_funcs daemon_funcs;
struct sock_socket_funcs socket_funcs;
const char *log_location = "file:";
const char *log_level = "NOTICE";
const char *t;
int interactive = 0;
int opt, ret;
bool ok;
pc = poptGetContext(argv[0],
argc,
argv,
cmdline_options,
0);
while ((opt = poptGetNextOpt(pc)) != -1) {
D_ERR("Invalid options %s: %s\n",
poptBadOption(pc, 0),
poptStrerror(opt));
exit(1);
}
t = getenv("CTDB_INTERACTIVE");
if (t != NULL) {
interactive = 1;
}
e_state = talloc_zero(NULL, struct event_daemon_state);
if (e_state == NULL) {
D_ERR("Memory allocation error\n");
ret = 1;
goto fail;
}
e_state->mem_ctx = talloc_new(e_state);
if (e_state->mem_ctx == NULL) {
D_ERR("Memory allocation error\n");
ret = 1;
goto fail;
}
e_state->socket = path_socket(e_state, "eventd");
if (e_state->socket == NULL) {
D_ERR("Memory allocation error\n");
ret = 1;
goto fail;
}
e_state->pidfile = path_pidfile(e_state, "eventd");
if (e_state->pidfile == NULL) {
D_ERR("Memory allocation error\n");
ret = 1;
goto fail;
}
ret = event_config_init(e_state, &e_state->config);
if (ret != 0) {
D_ERR("Failed to initialize event config\n");
goto fail;
}
e_state->ev = tevent_context_init(e_state->mem_ctx);
if (e_state->ev == NULL) {
D_ERR("Failed to initialize tevent\n");
ret = 1;
goto fail;
}
daemon_funcs = (struct sock_daemon_funcs) {
.startup = event_daemon_startup,
.reconfigure = event_daemon_reconfigure,
.shutdown = event_daemon_shutdown,
};
if (interactive == 0) {
log_location = event_config_log_location(e_state->config);
log_level = event_config_log_level(e_state->config);
}
ret = sock_daemon_setup(e_state->mem_ctx,
"ctdb-eventd",
log_location,
log_level,
&daemon_funcs,
e_state,
&e_state->sockd);
if (ret != 0) {
D_ERR("Failed to setup sock daemon\n");
goto fail;
}
socket_funcs = (struct sock_socket_funcs) {
.connect = event_client_connect,
.disconnect = event_client_disconnect,
.read_send = event_client_send,
.read_recv = event_client_recv,
};
ret = sock_daemon_add_unix(e_state->sockd,
e_state->socket,
&socket_funcs,
e_state);
if (ret != 0) {
D_ERR("Failed to setup socket %s\n", e_state->socket);
goto fail;
}
if (options.startup_fd != -1) {
ok = sock_daemon_set_startup_fd(e_state->sockd,
options.startup_fd);
if (!ok) {
goto fail;
}
}
ret = sock_daemon_run(e_state->ev,
e_state->sockd,
e_state->pidfile,
false,
false,
options.pid);
if (ret == EINTR) {
ret = 0;
}
if (t != NULL) {
talloc_report_full(e_state->mem_ctx, stderr);
}
fail:
talloc_free(e_state);
(void)poptFreeContext(pc);
exit(ret);
}