Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F3155098
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
35 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/config.c b/src/config.c
index b118516..4f978c6 100644
--- a/src/config.c
+++ b/src/config.c
@@ -1,409 +1,426 @@
/*
* Copyright (C) 2011 Jiaju Zhang <jjzhang@suse.de>
* Copyright (C) 2013 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.1 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 library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
+#include <zlib.h>
#include <errno.h>
#include <string.h>
#include "booth.h"
#include "config.h"
#include "ticket.h"
#include "log.h"
static int ticket_size = 0;
static int ticket_realloc(void)
{
void *p;
booth_conf = realloc(booth_conf, sizeof(struct booth_config)
+ (ticket_size + TICKET_ALLOC)
* sizeof(struct ticket_config));
if (!booth_conf) {
log_error("can't alloc more booth config");
return -ENOMEM;
}
p = (char *) booth_conf + sizeof(struct booth_config)
+ ticket_size * sizeof(struct ticket_config);
memset(p, 0, TICKET_ALLOC * sizeof(struct ticket_config));
ticket_size += TICKET_ALLOC;
return 0;
}
int add_node(char *address, int type);
int add_node(char *addr_string, int type)
{
int rv;
struct booth_node *node;
+ uLong nid;
+ uint32_t mask;
+
rv = 1;
if (booth_conf->node_count == MAX_NODES) {
log_error("too many nodes");
goto out;
}
if (strlen(addr_string)+1 >= sizeof(booth_conf->node[0].addr_string)) {
log_error("node address \"%s\" too long", addr_string);
goto out;
}
node = booth_conf->node + booth_conf->node_count;
node->family = BOOTH_PROTO_FAMILY;
node->type = type;
/* Make nodeid start at a non-zero point.
* Perhaps use hash over string or address? */
- node->nodeid = booth_conf->node_count * 0x11 + 0x98989011;
strcpy(node->addr_string, addr_string);
+
+ nid = crc32(0L, NULL, 0);
+ /* booth_config() uses memset(), so sizeof() is guaranteed to give
+ * the same result everywhere - no uninitialized bytes. */
+ node->nodeid = crc32(nid, node->addr_string,
+ sizeof(node->addr_string));
+ /* Make sure we will never collide with NO_OWNER,
+ * or be negative (to get "get_local_id() < 0" working). */
+ mask = 1 << (sizeof(node->nodeid)*4 -1);
+ assert(NO_OWNER & mask);
+ assert(NO_OWNER >= 0);
+ node->nodeid &= ~mask;
+
+
node->tcp_fd = -1;
booth_conf->node_count++;
rv = 0;
memset(&node->sa6, 0, sizeof(node->sa6));
if (inet_pton(AF_INET,
node->addr_string,
&node->sa4.sin_addr) > 0) {
node->family = AF_INET;
node->sa4.sin_family = node->family;
node->sa4.sin_port = htons(booth_conf->port);
node->saddrlen = sizeof(node->sa4);
node->addrlen = sizeof(node->sa4.sin_addr);
} else if (inet_pton(AF_INET6,
node->addr_string,
&node->sa6.sin6_addr) > 0) {
node->family = AF_INET6;
node->sa6.sin6_family = node->family;
node->sa6.sin6_flowinfo = 0;
node->sa6.sin6_port = htons(booth_conf->port);
node->saddrlen = sizeof(node->sa6);
node->addrlen = sizeof(node->sa6.sin6_addr);
} else {
log_error("Address string \"%s\" is bad", node->addr_string);
rv = EINVAL;
}
out:
return rv;
}
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 == '#';
}
int read_config(const char *path)
{
char line[1024];
FILE *fp;
char *s, *key, *val, *expiry, *weight, *c, *end_of_key;
const char *cp, *error;
int i;
int lineno = 0;
int got_transport = 0;
fp = fopen(path, "r");
if (!fp) {
log_error("failed to open %s: %s", path, strerror(errno));
return -1;
}
booth_conf = malloc(sizeof(struct booth_config)
+ TICKET_ALLOC * sizeof(struct ticket_config));
if (!booth_conf) {
log_error("failed to alloc memory for booth config");
return -ENOMEM;
}
memset(booth_conf, 0, sizeof(struct booth_config)
+ TICKET_ALLOC * sizeof(struct ticket_config));
ticket_size = TICKET_ALLOC;
booth_conf->proto = UDP;
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))
continue;
key = s;
/* Key */
end_of_key = skip_while(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 (* skip_while(s, isspace)) {
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)
booth_conf->proto = UDP;
else if (strcasecmp(val, "SCTP") == 0)
booth_conf->proto = SCTP;
else {
error = "invalid transport protocol";
goto err;
}
got_transport = 1;
}
if (strcmp(key, "port") == 0)
booth_conf->port = atoi(val);
if (strcmp(key, "name") == 0) {
if(strlen(val)+1 >= BOOTH_NAME_LEN) {
error = "Config name too long.";
goto err;
}
}
if (strcmp(key, "site") == 0) {
if (add_node(val, SITE))
goto out;
}
if (strcmp(key, "arbitrator") == 0) {
if (add_node(val, ARBITRATOR))
goto out;
}
if (strcmp(key, "ticket") == 0) {
int count = booth_conf->ticket_count;
if (booth_conf->ticket_count == ticket_size) {
if (ticket_realloc() < 0)
goto out;
}
expiry = index(val, ';');
weight = rindex(val, ';');
if (!expiry) {
strcpy(booth_conf->ticket[count].name, val);
booth_conf->ticket[count].expiry = DEFAULT_TICKET_EXPIRY;
log_info("expire is not set in %s."
" Set the default value %ds.",
booth_conf->ticket[count].name,
DEFAULT_TICKET_EXPIRY);
}
else if (expiry && expiry == weight) {
*expiry++ = '\0';
while (*expiry == ' ')
expiry++;
strcpy(booth_conf->ticket[count].name, val);
booth_conf->ticket[count].expiry = atoi(expiry);
} else {
*expiry++ = '\0';
*weight++ = '\0';
while (*expiry == ' ')
expiry++;
while (*weight == ' ')
weight++;
strcpy(booth_conf->ticket[count].name, val);
booth_conf->ticket[count].expiry = atoi(expiry);
i = 0;
while ((c = index(weight, ','))) {
*c++ = '\0';
booth_conf->ticket[count].weight[i++]
= atoi(weight);
while (*c == ' ')
c++;
weight = c;
if (i == MAX_NODES) {
error = "too many weights";
goto err;
}
}
}
booth_conf->ticket_count++;
}
}
/* Default: make config name match config filename. */
if (!booth_conf->name[0]) {
cp = strrchr(path, '/');
if (!cp)
cp = path;
/* TODO: locale? */
/* NUL-termination by memset. */
for(i=0; i<BOOTH_NAME_LEN-1 && isalnum(*cp); i++)
booth_conf->name[i] = *(cp++);
/* Last resort. */
if (!booth_conf->name[0])
strcpy(booth_conf->name, "booth");
}
return 0;
err:
log_error("%s in config file line %d",
error, lineno);
out:
free(booth_conf);
booth_conf = NULL;
return -1;
}
int check_config(int type)
{
if (!booth_conf)
return -1;
return 0;
}
int find_site_in_config(unsigned char *site, struct booth_node **node)
{
struct booth_node *n;
int i;
if (!booth_conf)
return 0;
for (i = 0; i < booth_conf->node_count; i++) {
n = booth_conf->node + i;
if (n->type == SITE &&
strcmp(n->addr_string, site) == 0) {
*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";
}
return "??invalid-type??";
}
diff --git a/src/main.c b/src/main.c
index 96ae69f..7d41d73 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,1249 +1,1248 @@
/*
* Copyright (C) 2011 Jiaju Zhang <jjzhang@suse.de>
* Copyright (C) 2013 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.1 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 library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 <pacemaker/crm/services.h>
#include <clplumbing/setproctitle.h>
#include <fcntl.h>
#include <string.h>
#include <assert.h>
#include <error.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <signal.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include "log.h"
#include "booth.h"
#include "config.h"
#include "transport.h"
#include "timer.h"
#include "pacemaker.h"
#include "ticket.h"
#define RELEASE_VERSION "1.0"
#define CLIENT_NALLOC 32
int daemonize = 0;
static int client_maxi;
static int client_size = 0;
struct client *client = NULL;
struct pollfd *pollfd = NULL;
typedef enum
{
BOOTHD_STARTED=0,
BOOTHD_STARTING
} BOOTH_DAEMON_STATE;
int poll_timeout = -1;
typedef enum {
OP_LIST = 1,
OP_GRANT,
OP_REVOKE,
} operation_t;
struct command_line {
int type; /* ACT_ */
int op; /* OP_ */
char configfile[BOOTH_PATH_LEN];
char lockfile[BOOTH_PATH_LEN];
struct boothc_ticket_site_msg msg;
};
static struct command_line cl;
int do_read(int fd, void *buf, size_t count)
{
int rv, off = 0;
while (off < count) {
rv = read(fd, (char *)buf + off, count - off);
if (rv == 0)
return -1;
if (rv == -1 && errno == EINTR)
continue;
if (rv == -1)
return -1;
off += rv;
}
return 0;
}
int do_write(int fd, void *buf, size_t count)
{
int rv, off = 0;
retry:
rv = write(fd, (char *)buf + off, count);
if (rv == -1 && errno == EINTR)
goto retry;
/* If we cannot write _any_ data, we'd be in an (potential) loop. */
if (rv <= 0) {
log_error("write failed: %s (%d)", strerror(errno), errno);
return rv;
}
if (rv != count) {
count -= rv;
off += rv;
goto retry;
}
return 0;
}
static int do_local_connect_and_write(void *data, int len, struct booth_node **ret)
{
struct booth_node *node;
int rv;
if (ret)
*ret = NULL;
/* Use locally reachable address, ie. in same cluster. */
if (!find_myself(&node, 1)) {
log_error("Cannot find local cluster.");
return ENOENT;
}
if (ret)
*ret = node;
/* Always use TCP within cluster. */
rv = booth_tcp_open(node);
if (rv < 0)
goto out;
rv = booth_tcp_send(node, data, len);
out:
return rv;
}
static void client_alloc(void)
{
int i;
if (!client) {
client = malloc(CLIENT_NALLOC * sizeof(struct client));
pollfd = malloc(CLIENT_NALLOC * sizeof(struct pollfd));
} else {
client = realloc(client, (client_size + CLIENT_NALLOC) *
sizeof(struct client));
pollfd = realloc(pollfd, (client_size + CLIENT_NALLOC) *
sizeof(struct pollfd));
if (!pollfd)
log_error("can't alloc for pollfd");
}
if (!client || !pollfd)
log_error("can't alloc for client array");
for (i = client_size; i < client_size + CLIENT_NALLOC; i++) {
client[i].workfn = NULL;
client[i].deadfn = NULL;
client[i].fd = -1;
pollfd[i].fd = -1;
pollfd[i].revents = 0;
}
client_size += CLIENT_NALLOC;
}
static void client_dead(int ci)
{
close(client[ci].fd);
client[ci].workfn = NULL;
client[ci].fd = -1;
pollfd[ci].fd = -1;
}
int client_add(int fd, void (*workfn)(int ci), void (*deadfn)(int ci))
{
int i;
if (!client)
client_alloc();
again:
for (i = 0; i < client_size; i++) {
if (client[i].fd == -1) {
client[i].workfn = workfn;
if (deadfn)
client[i].deadfn = deadfn;
else
client[i].deadfn = client_dead;
client[i].fd = fd;
pollfd[i].fd = fd;
pollfd[i].events = POLLIN;
if (i > client_maxi)
client_maxi = i;
return i;
}
}
client_alloc();
goto again;
}
void process_connection(int ci)
{
struct boothc_ticket_site_msg msg;
struct ticket_config *tc;
int is_local, rv, len, exp, olen;
void (*deadfn) (int ci);
char *data;
rv = do_read(client[ci].fd, &msg.header, sizeof(msg.header));
if (rv < 0) {
if (errno == ECONNRESET)
log_debug("client %d connection reset for fd %d",
ci, client[ci].fd);
goto kill;
}
if (check_boothc_header(&msg.header, -1) < 0)
goto kill;
/* Basic sanity checks already done. */
len = ntohl(msg.header.length);
if (len) {
if (len != sizeof(msg)) {
bad_len:
log_error("got wrong length %u", len);
return;
}
exp = len - sizeof(msg.header);
rv = do_read(client[ci].fd, msg.header.data, exp);
if (rv != exp) {
log_error("connection %d read data error %d, wanted %d",
ci, rv, exp);
goto kill;
}
}
olen = 0;
/* Commands have input msg;
* and output rv, data and olen (excluding header). */
switch (ntohl(msg.header.cmd)) {
case BOOTHC_CMD_LIST:
assert(!data);
rv = list_ticket(&data, &olen);
goto reply;
case BOOTHC_CMD_GRANT:
/* Expect boothc_ticket_site_msg. */
if (len != sizeof(msg))
goto bad_len;
/* Need to return ticket name etc. */
olen = len;
data = msg.header.data;
if (!check_ticket(msg.ticket.id, &tc)) {
rv = BOOTHC_RLT_INVALID_ARG;
goto reply;
}
if (tc->owner != NO_OWNER) {
log_error("client want to get an granted "
"ticket %s", msg.ticket.id);
rv = BOOTHC_RLT_OVERGRANT;
goto reply;
}
if (!check_site(msg.site.site, &is_local)) {
rv = BOOTHC_RLT_INVALID_ARG;
goto reply;
}
if (is_local)
rv = grant_ticket(msg.ticket.id);
else
rv = BOOTHC_RLT_REMOTE_OP;
break;
case BOOTHC_CMD_REVOKE:
/* Expect boothc_ticket_site_msg. */
if (len != sizeof(msg))
goto bad_len;
olen = len;
data = msg.header.data;
if (!check_ticket(msg.ticket.id, &tc)) {
msg.header.result = BOOTHC_RLT_INVALID_ARG;
goto reply;
}
if (!check_site(msg.site.site, &is_local)) {
msg.header.result = BOOTHC_RLT_INVALID_ARG;
goto reply;
}
if (is_local)
msg.header.result = revoke_ticket(msg.ticket.id);
else
msg.header.result = BOOTHC_RLT_REMOTE_OP;
break;
case BOOTHC_CMD_CATCHUP:
/* Expect boothc_ticket_site_msg. */
if (len != sizeof(msg))
goto bad_len;
/* Need to return ticket name etc. */
olen = len;
data = msg.header.data;
if (!check_ticket(msg.ticket.id, &tc)) {
rv = BOOTHC_RLT_INVALID_ARG;
goto reply;
}
rv = catchup_ticket(&msg.ticket, tc);
/* Only answer if we're the owner. */
if (rv == -1)
goto kill;
break;
default:
log_error("connection %d cmd %x unknown",
ci, ntohl(msg.header.cmd));
goto kill;
}
reply:
msg.header.result = htonl(rv);
msg.header.length = htonl(olen + sizeof(msg.header));
rv = do_write(client[ci].fd, &msg.header, sizeof(msg.header));
if (rv < 0)
log_error("connection %d write error %d", ci, rv);
if (len) {
rv = do_write(client[ci].fd, data, olen);
if (rv < 0)
log_error("connection %d write error %d", ci, rv);
}
return;
kill:
deadfn = client[ci].deadfn;
if(deadfn) {
deadfn(ci);
}
return;
}
static void process_listener(int ci)
{
int fd, i;
fd = accept(client[ci].fd, NULL, NULL);
if (fd < 0) {
log_error("process_listener: accept error for fd %d: %s (%d)",
client[ci].fd, strerror(errno), errno);
if (client[ci].deadfn)
client[ci].deadfn(ci);
return;
}
i = client_add(fd, process_connection, NULL);
log_debug("add client connection %d fd %d", i, fd);
}
static int setup_config(int type)
{
int rv;
rv = read_config(cl.configfile);
if (rv < 0)
goto out;
/* Set "local" pointer, ignoring errors. */
find_myself(NULL, 0);
rv = check_config(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,
"%s/%s.pid", BOOTH_RUN_DIR, booth_conf->name);
}
out:
return rv;
}
static int setup_transport(void)
{
int rv;
rv = transport()->init(ticket_recv);
if (rv < 0) {
log_error("failed to init booth_transport %s", transport()->name);
goto out;
}
rv = booth_transport[TCP].init(NULL);
if (rv < 0) {
log_error("failed to init booth_transport[TCP]");
goto out;
}
out:
return rv;
}
static int setup_timer(void)
{
return timerlist_init();
}
static int write_daemon_state(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_addr_string='%s' "
"booth_port=%d\n",
getpid(),
( state == BOOTHD_STARTED ? "started" :
state == BOOTHD_STARTING ? "starting" :
"invalid"),
type_to_string(local->type),
booth_conf->name,
local->addr_string,
booth_conf->port);
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));
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(int fd)
{
void (*workfn) (int ci);
void (*deadfn) (int ci);
int rv, i;
rv = setup_timer();
if (rv < 0)
goto fail;
rv = setup_transport();
if (rv < 0)
goto fail;
rv = setup_ticket();
if (rv < 0)
goto fail;
client_add(rv, process_listener, NULL);
rv = write_daemon_state(fd, BOOTHD_STARTED);
if (rv != 0) {
log_error("write daemon state %d to lockfile error %s: %s",
BOOTHD_STARTED, cl.lockfile, strerror(errno));
goto fail;
}
if (cl.type == ARBITRATOR)
log_info("BOOTH arbitrator daemon started");
else if (cl.type == SITE)
log_info("BOOTH cluster site daemon started");
while (1) {
rv = poll(pollfd, 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 (client[i].fd < 0)
continue;
if (pollfd[i].revents & POLLIN) {
workfn = client[i].workfn;
if (workfn)
workfn(i);
}
if (pollfd[i].revents &
(POLLERR | POLLHUP | POLLNVAL)) {
deadfn = client[i].deadfn;
if (deadfn)
deadfn(i);
}
}
process_timerlist();
}
return 0;
fail:
return -1;
}
static int query_get_string_answer(cmd_request_t cmd)
{
struct booth_node *node;
struct boothc_header h, reply;
char *data;
int data_len;
int rv;
data = NULL;
init_header(&h, cmd, 0, 0);
rv = do_local_connect_and_write(&h, sizeof(h), &node);
if (rv < 0)
goto out;
rv = local_transport->recv(node, &reply, sizeof(reply));
if (rv < 0)
goto out_free;
data_len = ntohl(reply.length) - sizeof(reply);
data = malloc(data_len);
if (!data) {
rv = -ENOMEM;
goto out_free;
}
rv = local_transport->recv(node, data, data_len);
if (rv < 0)
goto out_free;
do_write(STDOUT_FILENO, data, data_len);
rv = 0;
out_free:
free(data);
local_transport->close(node);
out:
return rv;
}
static int do_command(cmd_request_t cmd)
{
struct booth_node *node, *to;
struct boothc_header reply;
int rv;
node = NULL;
to = NULL;
init_ticket_site_header(&cl.msg, cmd);
rv = do_local_connect_and_write(&cl.msg, sizeof(cl.msg), &node);
if (rv < 0)
goto out_close;
rv = local_transport->recv(node, &reply, sizeof(reply));
if (rv < 0)
goto out_close;
if (reply.result == BOOTHC_RLT_INVALID_ARG) {
log_info("invalid argument!");
rv = -1;
goto out_close;
}
if (reply.result == BOOTHC_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;
goto out_close;
}
if (reply.result == BOOTHC_RLT_REMOTE_OP) {
if (!find_site_in_config(cl.msg.site.site, &to)) {
log_error("Redirected to unknown site %s.", cl.msg.site.site);
rv = -1;
goto out_close;
}
rv = booth_transport[TCP].open(to);
if (rv < 0) {
goto out_close;
}
rv = booth_transport[TCP].send(to, &cl.msg, sizeof(cl.msg));
if (rv < 0) {
booth_transport[TCP].close(to);
goto out_close;
}
rv = booth_transport[TCP].recv(to, &reply,
sizeof(struct boothc_header));
if (rv < 0) {
booth_transport[TCP].close(to);
goto out_close;
}
booth_transport[TCP].close(to);
}
if (reply.result == BOOTHC_RLT_ASYNC) {
if (cmd == BOOTHC_CMD_GRANT)
log_info("grant command sent, result will be returned "
"asynchronously, you can get the result from "
"the log files");
else if (cmd == BOOTHC_CMD_REVOKE)
log_info("revoke command sent, result will be returned "
"asynchronously, you can get the result from "
"the log files.");
else
log_error("internal error reading reply result!");
rv = 0;
} else if (reply.result == BOOTHC_RLT_SYNC_SUCC) {
if (cmd == BOOTHC_CMD_GRANT)
log_info("grant succeeded!");
else if (cmd == BOOTHC_CMD_REVOKE)
log_info("revoke succeeded!");
rv = 0;
} else if (reply.result == BOOTHC_RLT_SYNC_FAIL) {
if (cmd == BOOTHC_CMD_GRANT)
log_info("grant failed!");
else if (cmd == BOOTHC_CMD_REVOKE)
log_info("revoke failed!");
rv = -1;
} else {
log_error("internal error!");
rv = -1;
}
out_close:
if (node)
local_transport->close(node);
if (to)
booth_transport[TCP].close(to);
return rv;
}
static int do_grant(void)
{
return do_command(BOOTHC_CMD_GRANT);
}
static int do_revoke(void)
{
return do_command(BOOTHC_CMD_REVOKE);
}
static int _lockfile(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,
strlen(BOOTH_RUN_DIR)) == 0)
mkdir(BOOTH_RUN_DIR, 0775);
if (locked_by)
*locked_by = 0;
*fdp = -1;
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 int lockfile(void) {
int rv, fd;
fd = -1;
rv = _lockfile(O_CREAT | O_WRONLY, &fd, NULL);
if (fd == -1) {
log_error("lockfile %s open error %d: %s",
cl.lockfile, rv, strerror(rv));
return -1;
}
if (rv < 0) {
log_error("lockfile %s setlk error %d: %s",
cl.lockfile, rv, strerror(rv));
goto fail;
}
rv = write_daemon_state(fd, BOOTHD_STARTING);
if (rv != 0) {
log_error("write daemon state %d to lockfile error %s: %s",
BOOTHD_STARTING, cl.lockfile, strerror(errno));
goto fail;
}
return fd;
fail:
close(fd);
return -1;
}
static void unlink_lockfile(int fd)
{
unlink(cl.lockfile);
close(fd);
}
static void print_usage(void)
{
printf("Usage:\n");
printf("booth <type> <operation> [options]\n");
printf("\n");
printf("Types:\n");
printf(" arbitrator: daemon running on arbitrator\n");
printf(" site: daemon running on cluster site\n");
printf(" client: command running from client\n");
printf("\n");
printf("Operations:\n");
printf("Please note that operations are valid iff type is client!\n");
printf(" list: List all the tickets\n");
printf(" grant: Grant ticket T(-t T) to site S(-s S)\n");
printf(" revoke: Revoke ticket T(-t T) from site S(-s S)\n");
printf("\n");
printf("Options:\n");
printf(" -c FILE Specify config file [default " BOOTH_DEFAULT_CONF "]\n");
printf(" -l LOCKFILE Specify lock file path\n");
printf(" -D Enable debugging to stderr and don't fork\n");
printf(" -t ticket name\n");
printf(" -S report local daemon status (for site and arbitrator)\n");
printf(" RA script compliant return codes.\n");
printf(" -s site name\n");
printf(" -h Print this help, then exit\n");
}
#define OPTION_STRING "c:Dl:t:s:hS"
void safe_copy(char *dest, 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 = BOOTH_PROTO_FAMILY;
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(BOOTH_PROTO_FAMILY, &addr, ip_str, ip_size);
if (re_ntop == NULL) {
re = -1;
}
}
freeaddrinfo(result);
return re;
}
static int read_arguments(int argc, char **argv)
{
int optchar;
char *arg1 = argv[1];
char *op = NULL;
char site_arg[INET_ADDRSTRLEN] = {0};
if (argc < 2 || !strcmp(arg1, "help") || !strcmp(arg1, "--help") ||
!strcmp(arg1, "-h")) {
print_usage();
exit(EXIT_SUCCESS);
}
if (!strcmp(arg1, "version") || !strcmp(arg1, "--version") ||
!strcmp(arg1, "-V")) {
printf("%s %s (built %s %s)\n",
argv[0], RELEASE_VERSION, __DATE__, __TIME__);
exit(EXIT_SUCCESS);
}
if (strcmp(arg1, "arbitrator") == 0 ||
strcmp(arg1, "site") == 0 ||
strcmp(arg1, "start") == 0 ||
strcmp(arg1, "daemon") == 0) {
cl.type = DAEMON;
optind = 2;
} else if (strcmp(arg1, "status") == 0) {
cl.type = STATUS;
optind = 2;
} else if (strcmp(arg1, "client") == 0) {
cl.type = CLIENT;
if (argc < 3) {
print_usage();
exit(EXIT_FAILURE);
}
op = argv[2];
optind = 3;
} else {
cl.type = CLIENT;
op = argv[1];
optind = 2;
}
switch (cl.type) {
case ARBITRATOR:
break;
case SITE:
break;
case CLIENT:
if (!strcmp(op, "list"))
cl.op = OP_LIST;
else if (!strcmp(op, "grant"))
cl.op = OP_GRANT;
else if (!strcmp(op, "revoke"))
cl.op = OP_REVOKE;
else {
fprintf(stderr, "client operation \"%s\" is unknown\n",
op);
exit(EXIT_FAILURE);
}
break;
}
while (optind < argc) {
optchar = getopt(argc, argv, OPTION_STRING);
switch (optchar) {
case 'c':
safe_copy(cl.configfile, optarg, sizeof(cl.configfile), "config file");
break;
case 'D':
daemonize = 1;
debug_level++;
break;
case 'l':
safe_copy(cl.lockfile, optarg, sizeof(cl.lockfile), "lock file");
break;
case 't':
if (cl.op == OP_GRANT || cl.op == OP_REVOKE) {
safe_copy(cl.msg.ticket.id, optarg, sizeof(cl.msg.ticket.id), "ticket name");
} else {
print_usage();
exit(EXIT_FAILURE);
}
break;
case 's':
if (cl.op == OP_GRANT || cl.op == OP_REVOKE) {
int re = host_convert(optarg, site_arg, INET_ADDRSTRLEN);
if (re == 0) {
safe_copy(cl.msg.site.site, site_arg, sizeof(cl.msg.ticket), "site name");
} else {
safe_copy(cl.msg.site.site, optarg, sizeof(cl.msg.ticket), "site name");
}
} else {
print_usage();
exit(EXIT_FAILURE);
}
break;
case 'h':
print_usage();
exit(EXIT_SUCCESS);
break;
case ':':
case '?':
fprintf(stderr, "Please use '-h' for usage.\n");
exit(EXIT_FAILURE);
break;
default:
fprintf(stderr, "unknown option: %s\n", argv[optind]);
exit(EXIT_FAILURE);
break;
};
}
return 0;
}
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 void set_oom_adj(int val)
{
FILE *fp;
fp = fopen("/proc/self/oom_adj", "w");
if (!fp)
return;
fprintf(fp, "%i", val);
fclose(fp);
}
static int do_status(int type)
{
pid_t pid;
int rv, lock_fd, ret;
const char *reason = NULL;
char lockfile_data[1024], *cp;
ret = PCMK_OCF_NOT_RUNNING;
/* TODO: query all, and return quit only if it's _cleanly_ not
* running, ie. _neither_ of port/lockfile/process is available?
*
* Currently a single failure says "not running", even if "only" the
* lockfile has been removed. */
rv = setup_config(type);
if (rv) {
reason = "Error reading configuration.";
ret = PCMK_LSB_UNKNOWN_ERROR;
goto quit;
}
if (!local) {
reason = "No Service IP active here.";
goto quit;
}
rv = _lockfile(O_RDWR, &lock_fd, &pid);
if (rv == 0) {
reason = "PID file not locked.";
goto quit;
}
if (lock_fd == -1) {
reason = "No PID file.";
goto quit;
}
if (pid) {
fprintf(stdout, "booth_lockpid=%d ", pid);
fflush(stdout);
}
rv = read(lock_fd, lockfile_data, sizeof(lockfile_data) - 1);
if (rv < 4) {
reason = "Cannot read lockfile data.";
ret = PCMK_LSB_UNKNOWN_ERROR;
goto quit;
}
lockfile_data[rv] = 0;
if (lock_fd != -1)
close(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_udp_server(1);
if (rv == 0) {
reason = "UDP port not in use.";
goto quit;
}
fprintf(stdout, "booth_lockfile='%s' %s\n",
cl.lockfile, lockfile_data);
if (daemonize)
fprintf(stderr, "Booth at %s port %d seems to be running.\n",
local->addr_string, booth_conf->port);
return 0;
quit:
log_debug("not running: %s", reason);
/* Ie. "DEBUG" */
if (daemonize)
fprintf(stderr, "not running: %s\n", reason);
return ret;
}
static int do_server(int type)
{
int lock_fd = -1;
int rv = -1;
static char log_ent[128] = DAEMON_NAME "-";
rv = setup_config(type);
if (rv < 0)
goto out;
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 = lockfile();
if (lock_fd < 0)
return lock_fd;
strcat(log_ent, type_to_string(local->type));
cl_log_set_entity(log_ent);
cl_log_enable_stderr(debug_level ? TRUE : FALSE);
cl_log_set_facility(HA_LOG_FACILITY);
cl_inherit_logging_environment(0);
- if (local->type == ARBITRATOR)
- log_info("BOOTH arbitrator daemon is starting.");
- else if (local->type == SITE)
- log_info("BOOTH cluster site daemon is starting.");
+ log_info("BOOTH %s daemon is starting, node id is %08X.",
+ type_to_string(local->type),
+ local->nodeid);
set_scheduler();
set_oom_adj(-16);
set_proc_title("%s %s for [%s]:%d",
DAEMON_NAME,
type_to_string(local->type),
local->addr_string,
booth_conf->port);
rv = loop(lock_fd);
out:
if (lock_fd >= 0)
unlink_lockfile(lock_fd);
return rv;
}
static int do_client(void)
{
int rv = -1;
rv = setup_config(CLIENT);
if (rv < 0) {
log_error("cannot read config");
goto out;
}
switch (cl.op) {
case OP_LIST:
rv = query_get_string_answer(CMD_LIST);
break;
case OP_GRANT:
rv = do_grant();
break;
case OP_REVOKE:
rv = do_revoke();
break;
}
out:
return rv;
}
int main(int argc, char *argv[], char *envp[])
{
int rv;
init_set_proc_title(argc, argv, envp);
memset(&cl, 0, sizeof(cl));
strncpy(cl.configfile,
BOOTH_DEFAULT_CONF, BOOTH_PATH_LEN - 1);
cl.lockfile[0] = 0;
debug_level = 0;
cl_log_set_entity("booth");
cl_log_enable_stderr(TRUE);
cl_log_set_facility(0);
rv = read_arguments(argc, argv);
if (rv < 0)
goto out;
switch (cl.type) {
case STATUS:
rv = do_status(cl.type);
break;
case ARBITRATOR:
case DAEMON:
case SITE:
rv = do_server(cl.type);
break;
case CLIENT:
rv = do_client();
break;
}
out:
/* Normalize values. 0x100 would be seen as "OK" by waitpid(). */
return rv >= 0 && rv < 0x70 ? rv : 1;
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Wed, Feb 26, 7:26 PM (3 h, 1 m ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1465692
Default Alt Text
(35 KB)
Attached To
Mode
rB Booth
Attached
Detach File
Event Timeline
Log In to Comment