Page MenuHomeClusterLabs Projects

No OneTemporary

diff --git a/src/attr.c b/src/attr.c
index 7dae7f6..96c5e06 100644
--- a/src/attr.c
+++ b/src/attr.c
@@ -1,488 +1,490 @@
/*
* Copyright (C) 2015 Dejan Muhamedagic <dejan@hello-penguin.com>
*
* 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.
*
* This software 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, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
#include <string.h>
#include "attr.h"
#include "booth.h"
#include "ticket.h"
#include "pacemaker.h"
void print_geostore_usage(void)
{
printf(
"Usage:\n"
" geostore {list|set|get|delete} [-t ticket] [options] attr [value]\n"
"\n"
" list: List all attributes\n"
" set: Set attribute to a value\n"
" get: Get attribute's value\n"
" delete: Delete attribute\n"
"\n"
" -t <ticket> Ticket where attribute resides\n"
" (required, if more than one ticket is configured)\n"
"\n"
"Options:\n"
" -c FILE Specify config file [default " BOOTH_DEFAULT_CONF "]\n"
" Can be a path or just a name without \".conf\" suffix\n"
" -s <site> Connect to a different site\n"
" -h Print this help\n"
"\n"
"Examples:\n"
"\n"
" # geostore list -t ticket-A -s 10.121.8.183\n"
" # geostore set -s 10.121.8.183 sr_status ACTIVE\n"
" # geostore get -t ticket-A -s 10.121.8.183 sr_status\n"
" # geostore delete -s 10.121.8.183 sr_status\n"
"\n"
"See the geostore(8) man page for more details.\n"
);
}
/*
* the client side
*/
/* cl has all the input parameters:
* ticket, attr name, attr value
*/
-int test_attr_reply(cmd_result_t reply_code, cmd_request_t cmd)
+int test_attr_reply(struct command_line *cl, cmd_result_t reply_code)
{
int rv = 0;
const char *op_str = "";
- switch (cmd) {
+ switch (cl->type) {
case ATTR_SET: op_str = "set"; break;
case ATTR_GET: op_str = "get"; break;
case ATTR_LIST: op_str = "list"; break;
case ATTR_DEL: op_str = "delete"; break;
default:
log_error("internal error reading reply result!");
return -1;
}
switch (reply_code) {
case RLT_ASYNC:
log_info("%s command sent, result will be returned "
"asynchronously.", op_str);
rv = 0;
break;
case RLT_SYNC_SUCC:
case RLT_SUCCESS:
- if (cmd == ATTR_SET)
+ if (cl->type == ATTR_SET)
log_info("%s succeeded!", op_str);
rv = 0;
break;
case RLT_SYNC_FAIL:
log_info("%s failed!", op_str);
rv = -1;
break;
case RLT_INVALID_ARG:
log_error("ticket \"%s\" does not exist",
- cl.attr_msg.attr.tkt_id);
+ cl->attr_msg.attr.tkt_id);
rv = 1;
break;
case RLT_NO_SUCH_ATTR:
log_error("attribute \"%s\" not set",
- cl.attr_msg.attr.name);
+ cl->attr_msg.attr.name);
rv = 1;
break;
case RLT_AUTH:
log_error("authentication error");
rv = -1;
break;
default:
log_error("got an error code: %x", rv);
rv = -1;
}
return rv;
}
/* read the server's reply
* need to first get the header which contains the length of the
* reply
* return codes:
* -2: header not received
* -1: header received, but message too short
* >=0: success
*/
static int read_server_reply(
struct booth_transport const *tpt, struct booth_site *site,
char *msg)
{
struct boothc_header *header;
int rv;
int len;
header = (struct boothc_header *)msg;
rv = tpt->recv(site, header, sizeof(*header));
if (rv < 0) {
return -2;
}
len = ntohl(header->length);
rv = tpt->recv(site, msg+len, len-sizeof(*header));
if (rv < 0) {
return -1;
}
return rv;
}
-int do_attr_command(struct booth_config *conf_ptr, cmd_request_t cmd)
+int do_attr_command(struct command_line *cl, struct booth_config *conf_ptr)
{
struct booth_site *site = NULL;
struct boothc_header *header;
struct booth_transport const *tpt = NULL;
int len, rv = -1;
char *msg = NULL;
assert(conf_ptr != NULL && conf_ptr->transport != NULL);
+ assert(cl != NULL);
- if (!*cl.site)
+ if (*cl->site == '\0')
site = local;
else {
- if (!find_site_by_name(conf_ptr, cl.site, &site, 1)) {
- log_error("Site \"%s\" not configured.", cl.site);
+ if (!find_site_by_name(conf_ptr, cl->site, &site, 1)) {
+ log_error("Site \"%s\" not configured.", cl->site);
goto out_close;
}
}
if (site->type == ARBITRATOR) {
if (site == local) {
log_error("We're just an arbitrator, no attributes here.");
} else {
- log_error("%s is just an arbitrator, no attributes there.", cl.site);
+ log_error("%s is just an arbitrator, no attributes there.",
+ cl->site);
}
goto out_close;
}
tpt = *conf_ptr->transport + TCP;
- init_header(conf_ptr, &cl.attr_msg.header, cmd, 0, cl.options, 0, 0,
- sizeof(cl.attr_msg));
+ init_header(conf_ptr, &cl->attr_msg.header, cl->type, 0, cl->options,
+ 0, 0, sizeof(cl->attr_msg));
rv = tpt->open(site);
if (rv < 0)
goto out_close;
- rv = tpt->send(conf_ptr, site, &cl.attr_msg, sendmsglen(&cl.attr_msg));
+ rv = tpt->send(conf_ptr, site, &cl->attr_msg, sendmsglen(&cl->attr_msg));
if (rv < 0)
goto out_close;
msg = malloc(MAX_MSG_LEN);
if (!msg) {
log_error("out of memory");
rv = -1;
goto out_close;
}
rv = read_server_reply(tpt, site, msg);
header = (struct boothc_header *)msg;
if (rv < 0) {
if (rv == -1)
- (void)test_attr_reply(ntohl(header->result), cmd);
+ (void) test_attr_reply(cl, ntohl(header->result));
goto out_close;
}
len = ntohl(header->length);
if (check_boothc_header(header, len) < 0) {
log_error("message from %s receive error", site_string(site));
rv = -1;
goto out_close;
}
if (check_auth(conf_ptr, site, msg, len)) {
log_error("%s failed to authenticate", site_string(site));
rv = -1;
goto out_close;
}
- rv = test_attr_reply(ntohl(header->result), cmd);
+ rv = test_attr_reply(cl, ntohl(header->result));
out_close:
if (tpt && site)
tpt->close(site);
if (msg)
free(msg);
return rv;
}
/*
* the server side
*/
/* need to invert gboolean, our success is 0
*/
#define gbool2rlt(i) (i ? RLT_SUCCESS : RLT_SYNC_FAIL)
static void free_geo_attr(gpointer data)
{
struct geo_attr *a = (struct geo_attr *)data;
if (!a)
return;
g_free(a->val);
g_free(a);
}
int store_geo_attr(struct ticket_config *tk, const char *name,
const char *val, int notime)
{
struct geo_attr *a;
GDestroyNotify free_geo_attr_notify = free_geo_attr;
if (!tk)
return -1;
/*
* allocate new, if attr doesn't already exist
* copy the attribute value
* send status
*/
if (!tk->attr)
tk->attr = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, free_geo_attr_notify);
if (!tk->attr) {
log_error("out of memory");
return -1;
}
if (strnlen(name, BOOTH_NAME_LEN) == BOOTH_NAME_LEN)
tk_log_warn("name of the attribute too long (%d+ bytes), skipped",
BOOTH_NAME_LEN);
else if (strnlen(val, BOOTH_ATTRVAL_LEN) == BOOTH_ATTRVAL_LEN)
tk_log_warn("value of the attribute too long (%d+ bytes), skipped",
BOOTH_ATTRVAL_LEN);
else {
a = (struct geo_attr *)calloc(1, sizeof(struct geo_attr));
if (!a) {
log_error("out of memory");
return -1;
}
a->val = g_strdup(val);
if (!notime)
get_time(&a->update_ts);
g_hash_table_insert(tk->attr,
g_strdup(name), a);
}
return 0;
}
static cmd_result_t attr_set(struct booth_config *conf_ptr,
struct ticket_config *tk,
struct boothc_attr_msg *msg)
{
int rc;
assert(conf_ptr != NULL);
rc = store_geo_attr(tk, msg->attr.name, msg->attr.val, 0);
if (rc) {
return RLT_SYNC_FAIL;
}
(void) conf_ptr->ticket_handler->set_attr(tk, msg->attr.name,
msg->attr.val);
return RLT_SUCCESS;
}
static cmd_result_t attr_del(struct booth_config *conf_ptr,
struct ticket_config *tk,
struct boothc_attr_msg *msg)
{
gboolean rv;
gpointer orig_key, value;
assert(conf_ptr != NULL);
/*
* lookup attr
* deallocate, if found
* send status
*/
if (!tk->attr)
return RLT_NO_SUCH_ATTR;
rv = g_hash_table_lookup_extended(tk->attr, msg->attr.name,
&orig_key, &value);
if (!rv)
return RLT_NO_SUCH_ATTR;
rv = g_hash_table_remove(tk->attr, msg->attr.name);
(void) conf_ptr->ticket_handler->del_attr(tk, msg->attr.name);
return gbool2rlt(rv);
}
static void
append_attr(gpointer key, gpointer value, gpointer user_data)
{
char *attr_name = (char *)key;
struct geo_attr *a = (struct geo_attr *)value;
GString *data = (GString *)user_data;
char time_str[64];
time_t ts;
if (is_time_set(&a->update_ts)) {
ts = wall_ts(&a->update_ts);
strftime(time_str, sizeof(time_str), "%F %T",
localtime(&ts));
} else {
time_str[0] = '\0';
}
g_string_append_printf(data, "%s %s %s\n",
attr_name, a->val, time_str);
}
static cmd_result_t attr_get(struct booth_config *conf_ptr,
struct ticket_config *tk, int fd,
struct boothc_attr_msg *msg)
{
cmd_result_t rv = RLT_SUCCESS;
struct boothc_hdr_msg hdr;
struct geo_attr *a;
GString *attr_val;
/*
* lookup attr
* send value
*/
a = (struct geo_attr *)g_hash_table_lookup(tk->attr, msg->attr.name);
if (!a)
return RLT_NO_SUCH_ATTR;
attr_val = g_string_new(NULL);
if (!attr_val) {
log_error("out of memory");
return RLT_SYNC_FAIL;
}
g_string_printf(attr_val, "%s\n", a->val);
init_header(conf_ptr, &hdr.header, ATTR_GET, 0, 0, RLT_SUCCESS, 0,
sizeof(hdr) + attr_val->len);
if (send_header_plus(conf_ptr, fd, &hdr, attr_val->str, attr_val->len))
rv = RLT_SYNC_FAIL;
if (attr_val)
g_string_free(attr_val, FALSE);
return rv;
}
static cmd_result_t attr_list(struct booth_config *conf_ptr,
struct ticket_config *tk, int fd,
struct boothc_attr_msg *msg)
{
GString *data;
cmd_result_t rv;
struct boothc_hdr_msg hdr;
/*
* list all attributes for the ticket
* send the list
*/
data = g_string_sized_new(512);
if (!data) {
log_error("out of memory");
return RLT_SYNC_FAIL;
}
g_hash_table_foreach(tk->attr, append_attr, data);
init_header(conf_ptr, &hdr.header, ATTR_LIST, 0, 0, RLT_SUCCESS, 0,
sizeof(hdr) + data->len);
rv = send_header_plus(conf_ptr, fd, &hdr, data->str, data->len);
if (data)
g_string_free(data, FALSE);
return rv;
}
int process_attr_request(struct booth_config *conf_ptr,
struct client *req_client, void *buf)
{
cmd_result_t rv = RLT_SYNC_FAIL;
struct ticket_config *tk;
int cmd;
struct boothc_attr_msg *msg;
struct boothc_hdr_msg hdr;
msg = (struct boothc_attr_msg *)buf;
cmd = ntohl(msg->header.cmd);
if (!check_ticket(conf_ptr, msg->attr.tkt_id, &tk)) {
log_warn("client referenced unknown ticket %s",
msg->attr.tkt_id);
rv = RLT_INVALID_ARG;
goto reply_now;
}
switch (cmd) {
case ATTR_LIST:
rv = attr_list(conf_ptr, tk, req_client->fd, msg);
if (rv)
goto reply_now;
return 1;
case ATTR_GET:
rv = attr_get(conf_ptr, tk, req_client->fd, msg);
if (rv)
goto reply_now;
return 1;
case ATTR_SET:
rv = attr_set(conf_ptr, tk, msg);
break;
case ATTR_DEL:
rv = attr_del(conf_ptr, tk, msg);
break;
}
reply_now:
init_header(conf_ptr, &hdr.header, CL_RESULT, 0, 0, rv, 0, sizeof(hdr));
send_header_plus(conf_ptr, req_client->fd, &hdr, NULL, 0);
return 1;
}
/* read attr message from another site */
/* this is a NOOP and it should never be invoked
* only clients retrieve/manage attributes and they connect
* directly to the target site
*/
int attr_recv(struct booth_config *conf_ptr, void *buf,
struct booth_site *source)
{
struct boothc_attr_msg *msg;
struct ticket_config *tk;
msg = (struct boothc_attr_msg *)buf;
log_warn("unexpected attribute message from %s",
site_string(source));
if (!check_ticket(conf_ptr, msg->attr.tkt_id, &tk)) {
log_warn("got invalid ticket name %s from %s",
msg->attr.tkt_id, site_string(source));
source->invalid_cnt++;
return -1;
}
return 0;
}
diff --git a/src/attr.h b/src/attr.h
index 2671211..d35fcad 100644
--- a/src/attr.h
+++ b/src/attr.h
@@ -1,74 +1,84 @@
/*
* Copyright (C) 2015 Dejan Muhamedagic <dejan@hello-penguin.com>
*
* 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.
*
* This software 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, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef _ATTR_H
#define _ATTR_H
#define ATTR_PROG "geostore"
#include "b_config.h"
#include "log.h"
#include <stdlib.h>
#include <sys/types.h>
#include "booth.h"
#include "timer.h"
#include <glib.h>
void print_geostore_usage(void);
-int test_attr_reply(cmd_result_t reply_code, cmd_request_t cmd);
+
+/**
+ * @internal
+ * Late handling of the response towards the client
+ *
+ * @param[in] cl parsed command line form
+ * @param[in] reply_code what the inner handling returns
+ *
+ * @return 0 on success, -1 on failure, 1 when "cannot serve"
+ */
+int test_attr_reply(struct command_line *cl, cmd_result_t reply_code);
/**
* @internal
* Carry out a geo-atribute related command
*
+ * @param[in] cl parsed command line structure
* @param[inout] conf_ptr config object to refer to
- * @param[in] cmd what to perform
*
* @return 0 or negative value (-1 or -errno) on error
*/
-int do_attr_command(struct booth_config *conf_ptr, cmd_request_t cmd);
+int do_attr_command(struct command_line *cl, struct booth_config *conf_ptr);
/**
* @internal
* Facade to handle geostore related operations
*
* @param[inout] conf_ptr config object to refer to
* @param[in] req_client client structure of the sender
* @param[in] buf message itself
*
* @return 1 or see #attr_list, #attr_get, #attr_set, #attr_del
*/
int process_attr_request(struct booth_config *conf_ptr,
struct client *req_client, void *buf);
/**
* @internal
* Second stage of incoming datagram handling (after authentication)
*
* @param[inout] conf_ptr config object to refer to
* @param[in] buf message itself
* @param[in] source site structure of the sender
*
* @return -1 on error, 0 otherwise
*/
int attr_recv(struct booth_config *conf_ptr, void *buf,
struct booth_site *source);
int store_geo_attr(struct ticket_config *tk, const char *name, const char *val, int notime);
#endif /* _ATTR_H */
diff --git a/src/booth.h b/src/booth.h
index 68fc748..5dc2795 100644
--- a/src/booth.h
+++ b/src/booth.h
@@ -1,416 +1,427 @@
/*
* Copyright (C) 2011 Jiaju Zhang <jjzhang@suse.de>
* Copyright (C) 2013-2014 Philipp Marek <philipp.marek@linbit.com>
*
* 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.
*
* This software 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, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef _BOOTH_H
#define _BOOTH_H
#include <stdint.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <glib.h>
#include "timer.h"
struct booth_config;
#define BOOTH_RUN_DIR "/var/run/booth/"
#define BOOTH_LOG_DIR "/var/log"
#define BOOTH_LOGFILE_NAME "booth.log"
#define BOOTH_DEFAULT_CONF_DIR "/etc/booth/"
#define BOOTH_DEFAULT_CONF_NAME "booth"
#define BOOTH_DEFAULT_CONF_EXT ".conf"
#define BOOTH_DEFAULT_CONF \
BOOTH_DEFAULT_CONF_DIR BOOTH_DEFAULT_CONF_NAME BOOTH_DEFAULT_CONF_EXT
#define DAEMON_NAME "boothd"
#define BOOTH_PATH_LEN 127
#define BOOTH_MAX_KEY_LEN 64
#define BOOTH_MIN_KEY_LEN 8
/* hash size is 160 bits (sha1), but add a bit more space in case
* stronger hashes are required */
#define BOOTH_MAC_SIZE 24
/* tolerate packets which are not older than 10 minutes */
#define BOOTH_DEFAULT_MAX_TIME_SKEW 600
#define BOOTH_DEFAULT_PORT 9929
#define BOOTHC_MAGIC 0x5F1BA08C
#define BOOTHC_VERSION 0x00010003
/** Timeout value for poll().
* Determines frequency of periodic jobs, eg. when send-retries are done.
* See process_tickets(). */
#define POLL_TIMEOUT 100
/** @{ */
/** The on-network data structures and constants. */
#define BOOTH_NAME_LEN 64
#define BOOTH_ATTRVAL_LEN 128
#define CHAR2CONST(a,b,c,d) ((a << 24) | (b << 16) | (c << 8) | d)
/* Says that the ticket shouldn't be active anywhere.
* NONE wouldn't be specific enough. */
#define NO_ONE ((uint32_t)-1)
/* Says that another one should recover. */
#define TICKET_LOST CHAR2CONST('L', 'O', 'S', 'T')
typedef char boothc_site[BOOTH_NAME_LEN];
typedef char boothc_ticket[BOOTH_NAME_LEN];
typedef char boothc_attr[BOOTH_NAME_LEN];
typedef char boothc_attr_value[BOOTH_ATTRVAL_LEN];
/* message option bits */
enum {
BOOTH_OPT_AUTH = 1, /* authentication */
BOOTH_OPT_ATTR = 4, /* attr message type, otherwise ticket */
};
struct boothc_header {
/** Various options, message type, authentication
*/
uint32_t opts;
/** Generation info (used for authentication)
* This is something that would need to be monotone
* incremental. CLOCK_MONOTONIC should fit the purpose. On
* failover, however, it may happen that the new host has a
* clock which is significantly behind the clock of old host.
* We'll need to relax a bit for the nodes which are starting
* (just accept all OP_STATUS).
*/
uint32_t secs; /* seconds */
uint32_t usecs; /* microseconds */
/** BOOTHC_MAGIC */
uint32_t magic;
/** BOOTHC_VERSION */
uint32_t version;
/** Packet source; site_id. See add_site(). */
uint32_t from;
/** Length including header */
uint32_t length;
/** The command respectively protocol state. See cmd_request_t. */
uint32_t cmd;
/** The matching request (what do we reply to). See cmd_request_t. */
uint32_t request;
/** Command options. */
uint32_t options;
/** The reason for this RPC. */
uint32_t reason;
/** Result of operation. 0 == OK */
uint32_t result;
char data[0];
} __attribute__((packed));
struct ticket_msg {
/** Ticket name. */
boothc_ticket id;
/** Current leader. May be NO_ONE. See add_site().
* For a OP_REQ_VOTE this is */
uint32_t leader;
/** Current term. */
uint32_t term;
uint32_t term_valid_for;
/* Perhaps we need to send a status along, too - like
* starting, running, stopping, error, ...? */
} __attribute__((packed));
struct attr_msg {
/** Ticket name. */
boothc_ticket tkt_id;
/** Attribute name. */
boothc_attr name;
/** The value. */
boothc_attr_value val;
} __attribute__((packed));
/* GEO attributes
* attributes should be regularly updated.
*/
struct geo_attr {
/** Update timestamp. */
timetype update_ts;
/** The value. */
char *val;
/** Who set it (currently unused)
struct booth_site *origin;
*/
} __attribute__((packed));
struct hmac {
/** hash id, currently set to constant BOOTH_HASH */
uint32_t hid;
/** the calculated hash, BOOTH_MAC_SIZE is big enough to
* accommodate the hash of type hid */
unsigned char hash[BOOTH_MAC_SIZE];
} __attribute__((packed));
struct boothc_hdr_msg {
struct boothc_header header;
struct hmac hmac;
} __attribute__((packed));
struct boothc_ticket_msg {
struct boothc_header header;
struct ticket_msg ticket;
struct hmac hmac;
} __attribute__((packed));
struct boothc_attr_msg {
struct boothc_header header;
struct attr_msg attr;
struct hmac hmac;
} __attribute__((packed));
typedef enum {
/* 0x43 = "C"ommands */
CMD_LIST = CHAR2CONST('C', 'L', 's', 't'),
CMD_GRANT = CHAR2CONST('C', 'G', 'n', 't'),
CMD_REVOKE = CHAR2CONST('C', 'R', 'v', 'k'),
CMD_PEERS = CHAR2CONST('P', 'e', 'e', 'r'),
/* Replies */
CL_RESULT = CHAR2CONST('R', 's', 'l', 't'),
CL_LIST = CHAR2CONST('R', 'L', 's', 't'),
CL_GRANT = CHAR2CONST('R', 'G', 'n', 't'),
CL_REVOKE = CHAR2CONST('R', 'R', 'v', 'k'),
/* get status from another server */
OP_STATUS = CHAR2CONST('S', 't', 'a', 't'),
OP_MY_INDEX = CHAR2CONST('M', 'I', 'd', 'x'), /* reply to status */
/* Raft */
OP_REQ_VOTE = CHAR2CONST('R', 'V', 'o', 't'), /* start election */
OP_VOTE_FOR = CHAR2CONST('V', 't', 'F', 'r'), /* reply to REQ_VOTE */
OP_HEARTBEAT= CHAR2CONST('H', 'r', 't', 'B'), /* Heartbeat */
OP_ACK = CHAR2CONST('A', 'c', 'k', '.'), /* Ack for heartbeats and revokes */
OP_UPDATE = CHAR2CONST('U', 'p', 'd', 'E'), /* Update ticket */
OP_REVOKE = CHAR2CONST('R', 'e', 'v', 'k'), /* Revoke ticket */
OP_REJECTED = CHAR2CONST('R', 'J', 'C', '!'),
/* Attributes */
ATTR_SET = CHAR2CONST('A', 'S', 'e', 't'),
ATTR_GET = CHAR2CONST('A', 'G', 'e', 't'),
ATTR_DEL = CHAR2CONST('A', 'D', 'e', 'l'),
ATTR_LIST = CHAR2CONST('A', 'L', 's', 't'),
} cmd_request_t;
typedef enum {
/* for compatibility with other functions */
RLT_SUCCESS = 0,
RLT_ASYNC = CHAR2CONST('A', 's', 'y', 'n'),
RLT_MORE = CHAR2CONST('M', 'o', 'r', 'e'),
RLT_SYNC_SUCC = CHAR2CONST('S', 'c', 'c', 's'),
RLT_SYNC_FAIL = CHAR2CONST('F', 'a', 'i', 'l'),
RLT_INVALID_ARG = CHAR2CONST('I', 'A', 'r', 'g'),
RLT_NO_SUCH_ATTR = CHAR2CONST('N', 'A', 't', 'r'),
RLT_CIB_PENDING = CHAR2CONST('P', 'e', 'n', 'd'),
RLT_EXT_FAILED = CHAR2CONST('X', 'P', 'r', 'g'),
RLT_ATTR_PREREQ = CHAR2CONST('A', 'P', 'r', 'q'),
RLT_TICKET_IDLE = CHAR2CONST('T', 'i', 'd', 'l'),
RLT_OVERGRANT = CHAR2CONST('O', 'v', 'e', 'r'),
RLT_PROBABLY_SUCCESS = CHAR2CONST('S', 'u', 'c', '?'),
RLT_BUSY = CHAR2CONST('B', 'u', 's', 'y'),
RLT_AUTH = CHAR2CONST('A', 'u', 't', 'h'),
RLT_TERM_OUTDATED = CHAR2CONST('T', 'O', 'd', 't'),
RLT_TERM_STILL_VALID = CHAR2CONST('T', 'V', 'l', 'd'),
RLT_YOU_OUTDATED = CHAR2CONST('O', 'u', 't', 'd'),
RLT_REDIRECT = CHAR2CONST('R', 'e', 'd', 'r'),
} cmd_result_t;
typedef enum {
/* for compatibility with other functions */
OR_JUST_SO = 0,
OR_AGAIN = CHAR2CONST('A', 'a', 'a', 'a'),
OR_TKT_LOST = CHAR2CONST('T', 'L', 's', 't'),
OR_REACQUIRE = CHAR2CONST('R', 'a', 'c', 'q'),
OR_ADMIN = CHAR2CONST('A', 'd', 'm', 'n'),
OR_LOCAL_FAIL = CHAR2CONST('L', 'o', 'c', 'F'),
OR_STEPDOWN = CHAR2CONST('S', 'p', 'd', 'n'),
OR_SPLIT = CHAR2CONST('S', 'p', 'l', 't'),
} cmd_reason_t;
/* bitwise command options
*/
typedef enum {
OPT_IMMEDIATE = 1, /* immediate grant */
OPT_WAIT = 2, /* wait for the elections' outcome */
OPT_WAIT_COMMIT = 4, /* wait for the ticket commit to CIB */
} cmd_options_t;
/** @} */
/** @{ */
struct booth_site {
/** Calculated ID. See add_site(). */
int site_id;
int type;
int local;
/** Roles, like ACCEPTOR, PROPOSER, or LEARNER. Not really used ATM. */
int role;
boothc_site addr_string;
int tcp_fd;
int udp_fd;
/* 0-based, used for indexing into per-ticket weights.
* -1 for no_leader. */
int index;
uint64_t bitmask;
unsigned short family;
union {
struct sockaddr_in sa4;
struct sockaddr_in6 sa6;
};
int saddrlen;
int addrlen;
/** statistics */
time_t last_recv;
unsigned int sent_cnt;
unsigned int sent_err_cnt;
unsigned int resend_cnt;
unsigned int recv_cnt;
unsigned int recv_err_cnt;
unsigned int sec_cnt;
unsigned int invalid_cnt;
/** last timestamp seen from this site */
uint32_t last_secs;
uint32_t last_usecs;
} __attribute__((packed));
extern struct booth_site *local;
extern struct booth_site *const no_leader;
/** @} */
struct booth_transport;
struct client {
int fd;
const struct booth_transport *transport;
struct boothc_ticket_msg *msg;
int offset; /* bytes read so far into msg */
void (*workfn)(struct booth_config *conf_ptr, int);
void (*deadfn)(int);
};
extern struct client *clients;
extern struct pollfd *pollfds;
/**
* @internal
* For an established-connection socket, finalize the handling callbacks
*
* @param[in] file descriptor of the respective client socket
* @param[inout] tpt precooked transport handling callbacks to finalize
* @param[in] workfn callback to process incoming messages
* @param[in] workfn callback to handle termination of the connection
*
* @return number of clients tracked (incl. this one)
*/
int client_add(int fd, const struct booth_transport *tpt,
void (*workfn)(struct booth_config *conf_ptr, int ci),
void (*deadfn)(int ci));
int find_client_by_fd(int fd);
-void safe_copy(char *dest, char *value, size_t buflen, const char *description);
+
+/**
+ * @internal
+ * Like strncpy, but with explicit protection and better diagnostics
+ *
+ * @param[out] dest where to copy the string to
+ * @param[in] value where to copy the string from
+ * @param[in] buflen nmaximum size of #dest (incl. trailing '\0', or sizeof)
+ * @param[in] description how to refer to the target as
+ *
+ * @return number of clients tracked (incl. this one)
+ */
+void safe_copy(char *dest, const char *value, size_t buflen,
+ const char *description);
/**
* @internal
* Re-read and reflect possibly new contents of the authentication key file
*
* @note XXX UNUSED
*
* @param[inout] conf_ptr config object to refer to
*
* @return 0 in case of success, -1 otherwise
*/
int update_authkey(struct booth_config *conf_ptr);
/**
* @internal
* Response to "get all servers we know about"
*
* @param[inout] conf_ptr config object to refer to
* @param[in] fd file descriptor of the socket to respond to
*/
void list_peers(struct booth_config *conf_ptr, int fd);
struct command_line {
int type; /* ACT_ */
int op; /* OP_ */
int options; /* OPT_ */
char configfile[BOOTH_PATH_LEN];
char lockfile[BOOTH_PATH_LEN];
char site[BOOTH_NAME_LEN];
struct boothc_ticket_msg msg;
struct boothc_attr_msg attr_msg;
};
-extern struct command_line cl;
-
/* http://gcc.gnu.org/onlinedocs/gcc/Typeof.html */
#define min(a__,b__) \
({ typeof (a__) _a = (a__); \
typeof (b__) _b = (b__); \
_a < _b ? _a : _b; })
#define max(a__,b__) \
({ typeof (a__) _a = (a__); \
typeof (b__) _b = (b__); \
_a > _b ? _a : _b; })
#endif /* _BOOTH_H */
diff --git a/src/config.c b/src/config.c
index 0e10aca..0fd86b0 100644
--- a/src/config.c
+++ b/src/config.c
@@ -1,1029 +1,1033 @@
/*
* Copyright (C) 2011 Jiaju Zhang <jjzhang@suse.de>
* Copyright (C) 2013-2014 Philipp Marek <philipp.marek@linbit.com>
*
* 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.
*
* This software 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, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <assert.h>
#include <zlib.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include "b_config.h"
#include "booth.h"
#include "config.h"
#include "raft.h"
#include "ticket.h"
#include "log.h"
static int ticket_size = 0;
static int ticket_realloc(struct booth_config *conf_ptr)
{
const int added = 5;
int had, want;
void *p;
assert(conf_ptr != NULL);
had = conf_ptr->ticket_allocated;
want = had + added;
p = realloc(conf_ptr->ticket, sizeof(struct ticket_config) * want);
if (!p) {
log_error("can't alloc more tickets");
return -ENOMEM;
}
conf_ptr->ticket = p;
memset(conf_ptr->ticket + had, 0,
sizeof(struct ticket_config) * added);
conf_ptr->ticket_allocated = want;
return 0;
}
static void hostname_to_ip(char * hostname)
{
struct hostent *he;
struct in_addr **addr_list;
if ((he = gethostbyname(hostname)) == NULL) {
log_error("can't find IP for the host \"%s\"", hostname);
return;
}
addr_list = (struct in_addr **) he->h_addr_list;
/* Return the first found address */
if (addr_list[0] != NULL) {
strncpy(hostname, inet_ntoa(*addr_list[0]), BOOTH_NAME_LEN - 1);
/* buffer overflow will not happen (IPv6 notation < 63 chars),
but suppress the warnings */
hostname[BOOTH_NAME_LEN - 1] = '\0';
}
else {
log_error("no IP addresses found for the host \"%s\"", hostname);
}
}
static int add_site(struct booth_config *conf_ptr, char *addr_string, int type)
{
int rv;
struct booth_site *site;
uLong nid;
uint32_t mask;
int i;
assert(conf_ptr != NULL);
rv = 1;
if (conf_ptr->site_count == MAX_NODES) {
log_error("too many nodes");
goto out;
}
if (strnlen(addr_string, sizeof(conf_ptr->site[0].addr_string))
>= sizeof(conf_ptr->site[0].addr_string)) {
log_error("site address \"%s\" too long", addr_string);
goto out;
}
site = conf_ptr->site + conf_ptr->site_count;
site->family = AF_INET;
site->type = type;
/* buffer overflow will not hapen (we've already checked that
addr_string will fit incl. terminating '\0' above), but
suppress the warnings with copying everything but the boundary
byte, which is valid as-is, since this last byte will be safely
pre-zeroed from the struct booth_config initialization */
strncpy(site->addr_string, addr_string, sizeof(site->addr_string) - 1);
if (!(inet_pton(AF_INET, site->addr_string, &site->sa4.sin_addr) > 0) &&
!(inet_pton(AF_INET6, site->addr_string, &site->sa6.sin6_addr) > 0)) {
/* Not a valid address, so let us try to convert it into an IP address */
hostname_to_ip(site->addr_string);
}
site->index = conf_ptr->site_count;
site->bitmask = 1 << conf_ptr->site_count;
/* Catch site overflow */
assert(site->bitmask);
conf_ptr->all_bits |= site->bitmask;
if (type == SITE)
conf_ptr->sites_bits |= site->bitmask;
site->tcp_fd = -1;
conf_ptr->site_count++;
rv = 0;
memset(&site->sa6, 0, sizeof(site->sa6));
nid = crc32(0L, NULL, 0);
/* Using the ASCII representation in site->addr_string (both sizeof()
* and strlen()) gives quite a lot of collisions; a brute-force run
* from 0.0.0.0 to 24.0.0.0 gives ~4% collisions, and this tends to
* increase even more.
* Whether there'll be a collision in real-life, with 3 or 5 nodes, is
* another question ... but for now get the ID from the binary
* representation - that had *no* collisions up to 32.0.0.0.
* Note that POSIX mandates inet_pton to arange the address pointed
* to by "dst" in network byte order, assuring little/big-endianess
* mutual compatibility. */
if (inet_pton(AF_INET,
site->addr_string,
&site->sa4.sin_addr) > 0) {
site->family = AF_INET;
site->sa4.sin_family = site->family;
site->sa4.sin_port = htons(conf_ptr->port);
site->saddrlen = sizeof(site->sa4);
site->addrlen = sizeof(site->sa4.sin_addr);
site->site_id = crc32(nid, (void*)&site->sa4.sin_addr, site->addrlen);
} else if (inet_pton(AF_INET6,
site->addr_string,
&site->sa6.sin6_addr) > 0) {
site->family = AF_INET6;
site->sa6.sin6_family = site->family;
site->sa6.sin6_flowinfo = 0;
site->sa6.sin6_port = htons(conf_ptr->port);
site->saddrlen = sizeof(site->sa6);
site->addrlen = sizeof(site->sa6.sin6_addr);
site->site_id = crc32(nid, (void*)&site->sa6.sin6_addr, site->addrlen);
} else {
log_error("Address string \"%s\" is bad", site->addr_string);
rv = EINVAL;
}
/* Make sure we will never collide with NO_ONE,
* or be negative (to get "get_local_id() < 0" working). */
mask = 1 << (sizeof(site->site_id)*8 -1);
assert(NO_ONE & mask);
site->site_id &= ~mask;
/* Test for collisions with other sites */
for(i=0; i<site->index; i++)
if (conf_ptr->site[i].site_id == site->site_id) {
log_error("Got a site-ID collision. Please file a bug on https://github.com/ClusterLabs/booth/issues/new, attaching the configuration file.");
exit(1);
}
out:
return rv;
}
inline static char *skip_while_in(const char *cp, int (*fn)(int), const char *allowed)
{
/* strchr() returns a pointer to the terminator if *cp == 0. */
while (*cp &&
(fn(*cp) ||
strchr(allowed, *cp)))
cp++;
/* discard "const" qualifier */
return (char*)cp;
}
inline static char *skip_while(char *cp, int (*fn)(int))
{
while (fn(*cp))
cp++;
return cp;
}
inline static char *skip_until(char *cp, char expected)
{
while (*cp && *cp != expected)
cp++;
return cp;
}
static inline int is_end_of_line(char *cp)
{
char c = *cp;
return c == '\n' || c == 0 || c == '#';
}
static int add_ticket(struct booth_config *conf_ptr, const char *name,
struct ticket_config **tkp, const struct ticket_config *def)
{
int rv;
struct ticket_config *tk;
assert(conf_ptr != NULL);
if (conf_ptr->ticket_count == conf_ptr->ticket_allocated) {
rv = ticket_realloc(conf_ptr);
if (rv < 0)
return rv;
}
tk = conf_ptr->ticket + conf_ptr->ticket_count;
conf_ptr->ticket_count++;
if (!check_max_len_valid(name, sizeof(tk->name))) {
log_error("ticket name \"%s\" too long.", name);
return -EINVAL;
}
if (find_ticket_by_name(conf_ptr, name, NULL)) {
log_error("ticket name \"%s\" used again.", name);
return -EINVAL;
}
if (* skip_while_in(name, isalnum, "-/")) {
log_error("ticket name \"%s\" invalid; only alphanumeric names.", name);
return -EINVAL;
}
strcpy(tk->name, name);
tk->timeout = def->timeout;
tk->term_duration = def->term_duration;
tk->retries = def->retries;
memcpy(tk->weight, def->weight, sizeof(tk->weight));
tk->mode = def->mode;
if (tkp)
*tkp = tk;
return 0;
}
static int postproc_ticket(struct ticket_config *tk)
{
if (!tk)
return 1;
if (!tk->renewal_freq) {
tk->renewal_freq = tk->term_duration/2;
}
if (tk->timeout*(tk->retries+1) >= tk->renewal_freq) {
log_error("%s: total amount of time to "
"retry sending packets cannot exceed "
"renewal frequency "
"(%d*(%d+1) >= %d)",
tk->name, tk->timeout, tk->retries, tk->renewal_freq);
return 0;
}
return 1;
}
/* returns number of weights, or -1 on bad input. */
static int parse_weights(const char *input, int weights[MAX_NODES])
{
int i, v;
char *cp;
for(i=0; i<MAX_NODES; i++) {
/* End of input? */
if (*input == 0)
break;
v = strtol(input, &cp, 0);
if (input == cp) {
log_error("No integer weight value at \"%s\"", input);
return -1;
}
weights[i] = v;
while (*cp) {
/* Separator characters */
if (isspace(*cp) ||
strchr(",;:-+", *cp))
cp++;
/* Next weight */
else if (isdigit(*cp))
break;
/* Rest */
else {
log_error("Invalid character at \"%s\"", cp);
return -1;
}
}
input = cp;
}
/* Fill rest of vector. */
for(v=i; v<MAX_NODES; v++) {
weights[v] = 0;
}
return i;
}
/* returns TICKET_MODE_AUTO if failed to parse the ticket mode. */
static ticket_mode_e retrieve_ticket_mode(const char *input)
{
if (strcasecmp(input, "manual") == 0) {
return TICKET_MODE_MANUAL;
}
return TICKET_MODE_AUTO;
}
/* scan val for time; time is [0-9]+(ms)?, i.e. either in seconds
* or milliseconds
* returns -1 on failure, otherwise time in ms
*/
static long read_time(char *val)
{
long t;
char *ep;
t = strtol(val, &ep, 10);
if (ep == val) { /* matched none */
t = -1L;
} else if (*ep == '\0') { /* matched all */
t = t*1000L; /* in seconds, convert to ms */
} else if (strcmp(ep, "ms")) { /* ms not exactly matched */
t = -1L;
} /* otherwise, time in ms */
/* if second fractions configured, send finer resolution
* times (i.e. term_valid_for) */
if (t % 1000L) {
TIME_MULT = 1000;
}
return t;
}
/* make arguments for execv(2)
* tk_test.path points to the path
* tk_test.argv is argument vector (starts with the prog)
* (strtok pokes holes in the configuration parameter value, i.e.
* we don't need to allocate memory for arguments)
*/
static int parse_extprog(char *val, struct ticket_config *tk)
{
char *p;
int i = 0;
if (tk_test.path) {
free(tk_test.path);
}
if (!(tk_test.path = strdup(val))) {
log_error("out of memory");
return -1;
}
p = strtok(tk_test.path, " \t");
tk_test.argv[i++] = p;
do {
p = strtok(NULL, " \t");
if (i >= MAX_ARGS) {
log_error("too many arguments for the acquire-handler");
free(tk_test.path);
return -1;
}
tk_test.argv[i++] = p;
} while (p);
return 0;
}
struct toktab grant_type[] = {
{ "auto", GRANT_AUTO},
{ "manual", GRANT_MANUAL},
{ NULL, 0},
};
struct toktab attr_op[] = {
{"eq", ATTR_OP_EQ},
{"ne", ATTR_OP_NE},
{NULL, 0},
};
static int lookup_tokval(char *key, struct toktab *tab)
{
struct toktab *tp;
for (tp = tab; tp->str; tp++) {
if (!strcmp(tp->str, key))
return tp->val;
}
return 0;
}
/* attribute prerequisite
*/
static int parse_attr_prereq(char *val, struct ticket_config *tk)
{
struct attr_prereq *ap = NULL;
char *p;
ap = (struct attr_prereq *)calloc(1, sizeof(struct attr_prereq));
if (!ap) {
log_error("out of memory");
return -1;
}
p = strtok(val, " \t");
if (!p) {
log_error("not enough arguments to attr-prereq");
goto err_out;
}
ap->grant_type = lookup_tokval(p, grant_type);
if (!ap->grant_type) {
log_error("%s is not a grant type", p);
goto err_out;
}
p = strtok(NULL, " \t");
if (!p) {
log_error("not enough arguments to attr-prereq");
goto err_out;
}
if (!(ap->attr_name = strdup(p))) {
log_error("out of memory");
goto err_out;
}
p = strtok(NULL, " \t");
if (!p) {
log_error("not enough arguments to attr-prereq");
goto err_out;
}
ap->op = lookup_tokval(p, attr_op);
if (!ap->op) {
log_error("%s is not an attribute operation", p);
goto err_out;
}
p = strtok(NULL, " \t");
if (!p) {
log_error("not enough arguments to attr-prereq");
goto err_out;
}
if (!(ap->attr_val = strdup(p))) {
log_error("out of memory");
goto err_out;
}
tk->attr_prereqs = g_list_append(tk->attr_prereqs, ap);
if (!tk->attr_prereqs) {
log_error("out of memory");
goto err_out;
}
return 0;
err_out:
if (ap) {
if (ap->attr_val)
free(ap->attr_val);
if (ap->attr_name)
free(ap->attr_name);
free(ap);
}
return -1;
}
extern int poll_timeout;
int read_config(struct booth_config **conf_pptr,
const booth_transport_table_t *transport,
const struct ticket_handler *ticket_handler,
const char *path, int type)
{
char line[1024];
FILE *fp;
char *s, *key, *val, *end_of_key;
const char *error;
char *cp, *cp2;
int i;
int lineno = 0;
int got_transport = 0;
int min_timeout = 0;
struct ticket_config defaults = { { 0 } };
struct ticket_config *current_tk = NULL;
assert(conf_pptr != NULL);
free(*conf_pptr);
fp = fopen(path, "r");
if (!fp) {
log_error("failed to open %s: %s", path, strerror(errno));
*conf_pptr = NULL;
return -1;
}
*conf_pptr = malloc(sizeof(struct booth_config)
+ TICKET_ALLOC * sizeof(struct ticket_config));
if (*conf_pptr == NULL) {
fclose(fp);
log_error("failed to alloc memory for booth config");
return -ENOMEM;
}
memset(*conf_pptr, 0, sizeof(struct booth_config)
+ TICKET_ALLOC * sizeof(struct ticket_config));
ticket_size = TICKET_ALLOC;
(*conf_pptr)->transport = transport;
(*conf_pptr)->ticket_handler = ticket_handler;
(*conf_pptr)->proto = UDP;
(*conf_pptr)->port = BOOTH_DEFAULT_PORT;
(*conf_pptr)->maxtimeskew = BOOTH_DEFAULT_MAX_TIME_SKEW;
(*conf_pptr)->authkey[0] = '\0';
/* Provide safe defaults. -1 is reserved, though. */
(*conf_pptr)->uid = -2;
(*conf_pptr)->gid = -2;
strcpy((*conf_pptr)->site_user, "hacluster");
strcpy((*conf_pptr)->site_group, "haclient");
strcpy((*conf_pptr)->arb_user, "nobody");
strcpy((*conf_pptr)->arb_group, "nobody");
parse_weights("", defaults.weight);
defaults.clu_test.path = NULL;
defaults.clu_test.pid = 0;
defaults.clu_test.status = 0;
defaults.clu_test.progstate = EXTPROG_IDLE;
defaults.term_duration = DEFAULT_TICKET_EXPIRY;
defaults.timeout = DEFAULT_TICKET_TIMEOUT;
defaults.retries = DEFAULT_RETRIES;
defaults.acquire_after = 0;
defaults.mode = TICKET_MODE_AUTO;
error = "";
log_debug("reading config file %s", path);
while (fgets(line, sizeof(line), fp)) {
lineno++;
s = skip_while(line, isspace);
if (is_end_of_line(s) || *s == '#')
continue;
key = s;
/* Key */
end_of_key = skip_while_in(key, isalnum, "-_");
if (end_of_key == key) {
error = "No key";
goto err;
}
if (!*end_of_key)
goto exp_equal;
/* whitespace, and something else but nothing more? */
s = skip_while(end_of_key, isspace);
if (*s != '=') {
exp_equal:
error = "Expected '=' after key";
goto err;
}
s++;
/* It's my buffer, and I terminate if I want to. */
/* But not earlier than that, because we had to check for = */
*end_of_key = 0;
/* Value tokenizing */
s = skip_while(s, isspace);
switch (*s) {
case '"':
case '\'':
val = s+1;
s = skip_until(val, *s);
/* Terminate value */
if (!*s) {
error = "Unterminated quoted string";
goto err;
}
/* Remove and skip quote */
*s = 0;
s++;
if (*(s = skip_while(s, isspace)) && *s != '#') {
error = "Surplus data after value";
goto err;
}
*s = 0;
break;
case 0:
no_value:
error = "No value";
goto err;
break;
default:
val = s;
/* Rest of line. */
i = strlen(s);
/* i > 0 because of "case 0" above. */
while (i > 0 && isspace(s[i-1]))
i--;
s += i;
*s = 0;
}
if (val == s)
goto no_value;
if (strlen(key) > BOOTH_NAME_LEN
|| strlen(val) > BOOTH_NAME_LEN) {
error = "key/value too long";
goto err;
}
if (strcmp(key, "transport") == 0) {
if (got_transport) {
error = "config file has multiple transport lines";
goto err;
}
if (strcasecmp(val, "UDP") == 0)
(*conf_pptr)->proto = UDP;
else if (strcasecmp(val, "SCTP") == 0)
(*conf_pptr)->proto = SCTP;
else {
error = "invalid transport protocol";
goto err;
}
got_transport = 1;
continue;
}
if (strcmp(key, "port") == 0) {
(*conf_pptr)->port = atoi(val);
continue;
}
if (strcmp(key, "name") == 0) {
safe_copy((*conf_pptr)->name,
val, BOOTH_NAME_LEN,
"name");
continue;
}
#if HAVE_LIBGCRYPT || HAVE_LIBMHASH
if (strcmp(key, "authfile") == 0) {
safe_copy((*conf_pptr)->authfile,
val, BOOTH_PATH_LEN,
"authfile");
continue;
}
if (strcmp(key, "maxtimeskew") == 0) {
(*conf_pptr)->maxtimeskew = atoi(val);
continue;
}
#endif
if (strcmp(key, "site") == 0) {
if (add_site(*conf_pptr, val, SITE))
goto err;
continue;
}
if (strcmp(key, "arbitrator") == 0) {
if (add_site(*conf_pptr, val, ARBITRATOR))
goto err;
continue;
}
if (strcmp(key, "site-user") == 0) {
safe_copy((*conf_pptr)->site_user, optarg, BOOTH_NAME_LEN,
"site-user");
continue;
}
if (strcmp(key, "site-group") == 0) {
safe_copy((*conf_pptr)->site_group, optarg, BOOTH_NAME_LEN,
"site-group");
continue;
}
if (strcmp(key, "arbitrator-user") == 0) {
safe_copy((*conf_pptr)->arb_user, optarg, BOOTH_NAME_LEN,
"arbitrator-user");
continue;
}
if (strcmp(key, "arbitrator-group") == 0) {
safe_copy((*conf_pptr)->arb_group, optarg, BOOTH_NAME_LEN,
"arbitrator-group");
continue;
}
if (strcmp(key, "debug") == 0) {
if (type != CLIENT && type != GEOSTORE)
debug_level = max(debug_level, atoi(val));
continue;
}
if (strcmp(key, "ticket") == 0) {
if (current_tk && strcmp(current_tk->name, "__defaults__")) {
if (!postproc_ticket(current_tk)) {
goto err;
}
}
if (!strcmp(val, "__defaults__")) {
current_tk = &defaults;
} else if (add_ticket(*conf_pptr, val, &current_tk,
&defaults)) {
goto err;
}
continue;
}
/* current_tk must be allocated at this point, otherwise
* we don't know to which ticket the key refers
*/
if (!current_tk) {
error = "Unexpected keyword";
goto err;
}
if (strcmp(key, "expire") == 0) {
current_tk->term_duration = read_time(val);
if (current_tk->term_duration <= 0) {
error = "Expected time >0 for expire";
goto err;
}
continue;
}
if (strcmp(key, "timeout") == 0) {
current_tk->timeout = read_time(val);
if (current_tk->timeout <= 0) {
error = "Expected time >0 for timeout";
goto err;
}
if (!min_timeout) {
min_timeout = current_tk->timeout;
} else {
min_timeout = min(min_timeout, current_tk->timeout);
}
continue;
}
if (strcmp(key, "retries") == 0) {
current_tk->retries = strtol(val, &s, 0);
if (*s || s == val ||
current_tk->retries<3 || current_tk->retries > 100) {
error = "Expected plain integer value in the range [3, 100] for retries";
goto err;
}
continue;
}
if (strcmp(key, "renewal-freq") == 0) {
current_tk->renewal_freq = read_time(val);
if (current_tk->renewal_freq <= 0) {
error = "Expected time >0 for renewal-freq";
goto err;
}
continue;
}
if (strcmp(key, "acquire-after") == 0) {
current_tk->acquire_after = read_time(val);
if (current_tk->acquire_after < 0) {
error = "Expected time >=0 for acquire-after";
goto err;
}
continue;
}
if (strcmp(key, "before-acquire-handler") == 0) {
if (parse_extprog(val, current_tk)) {
goto err;
}
continue;
}
if (strcmp(key, "attr-prereq") == 0) {
if (parse_attr_prereq(val, current_tk)) {
goto err;
}
continue;
}
if (strcmp(key, "mode") == 0) {
current_tk->mode = retrieve_ticket_mode(val);
continue;
}
if (strcmp(key, "weights") == 0) {
if (parse_weights(val, current_tk->weight) < 0)
goto err;
continue;
}
error = "Unknown keyword";
goto err;
}
fclose(fp);
if (((*conf_pptr)->site_count % 2) == 0) {
log_warn("Odd number of nodes is strongly recommended!");
}
/* Default: make config name match config filename. */
if (!(*conf_pptr)->name[0]) {
cp = strrchr(path, '/');
cp = cp ? cp+1 : (char *)path;
cp2 = strrchr(cp, '.');
if (!cp2)
cp2 = cp + strlen(cp);
if (cp2-cp >= BOOTH_NAME_LEN) {
log_error("booth config file name too long");
goto out;
}
strncpy((*conf_pptr)->name, cp, cp2-cp);
*((*conf_pptr)->name+(cp2-cp)) = '\0';
}
if (!postproc_ticket(current_tk)) {
goto out;
}
+ safe_copy((*conf_pptr)->path_to_self, path,
+ sizeof((*conf_pptr)->path_to_self),
+ "path to config file itself");
+
poll_timeout = min(POLL_TIMEOUT, min_timeout/10);
if (!poll_timeout)
poll_timeout = POLL_TIMEOUT;
return 0;
err:
fclose(fp);
out:
log_error("%s in config file line %d",
error, lineno);
free(*conf_pptr);
*conf_pptr = NULL;
return -1;
}
int check_config(struct booth_config *conf_ptr, int type)
{
struct passwd *pw;
struct group *gr;
char *cp, *input;
if (conf_ptr == NULL)
return -1;
input = (type == ARBITRATOR)
? conf_ptr->arb_user
: conf_ptr->site_user;
if (!*input)
goto u_inval;
if (isdigit(input[0])) {
conf_ptr->uid = strtol(input, &cp, 0);
if (*cp != 0) {
u_inval:
log_error("User \"%s\" cannot be resolved into a UID.", input);
return ENOENT;
}
} else {
pw = getpwnam(input);
if (!pw)
goto u_inval;
conf_ptr->uid = pw->pw_uid;
}
input = (type == ARBITRATOR)
? conf_ptr->arb_group
: conf_ptr->site_group;
if (!*input)
goto g_inval;
if (isdigit(input[0])) {
conf_ptr->gid = strtol(input, &cp, 0);
if (*cp != 0) {
g_inval:
log_error("Group \"%s\" cannot be resolved into a UID.", input);
return ENOENT;
}
} else {
gr = getgrnam(input);
if (!gr)
goto g_inval;
conf_ptr->gid = gr->gr_gid;
}
return 0;
}
static int get_other_site(struct booth_config *conf_ptr,
struct booth_site **node)
{
struct booth_site *n;
int i;
*node = NULL;
if (conf_ptr == NULL)
return 0;
FOREACH_NODE(conf_ptr, i, n) {
if (n != local && n->type == SITE) {
if (!*node) {
*node = n;
} else {
return 0;
}
}
}
return !*node ? 0 : 1;
}
int find_site_by_name(struct booth_config *conf_ptr, const char *site,
struct booth_site **node, int any_type)
{
struct booth_site *n;
int i;
if (conf_ptr == NULL)
return 0;
if (!strcmp(site, OTHER_SITE))
return get_other_site(conf_ptr, node);
FOREACH_NODE(conf_ptr, i, n) {
if ((n->type == SITE || any_type) &&
strncmp(n->addr_string, site, sizeof(n->addr_string)) == 0) {
*node = n;
return 1;
}
}
return 0;
}
int find_site_by_id(struct booth_config *conf_ptr, uint32_t site_id,
struct booth_site **node)
{
struct booth_site *n;
int i;
if (site_id == NO_ONE) {
*node = no_leader;
return 1;
}
if (conf_ptr == NULL)
return 0;
FOREACH_NODE(conf_ptr, i, n) {
if (n->site_id == site_id) {
*node = n;
return 1;
}
}
return 0;
}
const char *type_to_string(int type)
{
switch (type)
{
case ARBITRATOR: return "arbitrator";
case SITE: return "site";
case CLIENT: return "client";
case GEOSTORE: return "attr";
}
return "??invalid-type??";
}
diff --git a/src/config.h b/src/config.h
index 37bd95d..243bbe0 100644
--- a/src/config.h
+++ b/src/config.h
@@ -1,393 +1,395 @@
/*
* Copyright (C) 2011 Jiaju Zhang <jjzhang@suse.de>
* Copyright (C) 2013-2014 Philipp Marek <philipp.marek@linbit.com>
*
* 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.
*
* This software 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, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef _CONFIG_H
#define _CONFIG_H
#include <stdint.h>
#include <sys/stat.h>
struct booth_config;
#include "booth.h"
#include "timer.h"
#include "raft.h"
#include "transport.h"
/** @{ */
/** Definitions for in-RAM data. */
#define MAX_NODES 16
#define MAX_ARGS 16
#define TICKET_ALLOC 16
#define OTHER_SITE "other"
typedef enum {
EXTPROG_IDLE,
EXTPROG_RUNNING,
EXTPROG_EXITED,
EXTPROG_IGNORE,
} extprog_state_e;
#define tk_test tk->clu_test
typedef enum {
ATTR_OP_EQ = 1,
ATTR_OP_NE,
} attr_op_e;
typedef enum {
GRANT_AUTO = 1,
GRANT_MANUAL,
} grant_type_e;
typedef enum {
TICKET_MODE_AUTO = 1,
TICKET_MODE_MANUAL,
} ticket_mode_e;
struct toktab {
const char *str;
int val;
};
struct attr_prereq {
grant_type_e grant_type; /* grant type */
attr_op_e op; /* attribute operation */
char *attr_name;
char *attr_val;
};
struct ticket_config {
/** \name Configuration items.
* @{ */
/** Name of ticket. */
boothc_ticket name;
/** How long a term lasts if not refreshed (in ms) */
int term_duration;
/** Network related timeouts (in ms) */
int timeout;
/** Retries before giving up. */
int retries;
/** If >0, time to wait for a site to get fenced.
* The ticket may be acquired after that timespan by
* another site. */
int acquire_after;
/* How often to renew the ticket (in ms)
*/
int renewal_freq;
/* Program to ask whether it makes sense to
* acquire the ticket */
struct clu_test {
char *path;
int is_dir;
char *argv[MAX_ARGS];
pid_t pid;
int status; /* child exit status */
extprog_state_e progstate; /* program running/idle/waited on */
} clu_test;
/** Node weights. */
int weight[MAX_NODES];
/* Mode operation of the ticket.
* Set to MANUAL to make sure that the ticket will be manipulated
* only by manual commands of the administrator. In such a case
* automatic elections will be disabled.
* Manual tickets do not have to be renewed every some time.
* The leader will continue to send heartbeat messages to other sites.
*/
ticket_mode_e mode;
/** @} */
/** \name Runtime values.
* @{ */
/** Current state. */
server_state_e state;
/** Next state. Used at startup. */
server_state_e next_state;
/** When something has to be done */
timetype next_cron;
/** Current leader. This is effectively the log[] in Raft. */
struct booth_site *leader;
/** Leader that got lost. */
struct booth_site *lost_leader;
/** Is the ticket granted? */
int is_granted;
/** Which site considered itself a leader.
* For manual tickets it is possible, that
* more than one site will act as a leader.
* This array is used for tracking that situation
* and notifying the user about the issue.
*
* Possible values for every site:
* 0: the site does not claim to be the leader
* 1: the site considers itself a leader and
* is sending or used to send heartbeat messages
*
* The site will be marked as '1' until this site
* receives revoke confirmation.
*
* If more than one site has '1', the geo cluster is
* considered to have multiple leadership and proper
* warning are generated.
*/
int sites_where_granted[MAX_NODES];
/** Timestamp of leadership expiration */
timetype term_expires;
/** End of election period */
timetype election_end;
struct booth_site *voted_for;
/** Who the various sites vote for.
* NO_OWNER = no vote yet. */
struct booth_site *votes_for[MAX_NODES];
/* bitmap */
uint64_t votes_received;
/** Last voting round that was seen. */
uint32_t current_term;
/** Do ticket updates whenever we get enough heartbeats.
* But do that only once.
* This is reset to 0 whenever we broadcast heartbeat and set
* to 1 once enough acks are received.
* Increased to 2 when the ticket is commited to the CIB (see
* delay_commit).
*/
uint32_t ticket_updated;
/** Outcome of whatever ticket request was processed.
* Can also be an intermediate stage.
*/
uint32_t outcome;
/** @} */
/** */
uint32_t last_applied;
uint32_t next_index[MAX_NODES];
uint32_t match_index[MAX_NODES];
/* Why did we start the elections?
*/
cmd_reason_t election_reason;
/* if it is potentially dangerous to grant the ticket
* immediately, then this is set to some point in time,
* usually (now + term_duration + acquire_after)
*/
timetype delay_commit;
/* the last request RPC we sent
*/
uint32_t last_request;
/* if we expect some acks, then set this to the id of
* the RPC which others will send us; it is cleared once all
* replies were received
*/
uint32_t acks_expected;
/* bitmask of servers which sent acks
*/
uint64_t acks_received;
/* timestamp of the request */
timetype req_sent_at;
/* we need to wait for MY_INDEX from other servers,
* hold the ticket processing for a while until they reply
*/
int start_postpone;
/** Last renewal time */
timetype last_renewal;
/* Do we need to update the copy in the CIB?
* Normally, the ticket is written only when it changes via
* the UPDATE RPC (for followers) and on expiration update
* (for leaders)
*/
int update_cib;
/* Is this ticket in election?
*/
int in_election;
/* don't log warnings unnecessarily
*/
int expect_more_rejects;
/** \name Needed while proposals are being done.
* @{ */
/* Need to keep the previous valid ticket in case we moved to
* start new elections and another server asks for the ticket
* status. It would be wrong to send our candidate ticket.
*/
struct ticket_config *last_valid_tk;
/** Attributes, user defined
*/
GHashTable *attr;
/** Attribute prerequisites
*/
GList *attr_prereqs;
/** Whom to vote for the next time.
* Needed to push a ticket to someone else. */
#if 0
/** Bitmap of sites that acknowledge that state. */
uint64_t proposal_acknowledges;
/** When an incompletely acknowledged proposal gets done.
* If all peers agree, that happens sooner.
* See switch_state_to(). */
struct timeval proposal_switch;
/** Timestamp of proposal expiration. */
time_t proposal_expires;
#endif
/** Number of send retries left.
* Used on the new owner.
* Starts at 0, counts up. */
int retry_number;
/** @} */
};
struct booth_config {
char name[BOOTH_NAME_LEN];
/** File containing the authentication file. */
char authfile[BOOTH_PATH_LEN];
struct stat authstat;
char authkey[BOOTH_MAX_KEY_LEN];
int authkey_len;
/** Maximum time skew between peers allowed */
int maxtimeskew;
transport_layer_t proto;
uint16_t port;
/** Stores the OR of sites bitmasks. */
uint64_t sites_bits;
/** Stores the OR of all members' bitmasks. */
uint64_t all_bits;
char site_user[BOOTH_NAME_LEN];
char site_group[BOOTH_NAME_LEN];
char arb_user[BOOTH_NAME_LEN];
char arb_group[BOOTH_NAME_LEN];
uid_t uid;
gid_t gid;
int site_count;
struct booth_site site[MAX_NODES];
int ticket_count;
int ticket_allocated;
struct ticket_config *ticket;
+ char path_to_self[BOOTH_PATH_LEN];
+
const booth_transport_table_t *transport;
const struct ticket_handler *ticket_handler;
};
#define is_auth_req(b_) ((b_)->authkey[0] != '\0')
/**
* @internal
* Parse booth configuration file and store as structured data
*
* @param[inout] conf_pptr config object to free-alloc cycle & fill accordingly
* @param[in] transport transport handlers table
* @param[in] path where the configuration file is expected
* @param[in] type role currently being acted as
*
* @return 0 or negative value (-1 or -errno) on error
*/
int read_config(struct booth_config **conf_pptr,
const booth_transport_table_t *transport,
const struct ticket_handler *ticket_handler,
const char *path, int type);
/**
* @internal
* Check booth configuration
*
* Currently it means checking that login user/group indeed exists,
* while converting it to respective numeric values for further use.
*
* @param[inout] conf_ptr config object to check
* @param[in] type role currently being acted as
*
* @return 0 or negative value (-1 or -errno) on error
*/
int check_config(struct booth_config *conf_ptr, int type);
/**
* @internal
* Find site in booth configuration by resolved host name
*
* @param[inout] conf_ptr config object to refer to
* @param[in] site name to match against previously resolved host names
* @param[out] node relevant tracked data when found
* @param[in] any_type whether or not to consider also non-site members
*
* @return 0 if nothing found, or 1 when found (node assigned accordingly)
*/
int find_site_by_name(struct booth_config *conf_ptr, const char *site,
struct booth_site **node, int any_type);
/**
* @internal
* Find site in booth configuration by a hash (id)
*
* @param[inout] conf_ptr config object to refer to
* @param[in] site_id hash (id) to match against previously resolved ones
* @param[out] node relevant tracked data when found
*
* @return 0 if nothing found, or 1 when found (node assigned accordingly)
*/
int find_site_by_id(struct booth_config *conf_ptr, uint32_t site_id,
struct booth_site **node);
const char *type_to_string(int type);
#endif /* _CONFIG_H */
diff --git a/src/handler.c b/src/handler.c
index 2b5d955..634275e 100644
--- a/src/handler.c
+++ b/src/handler.c
@@ -1,280 +1,280 @@
/*
* Copyright (C) 2014 Philipp Marek <philipp.marek@linbit.com>
*
* 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.
*
* This software 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, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>
#include <inttypes.h>
#include <dirent.h>
#include <stdio.h>
#include <assert.h>
#include <time.h>
#include "ticket.h"
#include "config.h"
#include "inline-fn.h"
#include "log.h"
#include "pacemaker.h"
#include "booth.h"
#include "handler.h"
static int set_booth_env(struct booth_config *conf_ptr,
struct ticket_config *tk)
{
int rv;
char expires[16];
assert(conf_ptr != NULL);
sprintf(expires, "%" PRId64, (int64_t)wall_ts(&tk->term_expires));
rv = setenv("BOOTH_TICKET", tk->name, 1) ||
setenv("BOOTH_LOCAL", local->addr_string, 1) ||
setenv("BOOTH_CONF_NAME", conf_ptr->name, 1) ||
- setenv("BOOTH_CONF_PATH", cl.configfile, 1) ||
+ setenv("BOOTH_CONF_PATH", conf_ptr->path_to_self, 1) ||
setenv("BOOTH_TICKET_EXPIRES", expires, 1);
if (rv) {
log_error("Cannot set environment: %s", strerror(errno));
}
return rv;
}
static void
closefiles(void)
{
int fd;
/* close all descriptors except stdin/out/err */
for (fd = getdtablesize() - 1; fd > STDERR_FILENO; fd--) {
close(fd);
}
}
static void
run_ext_prog(struct booth_config *conf_ptr, struct ticket_config *tk,
char *prog)
{
if (set_booth_env(conf_ptr, tk)) {
_exit(1);
}
closefiles(); /* don't leak open files */
tk_log_debug("running handler %s", prog);
execv(prog, tk_test.argv);
tk_log_error("%s: execv failed (%s)", prog, strerror(errno));
_exit(1);
}
static int
prog_filter(const struct dirent *dp)
{
return (*dp->d_name != '.');
}
static pid_t curr_pid;
static int ignore_status;
static int
test_exit_status(struct ticket_config *tk, char *prog, int status, int log_msg)
{
int rv = -1;
if (WIFEXITED(status)) {
rv = WEXITSTATUS(status);
} else if (WIFSIGNALED(status)) {
rv = 128 + WTERMSIG(status);
}
if (rv) {
if (log_msg) {
tk_log_warn("handler \"%s\" failed: %s",
prog, interpret_rv(status));
tk_log_warn("we are not allowed to acquire ticket");
}
} else {
tk_log_debug("handler \"%s\" exited with success",
prog);
}
return rv;
}
static void
reset_test_state(struct ticket_config *tk)
{
tk_test.pid = 0;
set_progstate(tk, EXTPROG_IDLE);
}
int tk_test_exit_status(struct ticket_config *tk)
{
int rv;
rv = test_exit_status(tk, tk_test.path, tk_test.status, !tk_test.is_dir);
reset_test_state(tk);
return rv;
}
void wait_child(struct booth_config *conf_ptr)
{
int i, status;
struct ticket_config *tk;
/* use waitpid(2) and not wait(2) in order not to interfere
* with popen(2)/pclose(2) and system(2) used in pacemaker.c
*/
FOREACH_TICKET(conf_ptr, i, tk) {
if (tk_test.path && tk_test.pid > 0 &&
(tk_test.progstate == EXTPROG_RUNNING ||
tk_test.progstate == EXTPROG_IGNORE) &&
waitpid(tk_test.pid, &status, WNOHANG) == tk_test.pid) {
if (tk_test.progstate == EXTPROG_IGNORE) {
/* not interested in the outcome */
reset_test_state(tk);
} else {
tk_test.status = status;
set_progstate(tk, EXTPROG_EXITED);
}
}
}
}
/* the parent may want to have us stop processing scripts, say
* when the ticket gets revoked
*/
static void ignore_rest(int sig)
{
signal(SIGTERM, SIG_IGN);
log_info("external programs handler caught TERM, ignoring status of external test programs");
ignore_status = 1;
if (curr_pid > 0) {
(void)kill(curr_pid, SIGTERM);
}
}
void ext_prog_timeout(struct ticket_config *tk)
{
tk_log_warn("handler timed out");
}
int is_ext_prog_running(struct ticket_config *tk)
{
if (!tk_test.path)
return 0;
return (tk_test.pid > 0 && tk_test.progstate == EXTPROG_RUNNING);
}
void ignore_ext_test(struct ticket_config *tk)
{
if (is_ext_prog_running(tk)) {
(void)kill(tk_test.pid, SIGTERM);
set_progstate(tk, EXTPROG_IGNORE);
} else if (tk_test.progstate == EXTPROG_EXITED) {
/* external prog exited, but the status not yet examined;
* we're not interested in checking the status anymore */
reset_test_state(tk);
}
}
static void
process_ext_dir(struct booth_config *conf_ptr, struct ticket_config *tk)
{
char prog[FILENAME_MAX+1];
int rv, n_progs, i, status;
struct dirent **proglist, *dp;
signal(SIGTERM, (__sighandler_t)ignore_rest);
signal(SIGCHLD, SIG_DFL);
signal(SIGUSR1, SIG_DFL);
signal(SIGINT, SIG_DFL);
tk_log_debug("running programs in directory %s", tk_test.path);
n_progs = scandir(tk_test.path, &proglist, prog_filter, alphasort);
if (n_progs == -1) {
tk_log_error("%s: scandir failed (%s)", tk_test.path, strerror(errno));
_exit(1);
}
for (i = 0; i < n_progs; i++) {
if (ignore_status)
break;
dp = proglist[i];
if (strlen(dp->d_name) + strlen(tk_test.path) + 1 > FILENAME_MAX) {
tk_log_error("%s: name exceeds max length (%s)",
tk_test.path, dp->d_name);
_exit(1);
}
strcpy(prog, tk_test.path);
strcat(prog, "/");
strcat(prog, dp->d_name);
switch(curr_pid=fork()) {
case -1:
log_error("fork: %s", strerror(errno));
_exit(1);
case 0: /* child */
run_ext_prog(conf_ptr, tk, prog);
break; /* run_ext_prog effectively noreturn */
default: /* parent */
while (waitpid(curr_pid, &status, 0) != curr_pid)
;
curr_pid = 0;
if (!ignore_status) {
rv = test_exit_status(tk, prog, status, 1);
if (rv)
_exit(rv);
}
}
}
_exit(0);
}
/* run some external program
* return codes:
* RUNCMD_ERR: executing program failed (or some other failure)
* RUNCMD_MORE: program forked, results later
*/
int run_handler(struct booth_config *conf_ptr, struct ticket_config *tk)
{
int rv = 0;
pid_t pid;
struct stat stbuf;
if (!tk_test.path)
return 0;
if (stat(tk_test.path, &stbuf)) {
tk_log_error("%s: stat failed (%s)", tk_test.path, strerror(errno));
return RUNCMD_ERR;
}
tk_test.is_dir = (stbuf.st_mode & S_IFDIR);
switch(pid=fork()) {
case -1:
log_error("fork: %s", strerror(errno));
return RUNCMD_ERR;
case 0: /* child */
if (tk_test.is_dir) {
process_ext_dir(conf_ptr, tk);
} else {
run_ext_prog(conf_ptr, tk, tk_test.path);
}
default: /* parent */
tk_test.pid = pid;
set_progstate(tk, EXTPROG_RUNNING);
rv = RUNCMD_MORE; /* program runs */
}
return rv;
}
diff --git a/src/main.c b/src/main.c
index 417eaea..ea75937 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,1661 +1,1666 @@
/*
* Copyright (C) 2011 Jiaju Zhang <jjzhang@suse.de>
* Copyright (C) 2013-2014 Philipp Marek <philipp.marek@linbit.com>
*
* 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.
*
* This software 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, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sched.h>
#include <errno.h>
#include <limits.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/poll.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <signal.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <pacemaker/crm/services.h>
#include "b_config.h"
#if HAVE_LIBGCRYPT
#include <gcrypt.h>
#endif
#ifndef NAMETAG_LIBSYSTEMD
#include <clplumbing/setproctitle.h>
#else
#include "alt/nametag_libsystemd.h"
#endif
#ifdef COREDUMP_NURSING
#include <sys/prctl.h>
#include <clplumbing/coredumps.h>
#endif
#include "log.h"
#include "booth.h"
#include "config.h"
#include "transport.h"
#include "inline-fn.h"
#include "pacemaker.h"
#include "ticket.h"
#include "request.h"
#include "attr.h"
#include "handler.h"
#define RELEASE_VERSION "1.0"
#define RELEASE_STR RELEASE_VERSION " (build " BOOTH_BUILD_VERSION ")"
#define CLIENT_NALLOC 32
extern const booth_transport_table_t booth__transport;
extern struct ticket_handler booth__pcmk_ticket_handler;
static int daemonize = 1;
int enable_stderr = 0;
timetype start_time;
static struct booth_config *booth_conf;
+static struct command_line cmd_line;
/** Structure for "clients".
* Filehandles with incoming data get registered here (and in pollfds),
* along with their callbacks.
* Because these can be reallocated with every new fd, addressing
* happens _only_ by their numeric index. */
struct client *clients = NULL;
struct pollfd *pollfds = NULL;
static int client_maxi;
static int client_size = 0;
static const struct booth_site _no_leader = {
.addr_string = "none",
.site_id = NO_ONE,
.index = -1,
};
struct booth_site *const no_leader = (struct booth_site*) &_no_leader;
typedef enum
{
BOOTHD_STARTED=0,
BOOTHD_STARTING
} BOOTH_DAEMON_STATE;
int poll_timeout;
-struct command_line cl;
static void client_alloc(void)
{
int i;
if (!(clients = realloc(
clients, (client_size + CLIENT_NALLOC) * sizeof(*clients))
) || !(pollfds = realloc(
pollfds, (client_size + CLIENT_NALLOC) * sizeof(*pollfds))
)) {
log_error("can't alloc for client array");
exit(1);
}
for (i = client_size; i < client_size + CLIENT_NALLOC; i++) {
clients[i].workfn = NULL;
clients[i].deadfn = NULL;
clients[i].fd = -1;
pollfds[i].fd = -1;
pollfds[i].revents = 0;
}
client_size += CLIENT_NALLOC;
}
static void client_dead(int ci)
{
struct client *c = clients + ci;
if (c->fd != -1) {
log_debug("removing client %d", c->fd);
close(c->fd);
}
c->fd = -1;
c->workfn = NULL;
if (c->msg) {
free(c->msg);
c->msg = NULL;
c->offset = 0;
}
pollfds[ci].fd = -1;
}
int client_add(int fd, const struct booth_transport *tpt,
void (*workfn)(struct booth_config *conf_ptr, int ci),
void (*deadfn)(int ci))
{
int i;
struct client *c;
if (client_size - 1 <= client_maxi ) {
client_alloc();
}
for (i = 0; i < client_size; i++) {
c = clients + i;
if (c->fd != -1)
continue;
c->workfn = workfn;
if (deadfn)
c->deadfn = deadfn;
else
c->deadfn = client_dead;
c->transport = tpt;
c->fd = fd;
c->msg = NULL;
c->offset = 0;
pollfds[i].fd = fd;
pollfds[i].events = POLLIN;
if (i > client_maxi)
client_maxi = i;
return i;
}
assert(!"no client");
}
int find_client_by_fd(int fd)
{
int i;
if (fd < 0)
return -1;
for (i = 0; i <= client_maxi; i++) {
if (clients[i].fd == fd)
return i;
}
return -1;
}
static int format_peers(struct booth_config *conf_ptr,
char **pdata, unsigned int *len)
{
struct booth_site *s;
char *data, *cp;
char time_str[64];
int i, alloc;
assert(conf_ptr != NULL);
*pdata = NULL;
*len = 0;
alloc = conf_ptr->site_count * (BOOTH_NAME_LEN + 256);
data = malloc(alloc);
if (!data)
return -ENOMEM;
cp = data;
FOREACH_NODE(conf_ptr, i, s) {
if (s == local)
continue;
strftime(time_str, sizeof(time_str), "%F %T",
localtime(&s->last_recv));
cp += snprintf(cp,
alloc - (cp - data),
"%-12s %s, last recv: %s\n",
type_to_string(s->type),
s->addr_string,
time_str);
cp += snprintf(cp,
alloc - (cp - data),
"\tSent pkts:%u error:%u resends:%u\n",
s->sent_cnt,
s->sent_err_cnt,
s->resend_cnt);
cp += snprintf(cp,
alloc - (cp - data),
"\tRecv pkts:%u error:%u authfail:%u invalid:%u\n\n",
s->recv_cnt,
s->recv_err_cnt,
s->sec_cnt,
s->invalid_cnt);
if (alloc - (cp - data) <= 0) {
free(data);
return -ENOMEM;
}
}
*pdata = data;
*len = cp - data;
return 0;
}
void list_peers(struct booth_config *conf_ptr, int fd)
{
char *data;
unsigned int olen;
struct boothc_hdr_msg hdr;
if (format_peers(conf_ptr, &data, &olen) < 0)
goto out;
init_header(conf_ptr, &hdr.header, CL_LIST, 0, 0, RLT_SUCCESS,
0, sizeof(hdr) + olen);
(void) send_header_plus(conf_ptr, fd, &hdr, data, olen);
out:
if (data)
free(data);
}
/* trim trailing spaces if the key is ascii
*/
static void trim_key(struct booth_config *conf_ptr)
{
char *p;
int i;
assert(conf_ptr != NULL);
for (i = 0, p = conf_ptr->authkey; i < conf_ptr->authkey_len; i++, p++)
if (!isascii(*p))
return;
p = conf_ptr->authkey;
while (conf_ptr->authkey_len > 0 && isspace(*p)) {
p++;
conf_ptr->authkey_len--;
}
memmove(conf_ptr->authkey, p, conf_ptr->authkey_len);
p = conf_ptr->authkey + conf_ptr->authkey_len - 1;
while (conf_ptr->authkey_len > 0 && isspace(*p)) {
conf_ptr->authkey_len--;
p--;
}
}
static int read_authkey(struct booth_config *conf_ptr)
{
int fd;
assert(conf_ptr != NULL);
conf_ptr->authkey[0] = '\0';
fd = open(conf_ptr->authfile, O_RDONLY);
if (fd < 0) {
log_error("cannot open %s: %s",
conf_ptr->authfile, strerror(errno));
return -1;
}
if (fstat(fd, &conf_ptr->authstat) < 0) {
log_error("cannot stat authentication file %s (%d): %s",
conf_ptr->authfile, fd, strerror(errno));
close(fd);
return -1;
}
if (conf_ptr->authstat.st_mode & (S_IRGRP | S_IROTH)) {
log_error("%s: file shall not be readable for anyone but the owner",
conf_ptr->authfile);
close(fd);
return -1;
}
conf_ptr->authkey_len = read(fd, conf_ptr->authkey, BOOTH_MAX_KEY_LEN);
close(fd);
trim_key(conf_ptr);
log_debug("read key of size %d in authfile %s",
conf_ptr->authkey_len, conf_ptr->authfile);
/* make sure that the key is of minimum length */
return (conf_ptr->authkey_len >= BOOTH_MIN_KEY_LEN) ? 0 : -1;
}
/* XXX UNUSED */
int update_authkey(struct booth_config *conf_ptr)
{
struct stat statbuf;
assert(conf_ptr != NULL);
if (stat(conf_ptr->authfile, &statbuf) < 0) {
log_error("cannot stat authentication file %s: %s",
conf_ptr->authfile, strerror(errno));
return -1;
}
if (statbuf.st_mtime > conf_ptr->authstat.st_mtime) {
return read_authkey(conf_ptr);
}
return 0;
}
-static int setup_config(struct booth_config **conf_pptr, int type)
+static int setup_config(struct command_line *cl, struct booth_config **conf_pptr)
{
int rv;
assert(conf_pptr != NULL);
rv = read_config(conf_pptr, &booth__transport,
- &booth__pcmk_ticket_handler, cl.configfile, type);
+ &booth__pcmk_ticket_handler, cl->configfile,
+ cl->type);
if (rv < 0)
goto out;
if (is_auth_req(*conf_pptr)) {
rv = read_authkey(*conf_pptr);
if (rv < 0)
goto out;
#if HAVE_LIBGCRYPT
if (!gcry_check_version(NULL)) {
log_error("gcry_check_version");
rv = -ENOENT;
goto out;
}
gcry_control(GCRYCTL_DISABLE_SECMEM, 0);
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
#endif
}
/* Set "local" pointer, ignoring errors. */
- if (cl.type == DAEMON && cl.site[0]) {
- if (!find_site_by_name(*conf_pptr, cl.site, &local, 1)) {
+ if (cl->type == DAEMON && cl->site[0]) {
+ if (!find_site_by_name(*conf_pptr, cl->site, &local, 1)) {
log_error("Cannot find \"%s\" in the configuration.",
- cl.site);
+ cl->site);
return -EINVAL;
}
local->local = 1;
} else
find_myself(*conf_pptr, NULL,
- type == CLIENT || type == GEOSTORE);
+ cl->type == CLIENT || cl->type == GEOSTORE);
- rv = check_config(*conf_pptr, type);
+ rv = check_config(*conf_pptr, cl->type);
if (rv < 0)
goto out;
/* Per default the PID file name is derived from the
* configuration name. */
- if (!cl.lockfile[0]) {
- snprintf(cl.lockfile, sizeof(cl.lockfile) - 1,
+ if (!cl->lockfile[0]) {
+ snprintf(cl->lockfile, sizeof(cl->lockfile)-1,
"%s/%s.pid", BOOTH_RUN_DIR, (*conf_pptr)->name);
}
out:
return rv;
}
static int setup_transport(struct booth_config *conf_ptr)
{
int rv;
assert(conf_ptr != NULL && conf_ptr->transport != NULL);
rv = transport(conf_ptr)->init(conf_ptr, message_recv);
if (rv < 0) {
log_error("failed to init booth_transport %s",
transport(conf_ptr)->name);
goto out;
}
rv = (*conf_ptr->transport)[TCP].init(conf_ptr, NULL);
if (rv < 0) {
log_error("failed to init booth_transport[TCP]");
goto out;
}
out:
return rv;
}
-static int write_daemon_state(struct booth_config *conf_ptr, int fd, int state)
+static int write_daemon_state(struct command_line *cl,
+ struct booth_config *conf_ptr, int fd, int state)
{
char buffer[1024];
int rv, size;
size = sizeof(buffer) - 1;
rv = snprintf(buffer, size,
"booth_pid=%d "
"booth_state=%s "
"booth_type=%s "
"booth_cfg_name='%s' "
"booth_id=%d "
"booth_addr_string='%s' "
"booth_port=%d\n",
getpid(),
( state == BOOTHD_STARTED ? "started" :
state == BOOTHD_STARTING ? "starting" : "invalid"),
type_to_string(local->type), conf_ptr->name,
get_local_id(), site_string(local),
site_port(local));
if (rv < 0 || rv == size) {
log_error("Buffer filled up in write_daemon_state().");
return -1;
}
size = rv;
rv = ftruncate(fd, 0);
if (rv < 0) {
log_error("lockfile %s truncate error %d: %s",
- cl.lockfile, errno, strerror(errno));
+ cl->lockfile, errno, strerror(errno));
return rv;
}
rv = lseek(fd, 0, SEEK_SET);
if (rv < 0) {
log_error("lseek set fd(%d) offset to 0 error, return(%d), message(%s)",
fd, rv, strerror(errno));
rv = -1;
return rv;
}
rv = write(fd, buffer, size);
if (rv != size) {
log_error("write to fd(%d, %d) returned %d, errno %d, message(%s)",
fd, size,
rv, errno, strerror(errno));
return -1;
}
return 0;
}
-static int loop(struct booth_config *conf_ptr, int fd)
+static int loop(struct command_line *cl, struct booth_config *conf_ptr, int fd)
{
void (*workfn) (struct booth_config *conf_ptr, int ci);
void (*deadfn) (int ci);
int rv, i;
rv = setup_transport(conf_ptr);
if (rv < 0)
goto fail;
rv = setup_ticket(conf_ptr);
if (rv < 0)
goto fail;
- rv = write_daemon_state(conf_ptr, fd, BOOTHD_STARTED);
+ rv = write_daemon_state(cl, conf_ptr, fd, BOOTHD_STARTED);
if (rv != 0) {
log_error("write daemon state %d to lockfile error %s: %s",
- BOOTHD_STARTED, cl.lockfile, strerror(errno));
+ BOOTHD_STARTED, cl->lockfile, strerror(errno));
goto fail;
}
log_info("BOOTH %s daemon started, node id is 0x%08X (%d).",
type_to_string(local->type),
local->site_id, local->site_id);
while (1) {
rv = poll(pollfds, client_maxi + 1, poll_timeout);
if (rv == -1 && errno == EINTR)
continue;
if (rv < 0) {
log_error("poll failed: %s (%d)", strerror(errno), errno);
goto fail;
}
for (i = 0; i <= client_maxi; i++) {
if (clients[i].fd < 0)
continue;
if (pollfds[i].revents & POLLIN) {
workfn = clients[i].workfn;
if (workfn)
workfn(conf_ptr, i);
}
if (pollfds[i].revents &
(POLLERR | POLLHUP | POLLNVAL)) {
deadfn = clients[i].deadfn;
if (deadfn)
deadfn(i);
}
}
process_tickets(conf_ptr);
}
return 0;
fail:
return -1;
}
-static int test_reply(cmd_result_t reply_code, cmd_request_t cmd)
+static int test_reply(struct command_line *cl, cmd_result_t reply_code)
{
int rv = 0;
const char *op_str = "";
- if (cmd == CMD_GRANT)
+ if (cl->type == CMD_GRANT)
op_str = "grant";
- else if (cmd == CMD_REVOKE)
+ else if (cl->type == CMD_REVOKE)
op_str = "revoke";
- else if (cmd == CMD_LIST)
+ else if (cl->type == CMD_LIST)
op_str = "list";
- else if (cmd == CMD_PEERS)
+ else if (cl->type == CMD_PEERS)
op_str = "peers";
else {
log_error("internal error reading reply result!");
return -1;
}
switch (reply_code) {
case RLT_OVERGRANT:
log_info("You're granting a granted ticket. "
"If you wanted to migrate a ticket, "
"use revoke first, then use grant.");
rv = -1;
break;
case RLT_TICKET_IDLE:
log_info("ticket is not owned");
rv = 0;
break;
case RLT_ASYNC:
log_info("%s command sent, result will be returned "
"asynchronously. Please use \"booth list\" to "
"see the outcome.", op_str);
rv = 0;
break;
case RLT_CIB_PENDING:
log_info("%s succeeded (CIB commit pending)", op_str);
/* wait for the CIB commit? */
- rv = (cl.options & OPT_WAIT_COMMIT) ? 3 : 0;
+ rv = (cl->options & OPT_WAIT_COMMIT) ? 3 : 0;
break;
case RLT_MORE:
rv = 2;
break;
case RLT_SYNC_SUCC:
case RLT_SUCCESS:
- if (cmd != CMD_LIST && cmd != CMD_PEERS)
+ if (cl->type != CMD_LIST && cl->type != CMD_PEERS)
log_info("%s succeeded!", op_str);
rv = 0;
break;
case RLT_SYNC_FAIL:
log_info("%s failed!", op_str);
rv = -1;
break;
case RLT_INVALID_ARG:
- log_error("ticket \"%s\" does not exist",
- cl.msg.ticket.id);
+ log_error("ticket \"%s\" does not exist", cl->msg.ticket.id);
rv = -1;
break;
case RLT_AUTH:
log_error("authentication error");
rv = -1;
break;
case RLT_EXT_FAILED:
log_error("before-acquire-handler for ticket \"%s\" failed, grant denied",
- cl.msg.ticket.id);
+ cl->msg.ticket.id);
rv = -1;
break;
case RLT_ATTR_PREREQ:
log_error("attr-prereq for ticket \"%s\" failed, grant denied",
- cl.msg.ticket.id);
+ cl->msg.ticket.id);
rv = -1;
break;
case RLT_REDIRECT:
/* talk to another site */
rv = 1;
break;
default:
log_error("got an error code: %x", rv);
rv = -1;
}
return rv;
}
-static int query_get_string_answer(struct booth_config *conf_ptr,
- cmd_request_t cmd)
+static int query_get_string_answer(struct command_line *cl,
+ struct booth_config *conf_ptr)
{
struct booth_site *site;
struct boothc_hdr_msg reply;
struct boothc_header *header;
char *data;
int data_len;
int rv;
struct booth_transport const *tpt;
- int (*test_reply_f) (cmd_result_t reply_code, cmd_request_t cmd);
+ int (*test_reply_f) (struct command_line *, cmd_result_t reply_code);
size_t msg_size;
void *request;
assert(conf_ptr != NULL && conf_ptr->transport != NULL);
- if (cl.type == GEOSTORE) {
+ if (cl->type == GEOSTORE) {
test_reply_f = test_attr_reply;
- msg_size = sizeof(cl.attr_msg);
- request = &cl.attr_msg;
+ msg_size = sizeof(cl->attr_msg);
+ request = &cl->attr_msg;
} else {
test_reply_f = test_reply;
- msg_size = sizeof(cl.msg);
- request = &cl.msg;
+ msg_size = sizeof(cl->msg);
+ request = &cl->msg;
}
header = (struct boothc_header *)request;
data = NULL;
- init_header(conf_ptr, header, cmd, 0, cl.options, 0, 0, msg_size);
+ init_header(conf_ptr, header, cl->op, 0, cl->options, 0, 0, msg_size);
- if (!*cl.site)
+ if (*cl->site == '\0')
site = local;
- else if (!find_site_by_name(conf_ptr, cl.site, &site, 1)) {
- log_error("cannot find site \"%s\"", cl.site);
+ else if (!find_site_by_name(conf_ptr, cl->site, &site, 1)) {
+ log_error("cannot find site \"%s\"", cl->site);
rv = ENOENT;
goto out;
}
tpt = *conf_ptr->transport + TCP;
rv = tpt->open(site);
if (rv < 0)
goto out_close;
rv = tpt->send(conf_ptr, site, request, msg_size);
if (rv < 0)
goto out_close;
rv = tpt->recv_auth(conf_ptr, site, &reply, sizeof(reply));
if (rv < 0)
goto out_close;
data_len = ntohl(reply.header.length) - rv;
/* no attribute, or no ticket found */
if (!data_len) {
goto out_test_reply;
}
data = malloc(data_len+1);
if (!data) {
rv = -ENOMEM;
goto out_close;
}
rv = tpt->recv(site, data, data_len);
if (rv < 0)
goto out_close;
*(data+data_len) = '\0';
*(data + data_len) = '\0';
(void)fputs(data, stdout);
fflush(stdout);
rv = 0;
out_test_reply:
- rv = test_reply_f(ntohl(reply.header.result), cmd);
+ rv = test_reply_f(cl, ntohl(reply.header.result));
out_close:
tpt->close(site);
out:
if (data)
free(data);
return rv;
}
-static int do_command(struct booth_config *conf_ptr,
- cmd_request_t cmd)
+static int do_command(struct command_line *cl, struct booth_config *conf_ptr)
{
struct booth_site *site;
struct boothc_ticket_msg reply;
struct booth_transport const *tpt;
uint32_t leader_id;
int rv;
int reply_cnt = 0, msg_logged = 0;
const char *op_str = "";
assert(conf_ptr != NULL && conf_ptr->transport != NULL);
- if (cmd == CMD_GRANT)
+ if (cl->type == CMD_GRANT)
op_str = "grant";
- else if (cmd == CMD_REVOKE)
+ else if (cl->type == CMD_REVOKE)
op_str = "revoke";
rv = 0;
site = NULL;
/* Always use TCP for client - at least for now. */
tpt = *conf_ptr->transport + TCP;
- if (!*cl.site)
+ if (*cl->site == '\0')
site = local;
else {
- if (!find_site_by_name(conf_ptr, cl.site, &site, 1)) {
- log_error("Site \"%s\" not configured.", cl.site);
+ if (!find_site_by_name(conf_ptr, cl->site, &site, 1)) {
+ log_error("Site \"%s\" not configured.", cl->site);
goto out_close;
}
}
if (site->type == ARBITRATOR) {
if (site == local) {
log_error("We're just an arbitrator, cannot grant/revoke tickets here.");
} else {
- log_error("%s is just an arbitrator, cannot grant/revoke tickets there.", cl.site);
+ log_error("%s is just an arbitrator, cannot grant/revoke tickets there.",
+ cl->site);
}
goto out_close;
}
assert(site->type == SITE);
/* We don't check for existence of ticket, so that asking can be
* done without local configuration, too.
* Although, that means that the UDP port has to be specified, too. */
- if (!cl.msg.ticket.id[0]) {
+ if (!cl->msg.ticket.id[0]) {
/* If the loaded configuration has only a single ticket defined, use that. */
if (conf_ptr->ticket_count == 1) {
- strncpy(cl.msg.ticket.id, conf_ptr->ticket[0].name,
- sizeof(cl.msg.ticket.id));
+ strncpy(cl->msg.ticket.id, conf_ptr->ticket[0].name,
+ sizeof(cl->msg.ticket.id));
} else {
log_error("No ticket given.");
goto out_close;
}
}
redirect:
- init_header(conf_ptr, &cl.msg.header, cmd, 0, cl.options, 0, 0, sizeof(cl.msg));
+ init_header(conf_ptr, &cl->msg.header, cl->type, 0, cl->options, 0, 0,
+ sizeof(cl->msg));
rv = tpt->open(site);
if (rv < 0)
goto out_close;
- rv = tpt->send(conf_ptr, site, &cl.msg, sendmsglen(&cl.msg));
+ rv = tpt->send(conf_ptr, site, &cl->msg, sendmsglen(&cl->msg));
if (rv < 0)
goto out_close;
read_more:
rv = tpt->recv_auth(conf_ptr, site, &reply, sizeof(reply));
if (rv < 0) {
/* print any errors depending on the code sent by the
* server */
- (void)test_reply(ntohl(reply.header.result), cmd);
+ (void) test_reply(cl, ntohl(reply.header.result));
goto out_close;
}
- rv = test_reply(ntohl(reply.header.result), cmd);
+ rv = test_reply(cl, ntohl(reply.header.result));
if (rv == 1) {
tpt->close(site);
leader_id = ntohl(reply.ticket.leader);
if (!find_site_by_id(conf_ptr, leader_id, &site)) {
log_error("Message with unknown redirect site %x received", leader_id);
rv = -1;
goto out_close;
}
goto redirect;
} else if (rv == 2 || rv == 3) {
/* the server has more to say */
/* don't wait too long */
- if (reply_cnt > 1 && !(cl.options & OPT_WAIT)) {
+ if (reply_cnt > 1 && !(cl->options & OPT_WAIT)) {
rv = 0;
log_info("Giving up on waiting for the definite result. "
"Please use \"booth list\" later to "
"see the outcome.");
goto out_close;
}
if (reply_cnt == 0) {
log_info("%s request sent, "
"waiting for the result ...", op_str);
msg_logged++;
} else if (rv == 3 && msg_logged < 2) {
log_info("waiting for the CIB commit ...");
msg_logged++;
}
reply_cnt++;
goto read_more;
}
out_close:
if (site)
tpt->close(site);
return rv;
}
-static int _lockfile(int mode, int *fdp, pid_t *locked_by)
+static int _lockfile(struct command_line *cl, int mode, int *fdp,
+ pid_t *locked_by)
{
struct flock lock;
int fd, rv;
/* After reboot the directory may not yet exist.
* Try to create it, but ignore errors. */
- if (strncmp(cl.lockfile, BOOTH_RUN_DIR,
+ if (strncmp(cl->lockfile, BOOTH_RUN_DIR,
strlen(BOOTH_RUN_DIR)) == 0)
mkdir(BOOTH_RUN_DIR, 0775);
if (locked_by)
*locked_by = 0;
*fdp = -1;
- fd = open(cl.lockfile, mode, 0664);
+ fd = open(cl->lockfile, mode, 0664);
if (fd < 0)
return errno;
*fdp = fd;
lock.l_type = F_WRLCK;
lock.l_start = 0;
lock.l_whence = SEEK_SET;
lock.l_len = 0;
lock.l_pid = 0;
if (fcntl(fd, F_SETLK, &lock) == 0)
return 0;
rv = errno;
if (locked_by)
if (fcntl(fd, F_GETLK, &lock) == 0)
*locked_by = lock.l_pid;
return rv;
}
static inline int is_root(void)
{
return geteuid() == 0;
}
-static int create_lockfile(struct booth_config *conf_ptr)
+static int create_lockfile(struct command_line *cl,
+ struct booth_config *conf_ptr)
{
int rv, fd;
fd = -1;
- rv = _lockfile(O_CREAT | O_WRONLY, &fd, NULL);
+ rv = _lockfile(cl, O_CREAT | O_WRONLY, &fd, NULL);
if (fd == -1) {
log_error("lockfile %s open error %d: %s",
- cl.lockfile, rv, strerror(rv));
+ cl->lockfile, rv, strerror(rv));
return -1;
}
if (rv < 0) {
log_error("lockfile %s setlk error %d: %s",
- cl.lockfile, rv, strerror(rv));
+ cl->lockfile, rv, strerror(rv));
goto fail;
}
- rv = write_daemon_state(conf_ptr, fd, BOOTHD_STARTING);
+ rv = write_daemon_state(cl, conf_ptr, fd, BOOTHD_STARTING);
if (rv != 0) {
log_error("write daemon state %d to lockfile error %s: %s",
- BOOTHD_STARTING, cl.lockfile, strerror(errno));
+ BOOTHD_STARTING, cl->lockfile, strerror(errno));
goto fail;
}
if (is_root()) {
if (fchown(fd, conf_ptr->uid, conf_ptr->gid) < 0)
log_error("fchown() on lockfile said %d: %s",
errno, strerror(errno));
}
return fd;
fail:
close(fd);
return -1;
}
static void unlink_lockfile(int fd)
{
- unlink(cl.lockfile);
+ unlink(cmd_line.lockfile);
close(fd);
}
static void print_usage(void)
{
printf(
"Usage:\n"
" booth list [options]\n"
" booth {grant|revoke} [options] <ticket>\n"
" booth status [options]\n"
"\n"
" list: List all tickets\n"
" grant: Grant ticket to site\n"
" revoke: Revoke ticket\n"
"\n"
"Options:\n"
" -c FILE Specify config file [default " BOOTH_DEFAULT_CONF "]\n"
" Can be a path or just a name without \".conf\" suffix\n"
" -s <site> Connect/grant to a different site\n"
" -F Try to grant the ticket immediately\n"
" even if not all sites are reachable\n"
" For manual tickets:\n"
" grant a manual ticket even if it has been already granted\n"
" -w Wait forever for the outcome of the request\n"
" -C Wait until the ticket is committed to the CIB (grant only)\n"
" -h Print this help\n"
"\n"
"Examples:\n"
"\n"
" # booth list (list tickets)\n"
" # booth grant ticket-A (grant ticket here)\n"
" # booth grant -s 10.121.8.183 ticket-A (grant ticket to site 10.121.8.183)\n"
" # booth revoke ticket-A (revoke ticket)\n"
"\n"
"See the booth(8) man page for more details.\n"
);
}
#define OPTION_STRING "c:Dl:t:s:FhSwC"
#define ATTR_OPTION_STRING "c:Dt:s:h"
-void safe_copy(char *dest, char *value, size_t buflen, const char *description) {
+void safe_copy(char *dest, const char *value, size_t buflen,
+ const char *description)
+{
int content_len = buflen - 1;
if (strlen(value) >= content_len) {
fprintf(stderr, "'%s' exceeds maximum %s length of %d\n",
value, description, content_len);
exit(EXIT_FAILURE);
}
strncpy(dest, value, content_len);
dest[content_len] = 0;
}
static int host_convert(char *hostname, char *ip_str, size_t ip_size)
{
struct addrinfo *result = NULL, hints = {0};
int re = -1;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
re = getaddrinfo(hostname, NULL, &hints, &result);
if (re == 0) {
struct in_addr addr = ((struct sockaddr_in *)result->ai_addr)->sin_addr;
const char *re_ntop = inet_ntop(AF_INET, &addr, ip_str, ip_size);
if (re_ntop == NULL) {
re = -1;
}
}
freeaddrinfo(result);
return re;
}
#define cparg(dest, descr) do { \
if (optind >= argc) \
goto missingarg; \
safe_copy(dest, argv[optind], sizeof(dest), descr); \
optind++; \
} while(0)
-static int read_arguments(int argc, char **argv)
+static int read_arguments(struct command_line *cl, int argc, char **argv)
{
int optchar;
char *arg1 = argv[1];
char *op = NULL;
char *cp;
const char *opt_string = OPTION_STRING;
char site_arg[INET_ADDRSTRLEN] = {0};
int left;
- cl.type = 0;
+ cl->type = 0;
if ((cp = strstr(argv[0], ATTR_PROG)) &&
!strcmp(cp, ATTR_PROG)) {
- cl.type = GEOSTORE;
+ cl->type = GEOSTORE;
op = argv[1];
optind = 2;
opt_string = ATTR_OPTION_STRING;
} else if (argc > 1 && (strcmp(arg1, "arbitrator") == 0 ||
strcmp(arg1, "site") == 0 ||
strcmp(arg1, "start") == 0 ||
strcmp(arg1, "daemon") == 0)) {
- cl.type = DAEMON;
+ cl->type = DAEMON;
optind = 2;
} else if (argc > 1 && (strcmp(arg1, "status") == 0)) {
- cl.type = STATUS;
+ cl->type = STATUS;
optind = 2;
} else if (argc > 1 && (strcmp(arg1, "client") == 0)) {
- cl.type = CLIENT;
+ cl->type = CLIENT;
if (argc < 3) {
print_usage();
exit(EXIT_FAILURE);
}
op = argv[2];
optind = 3;
}
- if (!cl.type) {
- cl.type = CLIENT;
+ if (!cl->type) {
+ cl->type = CLIENT;
op = argv[1];
optind = 2;
}
if (argc < 2 || !strcmp(arg1, "help") || !strcmp(arg1, "--help") ||
!strcmp(arg1, "-h")) {
- if (cl.type == GEOSTORE)
+ if (cl->type == GEOSTORE)
print_geostore_usage();
else
print_usage();
exit(EXIT_SUCCESS);
}
if (!strcmp(arg1, "version") || !strcmp(arg1, "--version") ||
!strcmp(arg1, "-V")) {
printf("%s %s\n", argv[0], RELEASE_STR);
exit(EXIT_SUCCESS);
}
- if (cl.type == CLIENT) {
+ if (cl->type == CLIENT) {
if (!strcmp(op, "list"))
- cl.op = CMD_LIST;
+ cl->op = CMD_LIST;
else if (!strcmp(op, "grant"))
- cl.op = CMD_GRANT;
+ cl->op = CMD_GRANT;
else if (!strcmp(op, "revoke"))
- cl.op = CMD_REVOKE;
+ cl->op = CMD_REVOKE;
else if (!strcmp(op, "peers"))
- cl.op = CMD_PEERS;
+ cl->op = CMD_PEERS;
else {
fprintf(stderr, "client operation \"%s\" is unknown\n",
op);
exit(EXIT_FAILURE);
}
- } else if (cl.type == GEOSTORE) {
+ } else if (cl->type == GEOSTORE) {
if (!strcmp(op, "list"))
- cl.op = ATTR_LIST;
+ cl->op = ATTR_LIST;
else if (!strcmp(op, "set"))
- cl.op = ATTR_SET;
+ cl->op = ATTR_SET;
else if (!strcmp(op, "get"))
- cl.op = ATTR_GET;
+ cl->op = ATTR_GET;
else if (!strcmp(op, "delete"))
- cl.op = ATTR_DEL;
+ cl->op = ATTR_DEL;
else {
fprintf(stderr, "attribute operation \"%s\" is unknown\n",
op);
exit(EXIT_FAILURE);
}
}
while (optind < argc) {
optchar = getopt(argc, argv, opt_string);
switch (optchar) {
case 'c':
if (strchr(optarg, '/')) {
- safe_copy(cl.configfile, optarg,
- sizeof(cl.configfile), "config file");
+ safe_copy(cl->configfile, optarg,
+ sizeof(cl->configfile), "config file");
} else {
/* If no "/" in there, use with default directory. */
- strcpy(cl.configfile, BOOTH_DEFAULT_CONF_DIR);
- cp = cl.configfile + strlen(BOOTH_DEFAULT_CONF_DIR);
- assert(cp > cl.configfile);
+ strcpy(cl->configfile, BOOTH_DEFAULT_CONF_DIR);
+ cp = cl->configfile + strlen(BOOTH_DEFAULT_CONF_DIR);
+ assert(cp > cl->configfile);
assert(*(cp-1) == '/');
/* Write at the \0, ie. after the "/" */
safe_copy(cp, optarg,
- (sizeof(cl.configfile) -
- (cp - cl.configfile) -
+ (sizeof(cl->configfile) -
+ (cp - cl->configfile) -
strlen(BOOTH_DEFAULT_CONF_EXT)),
"config name");
/* If no extension, append ".conf".
* Space is available, see -strlen() above. */
if (!strchr(cp, '.'))
strcat(cp, BOOTH_DEFAULT_CONF_EXT);
}
break;
case 'D':
debug_level++;
break;
case 'S':
daemonize = 0;
enable_stderr = 1;
break;
case 'l':
- safe_copy(cl.lockfile, optarg, sizeof(cl.lockfile), "lock file");
+ safe_copy(cl->lockfile, optarg, sizeof(cl->lockfile),
+ "lock file");
break;
case 't':
- if (cl.op == CMD_GRANT || cl.op == CMD_REVOKE) {
- safe_copy(cl.msg.ticket.id, optarg,
- sizeof(cl.msg.ticket.id), "ticket name");
- } else if (cl.type == GEOSTORE) {
- safe_copy(cl.attr_msg.attr.tkt_id, optarg,
- sizeof(cl.attr_msg.attr.tkt_id), "ticket name");
+ if (cl->op == CMD_GRANT || cl->op == CMD_REVOKE) {
+ safe_copy(cl->msg.ticket.id, optarg,
+ sizeof(cl->msg.ticket.id), "ticket name");
+ } else if (cl->type == GEOSTORE) {
+ safe_copy(cl->attr_msg.attr.tkt_id, optarg,
+ sizeof(cl->attr_msg.attr.tkt_id), "ticket name");
} else {
print_usage();
exit(EXIT_FAILURE);
}
break;
case 's':
/* For testing and debugging: allow "-s site" also for
* daemon start, so that the address that should be used
* can be set manually.
* This makes it easier to start multiple processes
* on one machine. */
- if (cl.type == CLIENT || cl.type == GEOSTORE ||
- (cl.type == DAEMON && debug_level)) {
+ if (cl->type == CLIENT || cl->type == GEOSTORE ||
+ (cl->type == DAEMON && debug_level)) {
if (strcmp(optarg, OTHER_SITE) &&
host_convert(optarg, site_arg, INET_ADDRSTRLEN) == 0) {
- safe_copy(cl.site, site_arg, sizeof(cl.site), "site name");
+ safe_copy(cl->site, site_arg, sizeof(cl->site), "site name");
} else {
- safe_copy(cl.site, optarg, sizeof(cl.site), "site name");
+ safe_copy(cl->site, optarg, sizeof(cl->site), "site name");
}
} else {
log_error("\"-s\" not allowed in daemon mode.");
exit(EXIT_FAILURE);
}
break;
case 'F':
- if (cl.type != CLIENT || cl.op != CMD_GRANT) {
+ if (cl->type != CLIENT || cl->op != CMD_GRANT) {
log_error("use \"-F\" only for client grant");
exit(EXIT_FAILURE);
}
- cl.options |= OPT_IMMEDIATE;
+ cl->options |= OPT_IMMEDIATE;
break;
case 'w':
- if (cl.type != CLIENT ||
- (cl.op != CMD_GRANT && cl.op != CMD_REVOKE)) {
+ if (cl->type != CLIENT ||
+ (cl->op != CMD_GRANT && cl->op != CMD_REVOKE)) {
log_error("use \"-w\" only for grant and revoke");
exit(EXIT_FAILURE);
}
- cl.options |= OPT_WAIT;
+ cl->options |= OPT_WAIT;
break;
case 'C':
- if (cl.type != CLIENT || cl.op != CMD_GRANT) {
+ if (cl->type != CLIENT || cl->op != CMD_GRANT) {
log_error("use \"-C\" only for grant");
exit(EXIT_FAILURE);
}
- cl.options |= OPT_WAIT | OPT_WAIT_COMMIT;
+ cl->options |= OPT_WAIT | OPT_WAIT_COMMIT;
break;
case 'h':
- if (cl.type == GEOSTORE)
+ if (cl->type == GEOSTORE)
print_geostore_usage();
else
print_usage();
exit(EXIT_SUCCESS);
break;
case ':':
case '?':
fprintf(stderr, "Please use '-h' for usage.\n");
exit(EXIT_FAILURE);
break;
case -1:
/* No more parameters on cmdline, only arguments. */
goto extra_args;
default:
goto unknown;
};
}
return 0;
extra_args:
- if (cl.type == CLIENT && !cl.msg.ticket.id[0]) {
- cparg(cl.msg.ticket.id, "ticket name");
- } else if (cl.type == GEOSTORE) {
- if (cl.op != ATTR_LIST) {
- cparg(cl.attr_msg.attr.name, "attribute name");
+ if (cl->type == CLIENT && !cl->msg.ticket.id[0]) {
+ cparg(cl->msg.ticket.id, "ticket name");
+ } else if (cl->type == GEOSTORE) {
+ if (cl->op != ATTR_LIST) {
+ cparg(cl->attr_msg.attr.name, "attribute name");
}
- if (cl.op == ATTR_SET) {
- cparg(cl.attr_msg.attr.val, "attribute value");
+ if (cl->op == ATTR_SET) {
+ cparg(cl->attr_msg.attr.val, "attribute value");
}
}
if (optind == argc)
return 0;
left = argc - optind;
fprintf(stderr, "Superfluous argument%s: %s%s\n",
left == 1 ? "" : "s",
argv[optind],
left == 1 ? "" : "...");
exit(EXIT_FAILURE);
unknown:
fprintf(stderr, "unknown option: %s\n", argv[optind]);
exit(EXIT_FAILURE);
missingarg:
fprintf(stderr, "not enough arguments\n");
exit(EXIT_FAILURE);
}
static void set_scheduler(void)
{
struct sched_param sched_param;
struct rlimit rlimit;
int rv;
rlimit.rlim_cur = RLIM_INFINITY;
rlimit.rlim_max = RLIM_INFINITY;
setrlimit(RLIMIT_MEMLOCK, &rlimit);
rv = mlockall(MCL_CURRENT | MCL_FUTURE);
if (rv < 0) {
log_error("mlockall failed");
}
rv = sched_get_priority_max(SCHED_RR);
if (rv != -1) {
sched_param.sched_priority = rv;
rv = sched_setscheduler(0, SCHED_RR, &sched_param);
if (rv == -1)
log_error("could not set SCHED_RR priority %d: %s (%d)",
sched_param.sched_priority,
strerror(errno), errno);
} else {
log_error("could not get maximum scheduler priority err %d",
errno);
}
}
static int set_procfs_val(const char *path, const char *val)
{
int rc = -1;
FILE *fp = fopen(path, "w");
if (fp) {
if (fprintf(fp, "%s", val) > 0)
rc = 0;
fclose(fp);
}
return rc;
}
-static int do_status(struct booth_config **conf_pptr, int type)
+static int do_status(struct command_line *cl, struct booth_config **conf_pptr)
{
pid_t pid;
int rv, status_lock_fd, ret;
const char *reason = NULL;
char lockfile_data[1024], *cp;
assert(conf_pptr != NULL);
ret = PCMK_OCF_NOT_RUNNING;
- rv = setup_config(conf_pptr, type);
+ rv = setup_config(cl, conf_pptr);
if (rv) {
reason = "Error reading configuration.";
ret = PCMK_OCF_UNKNOWN_ERROR;
goto quit;
}
if (!local) {
reason = "No Service IP active here.";
goto quit;
}
- rv = _lockfile(O_RDWR, &status_lock_fd, &pid);
+ rv = _lockfile(cl, O_RDWR, &status_lock_fd, &pid);
if (status_lock_fd == -1) {
reason = "No PID file.";
goto quit;
}
if (rv == 0) {
close(status_lock_fd);
reason = "PID file not locked.";
goto quit;
}
if (pid) {
fprintf(stdout, "booth_lockpid=%d ", pid);
fflush(stdout);
}
rv = read(status_lock_fd, lockfile_data, sizeof(lockfile_data) - 1);
if (rv < 4) {
close(status_lock_fd);
reason = "Cannot read lockfile data.";
ret = PCMK_LSB_UNKNOWN_ERROR;
goto quit;
}
lockfile_data[rv] = 0;
close(status_lock_fd);
/* Make sure it's only a single line */
cp = strchr(lockfile_data, '\r');
if (cp)
*cp = 0;
cp = strchr(lockfile_data, '\n');
if (cp)
*cp = 0;
rv = setup_tcp_listener(1);
if (rv == 0) {
reason = "TCP port not in use.";
goto quit;
}
fprintf(stdout, "booth_lockfile='%s' %s\n",
- cl.lockfile, lockfile_data);
+ cl->lockfile, lockfile_data);
if (!daemonize)
fprintf(stderr, "Booth at %s port %d seems to be running.\n",
site_string(local), site_port(local));
return 0;
quit:
log_debug("not running: %s", reason);
/* Ie. "DEBUG" */
if (!daemonize)
fprintf(stderr, "not running: %s\n", reason);
return ret;
}
static int limit_this_process(struct booth_config *conf_ptr)
{
int rv;
if (!is_root())
return 0;
if (setregid(conf_ptr->gid, conf_ptr->gid) < 0) {
rv = errno;
log_error("setregid() didn't work: %s", strerror(rv));
return rv;
}
if (setreuid(conf_ptr->uid, conf_ptr->uid) < 0) {
rv = errno;
log_error("setreuid() didn't work: %s", strerror(rv));
return rv;
}
return 0;
}
static int lock_fd = -1;
static void server_exit(void)
{
int rv;
if (lock_fd >= 0) {
/* We might not be able to delete it, but at least
* make it empty. */
rv = ftruncate(lock_fd, 0);
(void)rv;
unlink_lockfile(lock_fd);
}
log_info("exiting");
}
static void sig_exit_handler(int sig)
{
log_info("caught signal %d", sig);
exit(0);
}
static void wait_child_adaptor(int sig)
{
wait_child(booth_conf);
}
-static int do_server(struct booth_config **conf_pptr, int type)
+static int do_server(struct command_line *cl, struct booth_config **conf_pptr)
{
int rv = -1;
static char log_ent[128] = DAEMON_NAME "-";
assert(conf_pptr != NULL);
- rv = setup_config(conf_pptr, type);
+ rv = setup_config(cl, conf_pptr);
if (rv < 0)
return rv;
if (!local) {
log_error("Cannot find myself in the configuration.");
exit(EXIT_FAILURE);
}
if (daemonize) {
if (daemon(0, 0) < 0) {
perror("daemon error");
exit(EXIT_FAILURE);
}
}
/* The lockfile must be written to _after_ the call to daemon(), so
* that the lockfile contains the pid of the daemon, not the parent. */
- lock_fd = create_lockfile(*conf_pptr);
+ lock_fd = create_lockfile(cl, *conf_pptr);
if (lock_fd < 0)
return lock_fd;
atexit(server_exit);
strcat(log_ent, type_to_string(local->type));
cl_log_set_entity(log_ent);
cl_log_enable_stderr(enable_stderr ? TRUE : FALSE);
cl_log_set_facility(HA_LOG_FACILITY);
cl_inherit_logging_environment(0);
log_info("BOOTH %s %s daemon is starting",
type_to_string(local->type), RELEASE_STR);
signal(SIGUSR1, (__sighandler_t)tickets_log_info);
signal(SIGTERM, (__sighandler_t)sig_exit_handler);
signal(SIGINT, (__sighandler_t)sig_exit_handler);
/* we'll handle errors there and then */
signal(SIGPIPE, SIG_IGN);
set_scheduler();
/* we don't want to be killed by the OOM-killer */
if (set_procfs_val("/proc/self/oom_score_adj", "-999"))
(void)set_procfs_val("/proc/self/oom_adj", "-16");
set_proc_title("%s %s %s for [%s]:%d",
- DAEMON_NAME, cl.configfile, type_to_string(local->type),
+ DAEMON_NAME, cl->configfile, type_to_string(local->type),
site_string(local), site_port(local));
rv = limit_this_process(*conf_pptr);
if (rv)
return rv;
#ifdef COREDUMP_NURSING
if (cl_enable_coredumps(TRUE) < 0){
log_error("enabling core dump failed");
}
cl_cdtocoredir();
prctl(PR_SET_DUMPABLE, (unsigned long)TRUE, 0UL, 0UL, 0UL);
#else
if (chdir(BOOTH_CORE_DIR) < 0) {
log_error("cannot change working directory to %s", BOOTH_CORE_DIR);
}
#endif
signal(SIGCHLD, (__sighandler_t) wait_child_adaptor);
- rv = loop(*conf_pptr, lock_fd);
+ rv = loop(cl, *conf_pptr, lock_fd);
return rv;
}
-static int do_client(struct booth_config **conf_pptr)
+static int do_client(struct command_line *cl, struct booth_config **conf_pptr)
{
int rv;
- rv = setup_config(conf_pptr, CLIENT);
+ rv = setup_config(cl, conf_pptr);
if (rv < 0) {
log_error("cannot read config");
goto out;
}
- switch (cl.op) {
+ switch (cl->op) {
case CMD_LIST:
case CMD_PEERS:
- rv = query_get_string_answer(*conf_pptr, cl.op);
+ rv = query_get_string_answer(cl, *conf_pptr);
break;
case CMD_GRANT:
case CMD_REVOKE:
- rv = do_command(*conf_pptr, cl.op);
+ rv = do_command(cl, *conf_pptr);
break;
}
out:
return rv;
}
-static int do_attr(struct booth_config **conf_pptr)
+static int do_attr(struct command_line *cl, struct booth_config **conf_pptr)
{
int rv = -1;
assert(conf_pptr != NULL);
- rv = setup_config(conf_pptr, GEOSTORE);
+ rv = setup_config(cl, conf_pptr);
if (rv < 0) {
log_error("cannot read config");
goto out;
}
/* We don't check for existence of ticket, so that asking can be
* done without local configuration, too.
* Although, that means that the UDP port has to be specified, too. */
- if (!cl.attr_msg.attr.tkt_id[0]) {
+ if (!cl->attr_msg.attr.tkt_id[0]) {
/* If the loaded configuration has only a single ticket defined, use that. */
if ((*conf_pptr)->ticket_count == 1) {
- strncpy(cl.attr_msg.attr.tkt_id,
+ strncpy(cl->attr_msg.attr.tkt_id,
(*conf_pptr)->ticket[0].name,
- sizeof(cl.attr_msg.attr.tkt_id));
+ sizeof(cl->attr_msg.attr.tkt_id));
} else {
rv = 1;
log_error("No ticket given.");
goto out;
}
}
- switch (cl.op) {
+ switch (cl->op) {
case ATTR_LIST:
case ATTR_GET:
- rv = query_get_string_answer(*conf_pptr, cl.op);
+ rv = query_get_string_answer(cl, *conf_pptr);
break;
case ATTR_SET:
case ATTR_DEL:
- rv = do_attr_command((*conf_pptr), cl.op);
+ rv = do_attr_command(cl, *conf_pptr);
break;
}
out:
return rv;
}
int main(int argc, char *argv[], char *envp[])
{
int rv;
const char *cp;
#ifdef LOGGING_LIBQB
enum qb_log_target_slot i;
#endif
init_set_proc_title(argc, argv, envp);
get_time(&start_time);
- memset(&cl, 0, sizeof(cl));
- strncpy(cl.configfile,
- BOOTH_DEFAULT_CONF, BOOTH_PATH_LEN - 1);
- cl.lockfile[0] = 0;
+ memset(&cmd_line, 0, sizeof(cmd_line));
+ strncpy(cmd_line.configfile, BOOTH_DEFAULT_CONF, BOOTH_PATH_LEN - 1);
debug_level = 0;
cp = ((cp = strstr(argv[0], ATTR_PROG)) && !strcmp(cp, ATTR_PROG)
? ATTR_PROG
: "booth");
#ifndef LOGGING_LIBQB
cl_log_set_entity(cp);
#else
qb_log_init(cp, LOG_USER, LOG_DEBUG); /* prio driven by debug_level */
for (i = QB_LOG_TARGET_START; i < QB_LOG_TARGET_MAX; i++) {
if (i == QB_LOG_SYSLOG || i == QB_LOG_BLACKBOX)
continue;
qb_log_format_set(i, "%t %H %N: [%P]: %p: %b");
}
(void) qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FILE, "*", LOG_DEBUG);
#endif
cl_log_enable_stderr(TRUE);
cl_log_set_facility(0);
- rv = read_arguments(argc, argv);
+ rv = read_arguments(&cmd_line, argc, argv);
if (rv < 0)
goto out;
- switch (cl.type) {
+ switch (cmd_line.type) {
case STATUS:
- rv = do_status(&booth_conf, cl.type);
+ rv = do_status(&cmd_line, &booth_conf);
break;
case ARBITRATOR:
case DAEMON:
case SITE:
- rv = do_server(&booth_conf, cl.type);
+ rv = do_server(&cmd_line, &booth_conf);
break;
case CLIENT:
- rv = do_client(&booth_conf);
+ rv = do_client(&cmd_line, &booth_conf);
break;
case GEOSTORE:
- rv = do_attr(&booth_conf);
+ rv = do_attr(&cmd_line, &booth_conf);
break;
}
out:
#ifdef LOGGING_LIBQB
qb_log_fini();
#endif
/* Normalize values. 0x100 would be seen as "OK" by waitpid(). */
return (rv >= 0 && rv < 0x70) ? rv : 1;
}

File Metadata

Mime Type
text/x-diff
Expires
Mon, Feb 24, 8:38 AM (1 d, 27 m ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1464075
Default Alt Text
(113 KB)

Event Timeline