示例#1
0
void
attach_cib_generation(xmlNode * msg, const char *field, xmlNode * a_cib)
{
    xmlNode *generation = create_xml_node(NULL, XML_CIB_TAG_GENERATION_TUPPLE);

    if (a_cib != NULL) {
        copy_in_properties(generation, a_cib);
    }
    add_message_xml(msg, field, generation);
    free_xml(generation);
}
示例#2
0
int
cib_process_ping(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
                 xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
{
#ifdef CIBPIPE
    return -EINVAL;
#else
    const char *host = crm_element_value(req, F_ORIG);
    const char *seq = crm_element_value(req, F_CIB_PING_ID);
    char *digest = calculate_xml_versioned_digest(the_cib, FALSE, TRUE, CRM_FEATURE_SET);

    static struct qb_log_callsite *cs = NULL;

    crm_trace("Processing \"%s\" event %s from %s", op, seq, host);
    *answer = create_xml_node(NULL, XML_CRM_TAG_PING);

    crm_xml_add(*answer, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
    crm_xml_add(*answer, XML_ATTR_DIGEST, digest);
    crm_xml_add(*answer, F_CIB_PING_ID, seq);

    if (cs == NULL) {
        cs = qb_log_callsite_get(__func__, __FILE__, __FUNCTION__, LOG_TRACE, __LINE__, crm_trace_nonlog);
    }
    if (cs && cs->targets) {
        /* Append additional detail so the reciever can log the differences */
        add_message_xml(*answer, F_CIB_CALLDATA, the_cib);

    } else {
        /* Always include at least the version details */
        const char *tag = TYPE(the_cib);
        xmlNode *shallow = create_xml_node(NULL, tag);

        copy_in_properties(shallow, the_cib);
        add_message_xml(*answer, F_CIB_CALLDATA, shallow);
        free_xml(shallow);
    }

    crm_info("Reporting our current digest to %s: %s for %s.%s.%s (%p %d)",
             host, digest,
             crm_element_value(existing_cib, XML_ATTR_GENERATION_ADMIN),
             crm_element_value(existing_cib, XML_ATTR_GENERATION),
             crm_element_value(existing_cib, XML_ATTR_NUMUPDATES),
             existing_cib,
             cs && cs->targets);

    free(digest);

    return pcmk_ok;
#endif
}
示例#3
0
int
cib_process_erase(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
                  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
{
    int result = pcmk_ok;

    crm_trace("Processing \"%s\" event", op);
    *answer = NULL;
    free_xml(*result_cib);
    *result_cib = createEmptyCib(0);

    copy_in_properties(*result_cib, existing_cib);
    cib_update_counter(*result_cib, XML_ATTR_GENERATION_ADMIN, FALSE);

    return result;
}
示例#4
0
enum cib_errors
cib_process_erase(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
                  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
{
    enum cib_errors result = cib_ok;

    crm_debug_2("Processing \"%s\" event", op);
    *answer = NULL;
    free_xml(*result_cib);
    *result_cib = createEmptyCib();

    copy_in_properties(*result_cib, existing_cib);
    cib_update_counter(*result_cib, XML_ATTR_GENERATION, FALSE);

    return result;
}
示例#5
0
int
cib_process_query(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
                  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
{
    xmlNode *obj_root = NULL;
    int result = pcmk_ok;

    crm_trace("Processing \"%s\" event for section=%s", op, crm_str(section));

    if (options & cib_xpath) {
        return cib_process_xpath(op, options, section, req, input,
                                 existing_cib, result_cib, answer);
    }

    CRM_CHECK(*answer == NULL, free_xml(*answer));
    *answer = NULL;

    if (safe_str_eq(XML_CIB_TAG_SECTION_ALL, section)) {
        section = NULL;
    }

    obj_root = get_object_root(section, existing_cib);

    if (obj_root == NULL) {
        result = -ENXIO;

    } else if (options & cib_no_children) {
        const char *tag = TYPE(obj_root);
        xmlNode *shallow = create_xml_node(*answer, tag);

        copy_in_properties(shallow, obj_root);
        *answer = shallow;

    } else {
        *answer = obj_root;
    }

    if (result == pcmk_ok && *answer == NULL) {
        crm_err("Error creating query response");
        result = -ENOMSG;
    }

    return result;
}
示例#6
0
void
join_query_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
{
    xmlNode *local_cib = NULL;
    char *join_id = user_data;
    xmlNode *generation = create_xml_node(NULL, XML_CIB_TAG_GENERATION_TUPPLE);

    CRM_LOG_ASSERT(join_id != NULL);

    query_call_id = 0;

    if (rc == pcmk_ok) {
        local_cib = output;
        CRM_LOG_ASSERT(safe_str_eq(crm_element_name(local_cib), XML_TAG_CIB));
    }

    if (local_cib != NULL) {
        xmlNode *reply = NULL;

        crm_debug("Respond to join offer join-%s", join_id);
        crm_debug("Acknowledging %s as our DC", fsa_our_dc);
        copy_in_properties(generation, local_cib);

        reply = create_request(CRM_OP_JOIN_REQUEST, generation, fsa_our_dc,
                               CRM_SYSTEM_DC, CRM_SYSTEM_CRMD, NULL);

        crm_xml_add(reply, F_CRM_JOIN_ID, join_id);
        if (fsa_our_dc) {
            send_cluster_message(crm_get_peer(0, fsa_our_dc), crm_msg_crmd, reply, TRUE);
        } else {
            crm_warn("No DC for join-%s", join_id);
            send_cluster_message(NULL, crm_msg_crmd, reply, TRUE);
        }
        free_xml(reply);

    } else {
        crm_err("Could not retrieve Generation to attach to our"
                " join acknowledgement: %s", pcmk_strerror(rc));
        register_fsa_error_adv(C_FSA_INTERNAL, I_ERROR, NULL, NULL, __FUNCTION__);
    }

    free(join_id);
    free_xml(generation);
}
示例#7
0
void
join_query_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
{
    char *join_id = user_data;
    xmlNode *generation = create_xml_node(NULL, XML_CIB_TAG_GENERATION_TUPPLE);

    CRM_LOG_ASSERT(join_id != NULL);

    if (query_call_id != call_id) {
        crm_trace("Query %d superceeded", call_id);
        goto done;
    }

    query_call_id = 0;
    if(rc != pcmk_ok || output == NULL) {
        crm_err("Could not retrieve version details for join-%s: %s (%d)",
                join_id, pcmk_strerror(rc), rc);
        register_fsa_error_adv(C_FSA_INTERNAL, I_ERROR, NULL, NULL, __FUNCTION__);

    } else if (fsa_our_dc == NULL) {
        crm_debug("Membership is in flux, not continuing join-%s", join_id);

    } else {
        xmlNode *reply = NULL;

        crm_debug("Respond to join offer join-%s from %s", join_id, fsa_our_dc);
        copy_in_properties(generation, output);

        reply = create_request(CRM_OP_JOIN_REQUEST, generation, fsa_our_dc,
                               CRM_SYSTEM_DC, CRM_SYSTEM_CRMD, NULL);

        crm_xml_add(reply, F_CRM_JOIN_ID, join_id);
        send_cluster_message(crm_get_peer(0, fsa_our_dc), crm_msg_crmd, reply, TRUE);
        free_xml(reply);
    }

  done:
    free_xml(generation);
    free(join_id);
}
示例#8
0
enum cib_errors 
cib_process_erase(
	const char *op, int options, const char *section, crm_data_t *input,
	crm_data_t *existing_cib, crm_data_t **result_cib, crm_data_t **answer)
{
	crm_data_t *local_diff = NULL;
	enum cib_errors result = cib_ok;

	crm_debug_2("Processing \"%s\" event", op);
	*answer = NULL;
	free_xml(*result_cib);
	*result_cib = createEmptyCib();

	copy_in_properties(*result_cib, existing_cib);	
	cib_update_counter(*result_cib, XML_ATTR_GENERATION, FALSE);
	
	local_diff = diff_cib_object(existing_cib, *result_cib, FALSE);
	cib_replace_notify(*result_cib, result, local_diff);
	free_xml(local_diff);
	
	return result;
}
示例#9
0
int
cib_process_xpath(const char *op, int options, const char *section, xmlNode * req, xmlNode * input,
                  xmlNode * existing_cib, xmlNode ** result_cib, xmlNode ** answer)
{
    int lpc = 0;
    int max = 0;
    int rc = pcmk_ok;
    gboolean is_query = safe_str_eq(op, CIB_OP_QUERY);

    xmlXPathObjectPtr xpathObj = NULL;

    crm_trace("Processing \"%s\" event", op);

    if (is_query) {
        xpathObj = xpath_search(existing_cib, section);
    } else {
        xpathObj = xpath_search(*result_cib, section);
    }

    max = numXpathResults(xpathObj);

    if (max < 1 && safe_str_eq(op, CIB_OP_DELETE)) {
        crm_debug("%s was already removed", section);

    } else if (max < 1) {
        crm_debug("%s: %s does not exist", op, section);
        rc = -ENXIO;

    } else if (is_query) {
        if (max > 1) {
            *answer = create_xml_node(NULL, "xpath-query");
        }
    }

    if (safe_str_eq(op, CIB_OP_DELETE) && (options & cib_multiple)) {
        dedupXpathResults(xpathObj);
    }

    for (lpc = 0; lpc < max; lpc++) {
        xmlChar *path = NULL;
        xmlNode *match = getXpathResult(xpathObj, lpc);

        if (match == NULL) {
            continue;
        }

        path = xmlGetNodePath(match);
        crm_debug("Processing %s op for %s (%s)", op, section, path);
        free(path);

        if (safe_str_eq(op, CIB_OP_DELETE)) {
            if (match == *result_cib) {
                /* Attempting to delete the whole "/cib" */
                crm_warn("Cannot perform %s for %s: The xpath is addressing the whole /cib", op, section);
                rc = -EINVAL;
                break;
            }

            free_xml(match);
            if ((options & cib_multiple) == 0) {
                break;
            }

        } else if (safe_str_eq(op, CIB_OP_MODIFY)) {
            if (update_xml_child(match, input) == FALSE) {
                rc = -ENXIO;
            } else if ((options & cib_multiple) == 0) {
                break;
            }

        } else if (safe_str_eq(op, CIB_OP_CREATE)) {
            add_node_copy(match, input);
            break;

        } else if (safe_str_eq(op, CIB_OP_QUERY)) {

            if (options & cib_no_children) {
                const char *tag = TYPE(match);
                xmlNode *shallow = create_xml_node(*answer, tag);

                copy_in_properties(shallow, match);

                if (*answer == NULL) {
                    *answer = shallow;
                }

            } else if (options & cib_xpath_address) {

                int path_len = 0;
                char *path = NULL;
                xmlNode *parent = match;

                while (parent && parent->type == XML_ELEMENT_NODE) {
                    int extra = 1;
                    char *new_path = NULL;
                    const char *id = crm_element_value(parent, XML_ATTR_ID);

                    extra += strlen((const char *)parent->name);
                    if (id) {
                        extra += 8;     /* [@id=""] */
                        extra += strlen(id);
                    }

                    path_len += extra;
                    new_path = malloc(path_len + 1);
                    if(new_path == NULL) {
                        break;

                    } else if (id) {
                        snprintf(new_path, path_len + 1, "/%s[@id='%s']%s", parent->name, id,
                                 path ? path : "");
                    } else {
                        snprintf(new_path, path_len + 1, "/%s%s", parent->name, path ? path : "");
                    }
                    free(path);
                    path = new_path;
                    parent = parent->parent;
                }
                crm_trace("Got: %s\n", path);

                if (*answer == NULL) {
                    *answer = create_xml_node(NULL, "xpath-query");
                }
                parent = create_xml_node(*answer, "xpath-query-path");
                crm_xml_add(parent, XML_ATTR_ID, path);
                free(path);

            } else if (*answer) {
                add_node_copy(*answer, match);

            } else {
                *answer = match;
            }

        } else if (safe_str_eq(op, CIB_OP_REPLACE)) {
            xmlNode *parent = match->parent;

            free_xml(match);
            if (input != NULL) {
                add_node_copy(parent, input);
            }

            if ((options & cib_multiple) == 0) {
                break;
            }
        }
    }

    freeXpathObject(xpathObj);
    return rc;
}
示例#10
0
static int
update_cib_object(xmlNode * parent, xmlNode * update)
{
    int result = pcmk_ok;
    xmlNode *target = NULL;
    xmlNode *a_child = NULL;
    const char *replace = NULL;
    const char *object_id = NULL;
    const char *object_name = NULL;

    CRM_CHECK(update != NULL, return -EINVAL);
    CRM_CHECK(parent != NULL, return -EINVAL);

    object_name = crm_element_name(update);
    CRM_CHECK(object_name != NULL, return -EINVAL);

    object_id = ID(update);
    crm_trace("Processing: <%s id=%s>", crm_str(object_name), crm_str(object_id));

    if (object_id == NULL) {
        /*  placeholder object */
        target = find_xml_node(parent, object_name, FALSE);

    } else {
        target = find_entity(parent, object_name, object_id);
    }

    if (target == NULL) {
        target = create_xml_node(parent, object_name);
    }

    crm_trace("Found node <%s id=%s> to update", crm_str(object_name), crm_str(object_id));

    replace = crm_element_value(update, XML_CIB_ATTR_REPLACE);
    if (replace != NULL) {
        xmlNode *remove = NULL;
        int last = 0, lpc = 0, len = 0;

        len = strlen(replace);
        while (lpc <= len) {
            if (replace[lpc] == ',' || replace[lpc] == 0) {
                char *replace_item = NULL;

                if (last == lpc) {
                    /* nothing to do */
                    last = lpc + 1;
                    goto incr;
                }

                replace_item = calloc(1, lpc - last + 1);
                memcpy(replace_item, replace + last, lpc - last);

                remove = find_xml_node(target, replace_item, FALSE);
                if (remove != NULL) {
                    crm_trace("Replacing node <%s> in <%s>",
                              replace_item, crm_element_name(target));
                    free_xml(remove);
                    remove = NULL;
                }
                free(replace_item);
                last = lpc + 1;
            }
  incr:
            lpc++;
        }
        xml_remove_prop(update, XML_CIB_ATTR_REPLACE);
        xml_remove_prop(target, XML_CIB_ATTR_REPLACE);
    }

    copy_in_properties(target, update);

    crm_trace("Processing children of <%s id=%s>", crm_str(object_name), crm_str(object_id));

    for (a_child = __xml_first_child(update); a_child != NULL; a_child = __xml_next(a_child)) {
        int tmp_result = 0;

        crm_trace("Updating child <%s id=%s>", crm_element_name(a_child), ID(a_child));

        tmp_result = update_cib_object(target, a_child);

        /*  only the first error is likely to be interesting */
        if (tmp_result != pcmk_ok) {
            crm_err("Error updating child <%s id=%s>", crm_element_name(a_child), ID(a_child));

            if (result == pcmk_ok) {
                result = tmp_result;
            }
        }
    }

    crm_trace("Finished with <%s id=%s>", crm_str(object_name), crm_str(object_id));

    return result;
}
gboolean
cib_action_update(crm_action_t *action, int status, int op_rc)
{
	char *op_id  = NULL;
	char *code   = NULL;
	char *digest = NULL;
	xmlNode *tmp      = NULL;
	xmlNode *params   = NULL;
	xmlNode *state    = NULL;
	xmlNode *rsc      = NULL;
	xmlNode *xml_op   = NULL;
	xmlNode *action_rsc = NULL;

	enum cib_errors rc = cib_ok;

	const char *name   = NULL;
	const char *value  = NULL;
	const char *rsc_id = NULL;
	const char *task   = crm_element_value(action->xml, XML_LRM_ATTR_TASK);
	const char *target = crm_element_value(action->xml, XML_LRM_ATTR_TARGET);
	const char *task_uuid = crm_element_value(action->xml, XML_LRM_ATTR_TASK_KEY);
	const char *target_uuid = crm_element_value(action->xml, XML_LRM_ATTR_TARGET_UUID);

	int call_options = cib_quorum_override|cib_scope_local;

	if(status == LRM_OP_PENDING) {
	    crm_debug("%s %d: Recording pending operation %s on %s",
		     crm_element_name(action->xml), action->id, task_uuid, target);
	} else {
	    crm_warn("%s %d: %s on %s timed out",
		     crm_element_name(action->xml), action->id, task_uuid, target);
	}
	
	action_rsc = find_xml_node(action->xml, XML_CIB_TAG_RESOURCE, TRUE);
	if(action_rsc == NULL) {
		return FALSE;
	}
	
	rsc_id = ID(action_rsc);
	CRM_CHECK(rsc_id != NULL,
		  crm_log_xml_err(action->xml, "Bad:action");
		  return FALSE);
	
/*
  update the CIB

<node_state id="hadev">
      <lrm>
        <lrm_resources>
          <lrm_resource id="rsc2" last_op="start" op_code="0" target="hadev"/>
*/

	state = create_xml_node(NULL, XML_CIB_TAG_STATE);

	crm_xml_add(state, XML_ATTR_UUID,  target_uuid);
	crm_xml_add(state, XML_ATTR_UNAME, target);
	
	rsc = create_xml_node(state, XML_CIB_TAG_LRM);
	crm_xml_add(rsc, XML_ATTR_ID, target_uuid);

	rsc = create_xml_node(rsc,   XML_LRM_TAG_RESOURCES);
	rsc = create_xml_node(rsc,   XML_LRM_TAG_RESOURCE);
	crm_xml_add(rsc, XML_ATTR_ID, rsc_id);

	name = XML_ATTR_TYPE;
	value = crm_element_value(action_rsc, name);
	crm_xml_add(rsc, name, value);
	name = XML_AGENT_ATTR_CLASS;
	value = crm_element_value(action_rsc, name);
	crm_xml_add(rsc, name, value);
	name = XML_AGENT_ATTR_PROVIDER;
	value = crm_element_value(action_rsc, name);
	crm_xml_add(rsc, name, value);

	xml_op = create_xml_node(rsc, XML_LRM_TAG_RSC_OP);	
	crm_xml_add(xml_op, XML_ATTR_ID, task);
	
	op_id = generate_op_key(rsc_id, task, action->interval);
	crm_xml_add(xml_op, XML_ATTR_ID, op_id);
	crm_free(op_id);
	
	crm_xml_add_int(xml_op, XML_LRM_ATTR_CALLID, -1);
	crm_xml_add(xml_op, XML_LRM_ATTR_TASK, task);
	crm_xml_add(xml_op, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
	crm_xml_add_int(xml_op, XML_LRM_ATTR_OPSTATUS, status);
	crm_xml_add_int(xml_op, XML_LRM_ATTR_INTERVAL, action->interval);
	crm_xml_add_int(xml_op, XML_LRM_ATTR_RC, op_rc);
	crm_xml_add(xml_op, XML_ATTR_ORIGIN, __FUNCTION__);

	if(crm_str_eq(task, CRMD_ACTION_MIGRATED, TRUE)) {
	    char *key = crm_meta_name("migrate_source_uuid");
	    xmlNode *attrs = first_named_child(action->xml, XML_TAG_ATTRS);
	    const char *host = crm_element_value(attrs, key);
	    CRM_CHECK(host != NULL, crm_log_xml_err(action->xml, "Bad Op"));
	    crm_xml_add(xml_op, CRMD_ACTION_MIGRATED, host);
	    crm_free(key);
	}	
	
	code = generate_transition_key(
	    transition_graph->id, action->id, get_target_rc(action), te_uuid);
	crm_xml_add(xml_op, XML_ATTR_TRANSITION_KEY, code);
	crm_free(code);

	code = generate_transition_magic(
		crm_element_value(xml_op, XML_ATTR_TRANSITION_KEY), status, op_rc);
	crm_xml_add(xml_op,  XML_ATTR_TRANSITION_MAGIC, code);
	crm_free(code);

	tmp = find_xml_node(action->xml, "attributes", TRUE);
	params = create_xml_node(NULL, XML_TAG_PARAMS);
	copy_in_properties(params, tmp);
	
	filter_action_parameters(params, CRM_FEATURE_SET);
	digest = calculate_xml_digest(params, TRUE, FALSE);

	/* info for now as this area has been problematic to debug */
	crm_debug("Calculated digest %s for %s (%s)\n", 
		  digest, ID(xml_op),
		  crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC));
	crm_log_xml(LOG_DEBUG,  "digest:source", params);

	crm_xml_add(xml_op, XML_LRM_ATTR_OP_DIGEST, digest);
	crm_free(digest);
	free_xml(params);
	
	crm_debug_3("Updating CIB with \"%s\" (%s): %s %s on %s",
		  status<0?"new action":XML_ATTR_TIMEOUT,
		  crm_element_name(action->xml), crm_str(task), rsc_id, target);
	
	rc = fsa_cib_conn->cmds->update(
		fsa_cib_conn, XML_CIB_TAG_STATUS, state, call_options);

	crm_debug_2("Updating CIB with %s action %d: %s on %s (call_id=%d)",
		  op_status2text(status), action->id, task_uuid, target, rc);

	add_cib_op_callback(fsa_cib_conn, rc, FALSE, NULL, cib_action_updated);
	free_xml(state);

	action->sent_update = TRUE;
	
	if(rc < cib_ok) {
		return FALSE;
	}

	return TRUE;
}
示例#12
0
enum cib_errors 
cib_process_change(
	const char *op, int options, const char *section, crm_data_t *input,
	crm_data_t *existing_cib, crm_data_t **result_cib, crm_data_t **answer)
{
	gboolean verbose = FALSE;
	crm_data_t *failed = NULL;
	enum cib_errors result = cib_ok;
	int cib_update_op = CIB_UPDATE_OP_NONE;

	crm_debug_2("Processing \"%s\" event for section=%s", op, crm_str(section));


	if (strcasecmp(CIB_OP_CREATE, op) == 0) {
		cib_update_op = CIB_UPDATE_OP_ADD;
		
	} else if (strcasecmp(CIB_OP_UPDATE, op) == 0) {
		cib_update_op = CIB_UPDATE_OP_MODIFY;
		
	} else if (strcasecmp(CIB_OP_DELETE_ALT, op) == 0) {
		cib_update_op = CIB_UPDATE_OP_DELETE;
		
	} else {
		crm_err("Incorrect request handler invoked for \"%s\" op",
			crm_str(op));
		return cib_operation;
	}

	result = cib_ok;
	if (options & cib_verbose) {
		verbose = TRUE;
	}
	
	if(safe_str_eq(XML_CIB_TAG_SECTION_ALL, section)) {
		section = NULL;
	}

	if(input == NULL) {
		crm_err("Cannot perform modification with no data");
		return cib_NOOBJECT;
	}
	
	crm_validate_data(input);
	crm_validate_data(*result_cib);
	failed = create_xml_node(NULL, XML_TAG_FAILED);
	
	/* make changes to a temp copy then activate */
	if(section == NULL) {
		int lpc = 0;
		const char *type = NULL;
		crm_data_t *sub_input = NULL;

		/* order is no longer important here */
		const char *type_list[] = {
			XML_CIB_TAG_NODES,
			XML_CIB_TAG_CONSTRAINTS,
			XML_CIB_TAG_RESOURCES,
			XML_CIB_TAG_STATUS,
			XML_CIB_TAG_CRMCONFIG
		};

		copy_in_properties(*result_cib, input);
	
		for(lpc = 0; lpc < DIMOF(type_list); lpc++) {
			type = type_list[lpc];
	
			if(result == cib_ok) {
				crm_debug_2("Processing section=%s", type);
				sub_input = get_object_root(type, input);
				result = updateList(
					*result_cib, sub_input, failed,
					cib_update_op, type);
			}
		}

	} else {
		result = updateList(
			*result_cib, input, failed, cib_update_op, section);
	}

	if (result != cib_ok || xml_has_children(failed)) {
		if(result == cib_ok) {
			result = cib_unknown;
		}
		crm_log_xml_err(failed, "CIB Update failures");
		*answer = failed;
	} else {
		free_xml(failed);
	}

	return result;
}