Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F2825375
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
12 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/pacemaker.c b/src/pacemaker.c
index 7e3f9e6..1582fa8 100644
--- a/src/pacemaker.c
+++ b/src/pacemaker.c
@@ -1,539 +1,541 @@
/*
* 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 <stdlib.h>
#include <errno.h>
#include <inttypes.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include "ticket.h"
#include "log.h"
#include "attr.h"
#include "pacemaker.h"
#include "inline-fn.h"
enum atomic_ticket_supported {
YES=0,
NO,
FILENOTFOUND, /* Ie. UNKNOWN */
UNKNOWN = FILENOTFOUND,
};
/* http://thedailywtf.com/Articles/What_Is_Truth_0x3f_.aspx */
enum atomic_ticket_supported atomicity = UNKNOWN;
#define COMMAND_MAX 1024
/** Determines whether the installed crm_ticket can do atomic ticket grants,
* _including_ multiple attribute changes.
*
* See
* https://bugzilla.novell.com/show_bug.cgi?id=855099
*
* Run "crm_ticket" without "--force";
* - the old version asks for "Y/N" via STDIN, and returns 0
* when reading "no";
* - the new version just reports an error without asking.
+ * Since 2.0.0-rc2 error code is changed from 1 (EPERM) to 4 (CRM_EX_INSUFFICIENT_PRIV)
*/
static void test_atomicity(void)
{
int rv;
if (atomicity != UNKNOWN)
return;
rv = system("echo n | crm_ticket -g -t any-ticket-name > /dev/null 2> /dev/null");
if (rv == -1) {
log_error("Cannot run \"crm_ticket\"!");
/* BIG problem. Abort. */
exit(1);
}
if (WIFSIGNALED(rv)) {
log_error("\"crm_ticket\" terminated by a signal!");
/* Problem. Abort. */
exit(1);
}
switch (WEXITSTATUS(rv)) {
case 0:
atomicity = NO;
log_info("Old \"crm_ticket\" found, using non-atomic ticket updates.");
break;
- case 1:
+ case 1: /* Pacemaker < 2.0.0-rc2 - EPERM */
+ case 4: /* Pacemaker >= 2.0.0-rc2 - CRM_EX_INSUFFICIENT_PRIV */
atomicity = YES;
log_info("New \"crm_ticket\" found, using atomic ticket updates.");
break;
default:
log_error("Unexpected return value from \"crm_ticket\" (%d), "
"falling back to non-atomic ticket updates.",
- rv);
+ WEXITSTATUS(rv));
atomicity = NO;
}
assert(atomicity == YES || atomicity == NO);
}
const char * interpret_rv(int rv)
{
static char text[64];
if (rv == 0)
return "0";
if (WIFSIGNALED(rv))
sprintf(text, "got signal %d", WTERMSIG(rv));
else
sprintf(text, "exit code %d", WEXITSTATUS(rv));
return text;
}
static int pcmk_write_ticket_atomic(struct ticket_config *tk, int grant)
{
char cmd[COMMAND_MAX];
int rv;
/* The values are appended to "-v", so that NO_ONE
* (which is -1) isn't seen as another option. */
snprintf(cmd, COMMAND_MAX,
"crm_ticket -t '%s' "
"%s --force "
"-S owner -v%" PRIi32 " "
"-S expires -v%" PRIi64 " "
"-S term -v%" PRIi64,
tk->name,
(grant > 0 ? "-g" :
grant < 0 ? "-r" :
""),
(int32_t)get_node_id(tk->leader),
(int64_t)wall_ts(&tk->term_expires),
(int64_t)tk->current_term);
rv = system(cmd);
log_debug("command: '%s' was executed", cmd);
if (rv != 0)
log_error("error: \"%s\" failed, %s", cmd, interpret_rv(rv));
return rv;
}
static int pcmk_store_ticket_nonatomic(struct ticket_config *tk);
static int pcmk_grant_ticket(struct ticket_config *tk)
{
char cmd[COMMAND_MAX];
int rv;
test_atomicity();
if (atomicity == YES)
return pcmk_write_ticket_atomic(tk, +1);
rv = pcmk_store_ticket_nonatomic(tk);
if (rv)
return rv;
snprintf(cmd, COMMAND_MAX, "crm_ticket -t %s -g --force",
tk->name);
log_debug("command: '%s' was executed", cmd);
rv = system(cmd);
if (rv != 0)
log_error("error: \"%s\" failed, %s", cmd, interpret_rv(rv));
return rv;
}
static int pcmk_revoke_ticket(struct ticket_config *tk)
{
char cmd[COMMAND_MAX];
int rv;
test_atomicity();
if (atomicity == YES)
return pcmk_write_ticket_atomic(tk, -1);
rv = pcmk_store_ticket_nonatomic(tk);
if (rv)
return rv;
snprintf(cmd, COMMAND_MAX, "crm_ticket -t %s -r --force",
tk->name);
log_debug("command: '%s' was executed", cmd);
rv = system(cmd);
if (rv != 0)
log_error("error: \"%s\" failed, %s", cmd, interpret_rv(rv));
return rv;
}
static int _run_crm_ticket(char *cmd)
{
int i, rv;
/* If there are errors, there's not much we can do but retry ... */
for (i=0; i<3 &&
(rv = system(cmd));
i++) ;
log_debug("'%s' gave result %s", cmd, interpret_rv(rv));
return rv;
}
static int crm_ticket_set_int(const struct ticket_config *tk, const char *attr, int64_t val)
{
char cmd[COMMAND_MAX];
snprintf(cmd, COMMAND_MAX,
"crm_ticket -t '%s' -S '%s' -v %" PRIi64,
tk->name, attr, val);
return _run_crm_ticket(cmd);
}
static int pcmk_set_attr(struct ticket_config *tk, const char *attr, const char *val)
{
char cmd[COMMAND_MAX];
snprintf(cmd, COMMAND_MAX,
"crm_ticket -t '%s' -S '%s' -v '%s'",
tk->name, attr, val);
return _run_crm_ticket(cmd);
}
static int pcmk_del_attr(struct ticket_config *tk, const char *attr)
{
char cmd[COMMAND_MAX];
snprintf(cmd, COMMAND_MAX,
"crm_ticket -t '%s' -D '%s'",
tk->name, attr);
return _run_crm_ticket(cmd);
}
static int pcmk_store_ticket_nonatomic(struct ticket_config *tk)
{
int rv;
/* Always try to store *each* attribute, even if there's an error
* for one of them. */
rv = crm_ticket_set_int(tk, "owner", (int32_t)get_node_id(tk->leader));
rv = crm_ticket_set_int(tk, "expires", wall_ts(&tk->term_expires)) || rv;
rv = crm_ticket_set_int(tk, "term", tk->current_term) || rv;
if (rv)
log_error("setting crm_ticket attributes failed; %s",
interpret_rv(rv));
else
log_info("setting crm_ticket attributes successful");
return rv;
}
typedef int (*attr_f)(struct ticket_config *tk, const char *name,
const char *val);
struct attr_tab
{
const char *name;
attr_f handling_f;
};
static int save_expires(struct ticket_config *tk, const char *name,
const char *val)
{
secs2tv(unwall_ts(atol(val)), &tk->term_expires);
return 0;
}
static int save_term(struct ticket_config *tk, const char *name,
const char *val)
{
tk->current_term = atol(val);
return 0;
}
static int parse_boolean(const char *val)
{
long v;
if (!strncmp(val, "false", 5)) {
v = 0;
} else if (!strncmp(val, "true", 4)) {
v = 1;
} else {
v = atol(val);
}
return v;
}
static int save_granted(struct ticket_config *tk, const char *name,
const char *val)
{
tk->is_granted = parse_boolean(val);
return 0;
}
static int save_owner(struct ticket_config *tk, const char *name,
const char *val)
{
/* No check, node could have been deconfigured. */
tk->leader = NULL;
return !find_site_by_id(atol(val), &tk->leader);
}
static int ignore_attr(struct ticket_config *tk, const char *name,
const char *val)
{
return 0;
}
static int save_attr(struct ticket_config *tk, const char *name,
const char *val)
{
/* tell store_geo_attr not to store time, we don't have that
* information available
*/
return store_geo_attr(tk, name, val, 1);
}
struct attr_tab attr_handlers[] = {
{ "expires", save_expires},
{ "term", save_term},
{ "granted", save_granted},
{ "owner", save_owner},
{ "id", ignore_attr},
{ "last-granted", ignore_attr},
{ NULL, 0},
};
/* get_attr is currently not used and has not been tested
*/
static int pcmk_get_attr(struct ticket_config *tk, const char *attr, const char **vp)
{
char cmd[COMMAND_MAX];
char line[BOOTH_ATTRVAL_LEN+1];
int rv = 0;
FILE *p;
*vp = NULL;
snprintf(cmd, COMMAND_MAX,
"crm_ticket -t '%s' -G '%s' --quiet",
tk->name, attr);
p = popen(cmd, "r");
if (p == NULL) {
rv = errno;
log_error("popen error %d (%s) for \"%s\"",
rv, strerror(rv), cmd);
return rv || EINVAL;
}
if (fgets(line, BOOTH_ATTRVAL_LEN, p) == NULL) {
rv = ENODATA;
goto out;
}
*vp = g_strdup(line);
out:
rv = pclose(p);
if (!rv) {
log_debug("command \"%s\"", cmd);
} else if (WEXITSTATUS(rv) == 6) {
log_info("command \"%s\", ticket not found", cmd);
} else {
log_error("command \"%s\" %s", cmd, interpret_rv(rv));
}
return rv;
}
static int save_attributes(struct ticket_config *tk, xmlDocPtr doc)
{
int rv = 0, rc;
xmlNodePtr n;
xmlAttrPtr attr;
xmlChar *v;
struct attr_tab *atp;
n = xmlDocGetRootElement(doc);
if (n == NULL) {
tk_log_error("crm_ticket xml output empty");
return -EINVAL;
}
if (xmlStrcmp(n->name, (const xmlChar *)"ticket_state")) {
tk_log_error("crm_ticket xml root element not ticket_state");
return -EINVAL;
}
for (attr = n->properties; attr; attr = attr->next) {
v = xmlGetProp(n, attr->name);
for (atp = attr_handlers; atp->name; atp++) {
if (!strcmp(atp->name, (const char *) attr->name)) {
rc = atp->handling_f(tk, (const char *) attr->name,
(const char *) v);
break;
}
}
if (!atp->name) {
rc = save_attr(tk, (const char *) attr->name,
(const char *) v);
}
if (rc) {
tk_log_error("error storing attribute %s", attr->name);
rv |= rc;
}
xmlFree(v);
}
return rv;
}
#define CHUNK_SIZE 256
static int parse_ticket_state(struct ticket_config *tk, FILE *p)
{
int rv = 0;
GString *input = NULL;
char line[CHUNK_SIZE];
xmlDocPtr doc = NULL;
xmlErrorPtr errptr;
int opts = XML_PARSE_COMPACT | XML_PARSE_NONET;
/* skip first two lines of output */
if (fgets(line, CHUNK_SIZE-1, p) == NULL || fgets(line, CHUNK_SIZE-1, p) == NULL) {
tk_log_error("crm_ticket xml output empty");
rv = ENODATA;
goto out;
}
input = g_string_sized_new(CHUNK_SIZE);
if (!input) {
log_error("out of memory");
rv = -1;
goto out;
}
while (fgets(line, CHUNK_SIZE-1, p) != NULL) {
if (!g_string_append(input, line)) {
log_error("out of memory");
rv = -1;
goto out;
}
}
doc = xmlReadDoc((const xmlChar *) input->str, NULL, NULL, opts);
if (doc == NULL) {
errptr = xmlGetLastError();
if (errptr) {
tk_log_error("crm_ticket xml parse failed (domain=%d, level=%d, code=%d): %s",
errptr->domain, errptr->level,
errptr->code, errptr->message);
} else {
tk_log_error("crm_ticket xml parse failed");
}
rv = -EINVAL;
goto out;
}
rv = save_attributes(tk, doc);
out:
if (doc)
xmlFreeDoc(doc);
if (input)
g_string_free(input, TRUE);
return rv;
}
static int pcmk_load_ticket(struct ticket_config *tk)
{
char cmd[COMMAND_MAX];
int rv = 0, pipe_rv;
FILE *p;
/* This here gets run during startup; testing that here means that
* normal operation won't be interrupted with that test. */
test_atomicity();
snprintf(cmd, COMMAND_MAX,
"crm_ticket -t '%s' -q",
tk->name);
p = popen(cmd, "r");
if (p == NULL) {
pipe_rv = errno;
log_error("popen error %d (%s) for \"%s\"",
pipe_rv, strerror(pipe_rv), cmd);
return pipe_rv || -EINVAL;
}
rv = parse_ticket_state(tk, p);
if (!tk->leader) {
/* Hmm, no site found for the ticket we have in the
* CIB!?
* Assume that the ticket belonged to us if it was
* granted here!
*/
log_warn("%s: no site matches; site got reconfigured?",
tk->name);
if (tk->is_granted) {
log_warn("%s: granted here, assume it belonged to us",
tk->name);
set_leader(tk, local);
}
}
pipe_rv = pclose(p);
if (!pipe_rv) {
log_debug("command \"%s\"", cmd);
} else if (WEXITSTATUS(pipe_rv) == 6) {
log_info("command \"%s\", ticket not found", cmd);
} else {
log_error("command \"%s\" %s", cmd, interpret_rv(pipe_rv));
}
return rv | pipe_rv;
}
struct ticket_handler pcmk_handler = {
.grant_ticket = pcmk_grant_ticket,
.revoke_ticket = pcmk_revoke_ticket,
.load_ticket = pcmk_load_ticket,
.set_attr = pcmk_set_attr,
.get_attr = pcmk_get_attr,
.del_attr = pcmk_del_attr,
};
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Jan 25, 12:22 PM (19 h, 56 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1320086
Default Alt Text
(12 KB)
Attached To
Mode
rB Booth
Attached
Detach File
Event Timeline
Log In to Comment