Page MenuHomeClusterLabs Projects

No OneTemporary

diff --git a/include/crm/common/output_internal.h b/include/crm/common/output_internal.h
index 2e1a17dd59..de2defff1c 100644
--- a/include/crm/common/output_internal.h
+++ b/include/crm/common/output_internal.h
@@ -1,753 +1,767 @@
/*
* Copyright 2019-2020 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU Lesser General Public License
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#ifndef CRM_OUTPUT__H
# define CRM_OUTPUT__H
#ifdef __cplusplus
extern "C" {
#endif
/**
* \file
* \brief Formatted output for pacemaker tools
*/
# include <stdbool.h>
# include <stdio.h>
# include <libxml/tree.h>
# include <libxml/HTMLtree.h>
# include <glib.h>
# include <crm/common/results.h>
# define PCMK__API_VERSION "2.2"
#if defined(PCMK__WITH_ATTRIBUTE_OUTPUT_ARGS)
# define PCMK__OUTPUT_ARGS(ARGS...) __attribute__((output_args(ARGS)))
#else
# define PCMK__OUTPUT_ARGS(ARGS...)
#endif
typedef struct pcmk__output_s pcmk__output_t;
/*!
* \internal
* \brief The type of a function that creates a ::pcmk__output_t.
*
* Instances of this type are passed to pcmk__register_format(), stored in an
* internal data structure, and later accessed by pcmk__output_new(). For
* examples, see pcmk__mk_xml_output() and pcmk__mk_text_output().
*
* \param[in] argv The list of command line arguments.
*/
typedef pcmk__output_t * (*pcmk__output_factory_t)(char **argv);
/*!
* \internal
* \brief The type of a custom message formatting function.
*
* These functions are defined by various libraries to support formatting of
* types aside from the basic types provided by a ::pcmk__output_t.
*
* The meaning of the return value will be different for each message.
* In general, however, 0 should be returned on success and a positive value
* on error.
*
* \note These functions must not call va_start or va_end - that is done
* automatically before the custom formatting function is called.
*/
typedef int (*pcmk__message_fn_t)(pcmk__output_t *out, va_list args);
/*!
* \internal
* \brief Internal type for tracking custom messages.
*
* Each library can register functions that format custom message types. These
* are commonly used to handle some library-specific type. Registration is
* done by first defining a table of ::pcmk__message_entry_t structures and
* then passing that table to pcmk__register_messages(). Separate handlers
* can be defined for the same message, but for different formats (xml vs.
* text). Unknown formats will be ignored.
*
* Additionally, a "default" value for fmt_table can be used. In this case,
* fn will be registered for all supported formats. It is also possible to
* register a default and then override that registration with a format-specific
* function if necessary.
*
* \note The ::pcmk__message_entry_t table is processed in one pass, in order,
* from top to bottom. This means later entries with the same message_id will
* override previous ones. Thus, any default entry must come before any
* format-specific entries for the same message_id.
*/
typedef struct pcmk__message_entry_s {
/*!
* \brief The message to be handled.
*
* This must be the same ID that is passed to the message function of
* a ::pcmk__output_t. Unknown message IDs will be ignored.
*/
const char *message_id;
/*!
* \brief The format type this handler is for.
*
* This name must match the fmt_name of the currently active formatter in
* order for the registered function to be called. It is valid to have
* multiple entries for the same message_id but with different fmt_name
* values.
*/
const char *fmt_name;
/*!
* \brief The function to be called for message_id given a match on
* fmt_name. See comments on ::pcmk__message_fn_t.
*/
pcmk__message_fn_t fn;
} pcmk__message_entry_t;
/*!
* \internal
* \brief This structure contains everything needed to add support for a
* single output formatter to a command line program.
*/
typedef struct pcmk__supported_format_s {
/*!
* \brief The name of this output formatter, which should match the
* fmt_name parameter in some ::pcmk__output_t structure.
*/
const char *name;
/*!
* \brief A function that creates a ::pcmk__output_t.
*/
pcmk__output_factory_t create;
/*!
* \brief Format-specific command line options. This can be NULL if
* no command line options should be supported.
*/
GOptionEntry *options;
} pcmk__supported_format_t;
/* The following three blocks need to be updated each time a new base formatter
* is added.
*/
extern GOptionEntry pcmk__html_output_entries[];
extern GOptionEntry pcmk__log_output_entries[];
extern GOptionEntry pcmk__none_output_entries[];
extern GOptionEntry pcmk__text_output_entries[];
extern GOptionEntry pcmk__xml_output_entries[];
pcmk__output_t *pcmk__mk_html_output(char **argv);
pcmk__output_t *pcmk__mk_log_output(char **argv);
pcmk__output_t *pcmk__mk_none_output(char **argv);
pcmk__output_t *pcmk__mk_text_output(char **argv);
pcmk__output_t *pcmk__mk_xml_output(char **argv);
#define PCMK__SUPPORTED_FORMAT_HTML { "html", pcmk__mk_html_output, pcmk__html_output_entries }
#define PCMK__SUPPORTED_FORMAT_LOG { "log", pcmk__mk_log_output, pcmk__log_output_entries }
#define PCMK__SUPPORTED_FORMAT_NONE { "none", pcmk__mk_none_output, pcmk__none_output_entries }
#define PCMK__SUPPORTED_FORMAT_TEXT { "text", pcmk__mk_text_output, pcmk__text_output_entries }
#define PCMK__SUPPORTED_FORMAT_XML { "xml", pcmk__mk_xml_output, pcmk__xml_output_entries }
/*!
* \brief This structure contains everything that makes up a single output
* formatter.
*
* Instances of this structure may be created by calling pcmk__output_new()
* with the name of the desired formatter. They should later be freed with
* pcmk__output_free().
*/
struct pcmk__output_s {
/*!
* \brief The name of this output formatter.
*/
const char *fmt_name;
/*!
- * \brief A copy of the request that generated this output.
+ * \brief Should this formatter supress most output?
*
- * In the case of command line usage, this would be the command line
- * arguments. For other use cases, it could be different.
+ * \note This setting is not respected by all formatters. In general,
+ * machine-readable output formats will not support this while
+ * user-oriented formats will. Callers should use is_quiet()
+ * to test whether to print or not.
*/
- gchar *request;
+ bool quiet;
/*!
- * \brief Does this formatter support a special quiet mode?
+ * \brief A copy of the request that generated this output.
*
- * In this mode, most output can be supressed but some information is still
- * displayed to an interactive user. In general, machine-readable output
- * formats will not support this while user-oriented formats will.
+ * In the case of command line usage, this would be the command line
+ * arguments. For other use cases, it could be different.
*/
- bool supports_quiet;
+ gchar *request;
/*!
* \brief Where output should be written.
*
* This could be a file handle, or stdout or stderr. This is really only
* useful internally.
*/
FILE *dest;
/*!
* \brief Custom messages that are currently registered on this formatter.
*
* Keys are the string message IDs, values are ::pcmk__message_fn_t function
* pointers.
*/
GHashTable *messages;
/*!
* \brief Implementation-specific private data.
*
* Each individual formatter may have some private data useful in its
* implementation. This points to that data. Callers should not rely on
* its contents or structure.
*/
void *priv;
/*!
* \internal
* \brief Take whatever actions are necessary to prepare out for use. This is
* called by pcmk__output_new(). End users should not need to call this.
*
* \note For formatted output implementers - This function should be written in
* such a way that it can be called repeatedly on an already initialized
* object without causing problems, or on a previously finished object
* without crashing.
*
* \param[in,out] out The output functions structure.
*
* \return true on success, false on error.
*/
bool (*init) (pcmk__output_t *out);
/*!
* \internal
* \brief Free the private formatter-specific data.
*
* This is called from pcmk__output_free() and does not typically need to be
* called directly.
*
* \param[in,out] out The output functions structure.
*/
- void (*free_priv)(pcmk__output_t *out);
+ void (*free_priv) (pcmk__output_t *out);
/*!
* \internal
* \brief Take whatever actions are necessary to end formatted output.
*
* This could include flushing output to a file, but does not include freeing
* anything. The finish method can potentially be fairly complicated, adding
* additional information to the internal data structures or doing whatever
* else. It is therefore suggested that finish only be called once.
*
* \note The print parameter will only affect those formatters that do all
* their output at the end. Console-oriented formatters typically print
* a line at a time as they go, so this parameter will not affect them.
* Structured formatters will honor it, however.
*
* \note The copy_dest parameter does not apply to all formatters. Console-
* oriented formatters do not build up a structure as they go, and thus
* do not have anything to return. Structured formatters will honor it,
* however. Note that each type of formatter will return a different
* type of value in this parameter. To use this parameter, call this
* function like so:
*
* \code
* xmlNode *dest = NULL;
* out->finish(out, exit_code, false, (void **) &dest);
* \endcode
*
* \param[in,out] out The output functions structure.
* \param[in] exit_status The exit value of the whole program.
* \param[in] print Whether this function should write any output.
* \param[out] copy_dest A destination to store a copy of the internal
* data structure for this output, or NULL if no
* copy is required. The caller should free this
* memory when done with it.
*/
void (*finish) (pcmk__output_t *out, crm_exit_t exit_status, bool print,
void **copy_dest);
/*!
* \internal
* \brief Finalize output and then immediately set back up to start a new set
* of output.
*
* This is conceptually the same as calling finish and then init, though in
* practice more be happening behind the scenes.
*
* \note This function differs from finish in that no exit_status is added.
* The idea is that the program is not shutting down, so there is not
* yet a final exit code. Call finish on the last time through if this
* is needed.
*
* \param[in,out] out The output functions structure.
*/
void (*reset) (pcmk__output_t *out);
/*!
* \internal
* \brief Register a custom message.
*
* \param[in,out] out The output functions structure.
* \param[in] message_id The name of the message to register. This name
* will be used as the message_id parameter to the
* message function in order to call the custom
* format function.
* \param[in] fn The custom format function to call for message_id.
*/
void (*register_message) (pcmk__output_t *out, const char *message_id,
pcmk__message_fn_t fn);
/*!
* \internal
* \brief Call a previously registered custom message.
*
* \param[in,out] out The output functions structure.
* \param[in] message_id The name of the message to call. This name must
* be the same as the message_id parameter of some
* previous call to register_message.
* \param[in] ... Arguments to be passed to the registered function.
*
* \return A standard Pacemaker return code. Generally: 0 if a function was
* registered for the message, that function was called, and returned
* successfully; EINVAL if no function was registered; or pcmk_rc_no_output
* if a function was called but produced no output.
*/
int (*message) (pcmk__output_t *out, const char *message_id, ...);
/*!
* \internal
* \brief Format the output of a completed subprocess.
*
* \param[in,out] out The output functions structure.
* \param[in] exit_status The exit value of the subprocess.
* \param[in] proc_stdout stdout from the completed subprocess.
* \param[in] proc_stderr stderr from the completed subprocess.
*/
void (*subprocess_output) (pcmk__output_t *out, int exit_status,
const char *proc_stdout, const char *proc_stderr);
/*!
* \internal
* \brief Format version information. This is useful for the --version
* argument of command line tools.
*
* \param[in,out] out The output functions structure.
* \param[in] extended Add additional version information.
*/
void (*version) (pcmk__output_t *out, bool extended);
/*!
* \internal
* \brief Format an informational message that should be shown to
* to an interactive user. Not all formatters will do this.
*
* \note A newline will automatically be added to the end of the format
* string, so callers should not include a newline.
*
* \param[in,out] out The output functions structure.
* \param[in] buf The message to be printed.
* \param[in] ... Arguments to be formatted.
*/
void (*info) (pcmk__output_t *out, const char *format, ...) G_GNUC_PRINTF(2, 3);
/*!
* \internal
* \brief Format an error message that should be shown to an interactive
* user. Not all formatters will do this.
*
* \note A newline will automatically be added to the end of the format
* string, so callers should not include a newline.
*
* \param[in,out] out The output functions structure.
* \param[in] buf The message to be printed.
* \param[in] ... Arguments to be formatted.
*/
void (*err) (pcmk__output_t *out, const char *format, ...) G_GNUC_PRINTF(2, 3);
/*!
* \internal
* \brief Format already formatted XML.
*
* \param[in,out] out The output functions structure.
* \param[in] name A name to associate with the XML.
* \param[in] buf The XML in a string.
*/
void (*output_xml) (pcmk__output_t *out, const char *name, const char *buf);
/*!
* \internal
* \brief Start a new list of items.
*
* \note For text output, this corresponds to another level of indentation. For
* XML output, this corresponds to wrapping any following output in another
* layer of tags.
*
* \note If singular_noun and plural_noun are non-NULL, calling end_list will
* result in a summary being added.
*
* \param[in,out] out The output functions structure.
* \param[in] singular_noun When outputting the summary for a list with
* one item, the noun to use.
* \param[in] plural_noun When outputting the summary for a list with
* more than one item, the noun to use.
* \param[in] format The format string.
* \param[in] ... Arguments to be formatted.
*/
void (*begin_list) (pcmk__output_t *out, const char *singular_noun,
const char *plural_noun, const char *format, ...)
G_GNUC_PRINTF(4, 5);
/*!
* \internal
* \brief Format a single item in a list.
*
* \param[in,out] out The output functions structure.
* \param[in] name A name to associate with this item.
* \param[in] format The format string.
* \param[in] ... Arguments to be formatted.
*/
void (*list_item) (pcmk__output_t *out, const char *name, const char *format, ...)
G_GNUC_PRINTF(3, 4);
/*!
* \internal
* \brief Increment the internal counter of the current list's length.
*
* Typically, this counter is maintained behind the scenes as a side effect
* of calling list_item(). However, custom functions that maintain lists
* some other way will need to manage this counter manually. This is
* useful for implementing custom message functions and should not be
* needed otherwise.
*
* \param[in,out] out The output functions structure.
*/
void (*increment_list) (pcmk__output_t *out);
/*!
* \internal
* \brief Conclude a list.
*
* \note If begin_list was called with non-NULL for both the singular_noun
* and plural_noun arguments, this function will output a summary.
* Otherwise, no summary will be added.
*
* \param[in,out] out The output functions structure.
*/
void (*end_list) (pcmk__output_t *out);
+
+ /*!
+ * \internal
+ * \brief Should anything be printed to the user?
+ *
+ * \note This takes into account both the \p quiet value as well as the
+ * current formatter.
+ *
+ * \param[in] out The output functions structure.
+ *
+ * \return true if output should be supressed, false otherwise.
+ */
+ bool (*is_quiet) (pcmk__output_t *out);
};
/*!
* \internal
* \brief Call a formatting function for a previously registered message.
*
* \note This function is for implementing custom formatters. It should not
* be called directly. Instead, call out->message.
*
* \param[in,out] out The output functions structure.
* \param[in] message_id The message to be handled. Unknown messages
* will be ignored.
* \param[in] ... Arguments to be passed to the registered function.
*/
int
pcmk__call_message(pcmk__output_t *out, const char *message_id, ...);
/*!
* \internal
* \brief Free a ::pcmk__output_t structure that was previously created by
* pcmk__output_new().
*
* \note While the create and finish functions are designed in such a way that
* they can be called repeatedly, this function will completely free the
* memory of the object. Once this function has been called, producing
* more output requires starting over from pcmk__output_new().
*
* \param[in,out] out The output structure.
*/
void pcmk__output_free(pcmk__output_t *out);
/*!
* \internal
* \brief Create a new ::pcmk__output_t structure.
*
* \param[in,out] out The destination of the new ::pcmk__output_t.
* \param[in] fmt_name How should output be formatted?
* \param[in] filename Where should formatted output be written to? This
* can be a filename (which will be overwritten if it
* already exists), or NULL or "-" for stdout. For no
* output, pass a filename of "/dev/null".
* \param[in] argv The list of command line arguments.
*
* \return Standard Pacemaker return code
*/
int pcmk__output_new(pcmk__output_t **out, const char *fmt_name,
const char *filename, char **argv);
/*!
* \internal
* \brief Register a new output formatter, making it available for use
* the same as a base formatter.
*
* \param[in,out] group A ::GOptionGroup that formatted output related command
* line arguments should be added to. This can be NULL
* for use outside of command line programs.
* \param[in] name The name of the format. This will be used to select a
* format from command line options and for displaying help.
* \param[in] create A function that creates a ::pcmk__output_t.
* \param[in] options Format-specific command line options. These will be
* added to the context. This argument can also be NULL.
*
* \return 0 on success or an error code on error.
*/
int
pcmk__register_format(GOptionGroup *group, const char *name,
pcmk__output_factory_t create, GOptionEntry *options);
/*!
* \internal
* \brief Register an entire table of output formatters at once.
*
* \param[in,out] group A ::GOptionGroup that formatted output related command
* line arguments should be added to. This can be NULL
* for use outside of command line programs.
* \param[in] table An array of ::pcmk__supported_format_t which should
* all be registered. This array must be NULL-terminated.
*
*/
void
pcmk__register_formats(GOptionGroup *group, pcmk__supported_format_t *table);
/*!
* \internal
* \brief Unregister a previously registered table of custom formatting
* functions and destroy the internal data structures associated with them.
*/
void
pcmk__unregister_formats(void);
/*!
* \internal
* \brief Register a function to handle a custom message.
*
* \note This function is for implementing custom formatters. It should not
* be called directly. Instead, call out->register_message.
*
* \param[in,out] out The output functions structure.
* \param[in] message_id The message to be handled.
* \param[in] fn The custom format function to call for message_id.
*/
void
pcmk__register_message(pcmk__output_t *out, const char *message_id,
pcmk__message_fn_t fn);
/*!
* \internal
* \brief Register an entire table of custom formatting functions at once.
*
* This table can contain multiple formatting functions for the same message ID
* if they are for different format types.
*
* \param[in,out] out The output functions structure.
* \param[in] table An array of ::pcmk__message_entry_t values which should
* all be registered. This array must be NULL-terminated.
*/
void
pcmk__register_messages(pcmk__output_t *out, pcmk__message_entry_t *table);
/* Functions that are useful for implementing custom message formatters */
/*!
* \internal
* \brief A printf-like function.
*
* This function writes to out->dest and indents the text to the current level
* of the text formatter's nesting. This should be used when implementing
* custom message functions instead of printf.
*
* \param[in,out] out The output functions structure.
*/
void
pcmk__indented_printf(pcmk__output_t *out, const char *format, ...) G_GNUC_PRINTF(2, 3);
/*!
* \internal
* \brief A vprintf-like function.
*
* This function is like pcmk__indented_printf(), except it takes a va_list instead
* of a list of arguments. This should be used when implementing custom message
* functions instead of vprintf.
*
* \param[in,out] out The output functions structure.
* \param[in] format The format string.
* \param[in] args A list of arguments to apply to the format string.
*/
void
pcmk__indented_vprintf(pcmk__output_t *out, const char *format, va_list args) G_GNUC_PRINTF(2, 0);
/*!
* \internal
* \brief Create and return a new XML node with the given name, as a child of the
* current list parent. The new node is then added as the new list parent,
* meaning all subsequent nodes will be its children. This is used when
* implementing custom functions.
*
* \param[in,out] out The output functions structure.
* \param[in] name The name of the node to be created.
*/
xmlNodePtr
pcmk__output_xml_create_parent(pcmk__output_t *out, const char *name);
/*!
* \internal
* \brief Add the given node as a child of the current list parent. This is
* used when implementing custom message functions.
*
* \param[in,out] out The output functions structure.
* \param[in] node An XML node to be added as a child.
*/
void
pcmk__output_xml_add_node(pcmk__output_t *out, xmlNodePtr node);
/*!
* \internal
* \brief Create and return a new XML node with the given name, as a child of the
* current list parent. This is used when implementing custom functions.
*
* \param[in,out] out The output functions structure.
* \param[in] name The name of the node to be created.
*/
xmlNodePtr
pcmk__output_create_xml_node(pcmk__output_t *out, const char *name);
/*!
* \internal
* \brief Like pcmk__output_create_xml_node(), but add the given text content to the
* new node.
*
* \param[in,out] out The output functions structure.
* \param[in] name The name of the node to be created.
* \param[in] content The text content of the node.
*/
xmlNodePtr
pcmk__output_create_xml_text_node(pcmk__output_t *out, const char *name, const char *content);
/*!
* \internal
* \brief Push a parent XML node onto the stack. This is used when implementing
* custom message functions.
*
* The XML output formatter maintains an internal stack to keep track of which nodes
* are parents in order to build up the tree structure. This function can be used
* to temporarily push a new node onto the stack. After calling this function, any
* other formatting functions will have their nodes added as children of this new
* parent.
*
* \param[in,out] out The output functions structure.
* \param[in] node The node to be added/
*/
void
pcmk__output_xml_push_parent(pcmk__output_t *out, xmlNodePtr node);
/*!
* \internal
* \brief Pop a parent XML node onto the stack. This is used when implementing
* custom message functions.
*
* This function removes a parent node from the stack. See pcmk__xml_push_parent()
* for more details.
*
* \note Little checking is done with this function. Be sure you only pop parents
* that were previously pushed. In general, it is best to keep the code between
* push and pop simple.
*
* \param[in,out] out The output functions structure.
*/
void
pcmk__output_xml_pop_parent(pcmk__output_t *out);
/*!
* \internal
* \brief Peek a parent XML node onto the stack. This is used when implementing
* custom message functions.
*
* This function peeks a parent node on stack. See pcmk__xml_push_parent()
* for more details. It has no side-effect and can be called for an empty stack.
*
* \note Little checking is done with this function.
*
* \param[in,out] out The output functions structure.
*
* \return NULL if stack is empty, otherwise the parent of the stack.
*/
xmlNodePtr
pcmk__output_xml_peek_parent(pcmk__output_t *out);
/*!
* \internal
* \brief Create a new XML node consisting of the provided text inside an HTML
* element node of the given name.
*
* \param[in,out] out The output functions structure.
* \param[in] element_name The name of the new HTML element.
* \param[in] id The CSS ID selector to apply to this element.
* If NULL, no ID is added.
* \param[in] class_name The CSS class selector to apply to this element.
* If NULL, no class is added.
* \param[in] text The text content of the node.
*/
xmlNodePtr
pcmk__output_create_html_node(pcmk__output_t *out, const char *element_name, const char *id,
const char *class_name, const char *text);
/*!
* \internal
* \brief Add an HTML tag to the <head> section.
*
* The arguments after name are a NULL-terminated list of keys and values,
* all of which will be added as attributes to the given tag. For instance,
* the following code would generate the tag "<meta http-equiv='refresh' content='19'>":
*
* \code
* pcmk__html_add_header("meta", "http-equiv", "refresh", "content", "19", NULL);
* \endcode
*
* \param[in] name The HTML tag for the new node.
* \param[in] ... A NULL-terminated key/value list of attributes.
*/
void
pcmk__html_add_header(const char *name, ...)
G_GNUC_NULL_TERMINATED;
#define PCMK__OUTPUT_SPACER_IF(out_obj, cond) \
if (cond) { \
out_obj->info(out_obj, "%s", ""); \
}
#define PCMK__OUTPUT_LIST_HEADER(out_obj, cond, retcode, title...) \
if (retcode == pcmk_rc_no_output) { \
PCMK__OUTPUT_SPACER_IF(out_obj, cond); \
retcode = pcmk_rc_ok; \
out_obj->begin_list(out_obj, NULL, NULL, title); \
}
#define PCMK__OUTPUT_LIST_FOOTER(out_obj, retcode) \
if (retcode == pcmk_rc_ok) { \
out_obj->end_list(out_obj); \
}
#ifdef __cplusplus
}
#endif
#endif
diff --git a/include/pcmki/pcmki_fence.h b/include/pcmki/pcmki_fence.h
index 4b8a128ba2..241d0307b0 100644
--- a/include/pcmki/pcmki_fence.h
+++ b/include/pcmki/pcmki_fence.h
@@ -1,223 +1,222 @@
/*
* Copyright 2019-2020 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU Lesser General Public License
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#ifndef PCMKI_STONITH_H
# define PCMKI_STONITH_H
# include <crm/stonith-ng.h>
# include <crm/common/output_internal.h>
/*!
* \brief Perform a STONITH action.
*
* \note This is the internal version of pcmk_fence_action(). External users
* of the pacemaker API should use that function instead.
*
* \param[in] st A connection to the STONITH API.
* \param[in] target The node receiving the action.
* \param[in] action The action to perform.
* \param[in] name Who requested the fence action?
* \param[in] timeout How long to wait for the operation to complete (in ms).
* \param[in] tolerance If a successful action for \p target happened within
* this many ms, return 0 without performing the action
* again.
* \param[in] delay Apply a fencing delay. Value -1 means disable also any
* static/random fencing delays from pcmk_delay_base/max
*
* \return Standard Pacemaker return code
*/
int pcmk__fence_action(stonith_t *st, const char *target, const char *action,
const char *name, unsigned int timeout, unsigned int tolerance,
int delay);
/*!
* \brief List the fencing operations that have occurred for a specific node.
*
* \note This is the internal version of pcmk_fence_history(). External users
* of the pacemaker API should use that function instead.
*
* \note \p out should be initialized with pcmk__output_new() before calling this
* function and destroyed with out->finish and pcmk__output_free() before
* reusing it with any other functions in this library.
*
* \param[in,out] out The output functions structure.
* \param[in] st A connection to the STONITH API.
* \param[in] target The node to get history for.
* \param[in] timeout How long to wait for the operation to complete (in ms).
- * \param[in] quiet Suppress most output.
* \param[in] verbose Include additional output.
* \param[in] broadcast Gather fencing history from all nodes.
* \param[in] cleanup Clean up fencing history after listing.
*
* \return Standard Pacemaker return code
*/
int pcmk__fence_history(pcmk__output_t *out, stonith_t *st, char *target,
- unsigned int timeout, bool quiet, int verbose,
- bool broadcast, bool cleanup);
+ unsigned int timeout, int verbose, bool broadcast,
+ bool cleanup);
/**
* \brief List all installed STONITH agents.
*
* \note This is the internal version of pcmk_fence_installed(). External users
* of the pacemaker API should use that function instead.
*
* \note \p out should be initialized with pcmk__output_new() before calling this
* function and destroyed with out->finish and pcmk__output_free() before
* reusing it with any other functions in this library.
*
* \param[in,out] out The output functions structure.
* \param[in] st A connection to the STONITH API.
* \param[in] timeout How long to wait for the operation to complete (in ms).
*
* \return Standard Pacemaker return code
*/
int pcmk__fence_installed(pcmk__output_t *out, stonith_t *st, unsigned int timeout);
/*!
* \brief When was a device last fenced?
*
* \note This is the internal version of pcmk_fence_last(). External users
* of the pacemaker API should use that function instead.
*
* \note \p out should be initialized with pcmk__output_new() before calling this
* function and destroyed with out->finish and pcmk__output_free() before
* reusing it with any other functions in this library.
*
* \param[in,out] out The output functions structure.
* \param[in] target The node that was fenced.
* \param[in] as_nodeid
*
* \return Standard Pacemaker return code
*/
int pcmk__fence_last(pcmk__output_t *out, const char *target, bool as_nodeid);
/*!
* \brief List nodes that can be fenced.
*
* \note This is the internal version of pcmk_fence_list_targets(). External users
* of the pacemaker API should use that function instead.
*
* \note \p out should be initialized with pcmk__output_new() before calling this
* function and destroyed with out->finish and pcmk__output_free() before
* reusing it with any other functions in this library.
*
* \param[in,out] out The output functions structure
* \param[in] st A connection to the STONITH API
* \param[in] device_id Resource ID of fence device to check
* \param[in] timeout How long to wait for the operation to complete (in ms)
*
* \return Standard Pacemaker return code
*/
int pcmk__fence_list_targets(pcmk__output_t *out, stonith_t *st,
const char *device_id, unsigned int timeout);
/*!
* \brief Get metadata for a resource.
*
* \note This is the internal version of pcmk_fence_metadata(). External users
* of the pacemaker API should use that function instead.
*
* \note \p out should be initialized with pcmk__output_new() before calling this
* function and destroyed with out->finish and pcmk__output_free() before
* reusing it with any other functions in this library.
*
* \param[in,out] out The output functions structure.
* \param[in] st A connection to the STONITH API.
* \param[in] agent The fence agent to get metadata for.
* \param[in] timeout How long to wait for the operation to complete (in ms).
*
* \return Standard Pacemaker return code
*/
int pcmk__fence_metadata(pcmk__output_t *out, stonith_t *st, char *agent,
unsigned int timeout);
/*!
* \brief List registered fence devices.
*
* \note This is the internal version of pcmk_fence_metadata(). External users
* of the pacemaker API should use that function instead.
*
* \note \p out should be initialized with pcmk__output_new() before calling this
* function and destroyed with out->finish and pcmk__output_free() before
* reusing it with any other functions in this library.
*
* \param[in,out] out The output functions structure.
* \param[in] st A connection to the STONITH API.
* \param[in] target If not NULL, only return devices that can fence
* this node.
* \param[in] timeout How long to wait for the operation to complete (in ms).
*
* \return Standard Pacemaker return code
*/
int pcmk__fence_registered(pcmk__output_t *out, stonith_t *st, char *target,
unsigned int timeout);
/*!
* \brief Register a fencing level for a specific node, node regex, or attribute.
*
* \note This is the internal version of pcmk_fence_register_level(). External users
* of the pacemaker API should use that function instead.
*
* \p target can take three different forms:
* - name=value, in which case \p target is an attribute.
* - @pattern, in which case \p target is a node regex.
* - Otherwise, \p target is a node name.
*
* \param[in] st A connection to the STONITH API.
* \param[in] target The object to register a fencing level for.
* \param[in] fence_level Index number of level to add.
* \param[in] devices Devices to use in level.
*
* \return Standard Pacemaker return code
*/
int pcmk__fence_register_level(stonith_t *st, char *target, int fence_level,
stonith_key_value_t *devices);
/*!
* \brief Unregister a fencing level for a specific node, node regex, or attribute.
*
* \note This is the internal version of pcmk_fence_unregister_level(). External users
* of the pacemaker API should use that function instead.
*
* \p target can take three different forms:
* - name=value, in which case \p target is an attribute.
* - @pattern, in which case \p target is a node regex.
* - Otherwise, \p target is a node name.
*
* \param[in] st A connection to the STONITH API.
* \param[in] target The object to unregister a fencing level for.
* \param[in] fence_level Index number of level to remove.
*
* \return Standard Pacemaker return code
*/
int pcmk__fence_unregister_level(stonith_t *st, char *target, int fence_level);
/**
* \brief Validate a STONITH device configuration.
*
* \note This is the internal version of pcmk_stonith_validate(). External users
* of the pacemaker API should use that function instead.
*
* \note \p out should be initialized with pcmk__output_new() before calling this
* function and destroyed with out->finish and pcmk__output_free() before
* reusing it with any other functions in this library.
*
* \param[in,out] out The output functions structure.
* \param[in] st A connection to the STONITH API.
* \param[in] agent The agent to validate (for example, "fence_xvm").
* \param[in] id STONITH device ID (may be NULL).
* \param[in] params STONITH device configuration parameters.
* \param[in] timeout How long to wait for the operation to complete (in ms).
*
* \return Standard Pacemaker return code
*/
int pcmk__fence_validate(pcmk__output_t *out, stonith_t *st, const char *agent,
const char *id, stonith_key_value_t *params,
unsigned int timeout);
#endif
diff --git a/lib/common/output.c b/lib/common/output.c
index 88733e6019..1c7ee7f44a 100644
--- a/lib/common/output.c
+++ b/lib/common/output.c
@@ -1,151 +1,153 @@
/*
* Copyright 2019-2020 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU Lesser General Public License
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#include <libxml/tree.h>
#include <crm/common/util.h>
#include <crm/common/xml.h>
#include <crm/common/internal.h>
#include <crm/common/output_internal.h>
#include <crm/common/strings_internal.h>
#include <libxml/tree.h>
static GHashTable *formatters = NULL;
void
pcmk__output_free(pcmk__output_t *out) {
out->free_priv(out);
if (out->messages != NULL) {
g_hash_table_destroy(out->messages);
}
g_free(out->request);
free(out);
}
int
pcmk__output_new(pcmk__output_t **out, const char *fmt_name, const char *filename,
char **argv) {
pcmk__output_factory_t create = NULL;;
if (formatters == NULL) {
return EINVAL;
}
/* If no name was given, just try "text". It's up to each tool to register
* what it supports so this also may not be valid.
*/
if (fmt_name == NULL) {
create = g_hash_table_lookup(formatters, "text");
} else {
create = g_hash_table_lookup(formatters, fmt_name);
}
if (create == NULL) {
return pcmk_rc_unknown_format;
}
*out = create(argv);
if (*out == NULL) {
return ENOMEM;
}
if (pcmk__str_eq(filename, "-", pcmk__str_null_matches)) {
(*out)->dest = stdout;
} else {
(*out)->dest = fopen(filename, "w");
if ((*out)->dest == NULL) {
return errno;
}
}
+ (*out)->quiet = false;
+
(*out)->messages = g_hash_table_new_full(crm_str_hash, g_str_equal, free, NULL);
if ((*out)->init(*out) == false) {
pcmk__output_free(*out);
return ENOMEM;
}
return pcmk_rc_ok;
}
int
pcmk__register_format(GOptionGroup *group, const char *name,
pcmk__output_factory_t create, GOptionEntry *options) {
if (create == NULL) {
return -EINVAL;
}
if (formatters == NULL) {
formatters = g_hash_table_new_full(crm_str_hash, g_str_equal, free, NULL);
}
if (options != NULL && group != NULL) {
g_option_group_add_entries(group, options);
}
g_hash_table_insert(formatters, strdup(name), create);
return 0;
}
void
pcmk__register_formats(GOptionGroup *group, pcmk__supported_format_t *formats) {
pcmk__supported_format_t *entry = NULL;
if (formats == NULL) {
return;
}
for (entry = formats; entry->name != NULL; entry++) {
pcmk__register_format(group, entry->name, entry->create, entry->options);
}
}
void
pcmk__unregister_formats() {
if (formatters != NULL) {
g_hash_table_destroy(formatters);
}
}
int
pcmk__call_message(pcmk__output_t *out, const char *message_id, ...) {
va_list args;
int rc = pcmk_rc_ok;
pcmk__message_fn_t fn;
fn = g_hash_table_lookup(out->messages, message_id);
if (fn == NULL) {
return EINVAL;
}
va_start(args, message_id);
rc = fn(out, args);
va_end(args);
return rc;
}
void
pcmk__register_message(pcmk__output_t *out, const char *message_id,
pcmk__message_fn_t fn) {
g_hash_table_replace(out->messages, strdup(message_id), fn);
}
void
pcmk__register_messages(pcmk__output_t *out, pcmk__message_entry_t *table) {
pcmk__message_entry_t *entry;
for (entry = table; entry->message_id != NULL; entry++) {
if (pcmk__strcase_any_of(entry->fmt_name, "default", out->fmt_name, NULL)) {
pcmk__register_message(out, entry->message_id, entry->fn);
}
}
}
diff --git a/lib/common/output_html.c b/lib/common/output_html.c
index cd169313c8..156887dd7b 100644
--- a/lib/common/output_html.c
+++ b/lib/common/output_html.c
@@ -1,439 +1,445 @@
/*
* Copyright 2019-2020 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU Lesser General Public License
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#include <ctype.h>
#include <libxml/HTMLtree.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <crm/crm.h>
#include <crm/common/output_internal.h>
#include <crm/common/xml.h>
static const char *stylesheet_default =
".bold { font-weight: bold }\n"
".maint { color: blue }\n"
".offline { color: red }\n"
".online { color: green }\n"
".rsc-failed { color: red }\n"
".rsc-failure-ignored { color: yellow }\n"
".rsc-managed { color: yellow }\n"
".rsc-multiple { color: orange }\n"
".rsc-ok { color: green }\n"
".standby { color: orange }\n"
".warning { color: red, font-weight: bold }";
static gboolean cgi_output = FALSE;
static char *stylesheet_link = NULL;
static char *title = NULL;
static GSList *extra_headers = NULL;
GOptionEntry pcmk__html_output_entries[] = {
{ "html-cgi", 0, 0, G_OPTION_ARG_NONE, &cgi_output,
"Add CGI headers (requires --output-as=html)",
NULL },
{ "html-stylesheet", 0, 0, G_OPTION_ARG_STRING, &stylesheet_link,
"Link to an external stylesheet (requires --output-as=html)",
"URI" },
{ "html-title", 0, 0, G_OPTION_ARG_STRING, &title,
"Specify a page title (requires --output-as=html)",
"TITLE" },
{ NULL }
};
typedef struct private_data_s {
xmlNode *root;
GQueue *parent_q;
GSList *errors;
} private_data_t;
static void
html_free_priv(pcmk__output_t *out) {
private_data_t *priv = out->priv;
if (priv == NULL) {
return;
}
xmlFreeNode(priv->root);
g_queue_free(priv->parent_q);
g_slist_free(priv->errors);
free(priv);
out->priv = NULL;
}
static bool
html_init(pcmk__output_t *out) {
private_data_t *priv = NULL;
/* If html_init was previously called on this output struct, just return. */
if (out->priv != NULL) {
return true;
} else {
out->priv = calloc(1, sizeof(private_data_t));
if (out->priv == NULL) {
return false;
}
priv = out->priv;
}
priv->parent_q = g_queue_new();
priv->root = create_xml_node(NULL, "html");
xmlCreateIntSubset(priv->root->doc, (pcmkXmlStr) "html", NULL, NULL);
xmlSetProp(priv->root, (pcmkXmlStr) "lang", (pcmkXmlStr) "en");
g_queue_push_tail(priv->parent_q, priv->root);
priv->errors = NULL;
pcmk__output_xml_create_parent(out, "body");
return true;
}
static void
add_error_node(gpointer data, gpointer user_data) {
char *str = (char *) data;
pcmk__output_t *out = (pcmk__output_t *) user_data;
out->list_item(out, NULL, "%s", str);
}
static void
finish_reset_common(pcmk__output_t *out, crm_exit_t exit_status, bool print) {
private_data_t *priv = out->priv;
htmlNodePtr head_node = NULL;
htmlNodePtr charset_node = NULL;
if (cgi_output && print) {
fprintf(out->dest, "Content-Type: text/html\n\n");
}
/* Add the head node last - it's not needed earlier because it doesn't contain
* anything else that the user could add, and we want it done last to pick up
* any options that may have been given.
*/
head_node = xmlNewNode(NULL, (pcmkXmlStr) "head");
if (title != NULL ) {
pcmk_create_xml_text_node(head_node, "title", title);
} else if (out->request != NULL) {
pcmk_create_xml_text_node(head_node, "title", out->request);
}
charset_node = create_xml_node(head_node, "meta");
xmlSetProp(charset_node, (pcmkXmlStr) "charset", (pcmkXmlStr) "utf-8");
/* Add any extra header nodes the caller might have created. */
for (int i = 0; i < g_slist_length(extra_headers); i++) {
xmlAddChild(head_node, xmlCopyNode(g_slist_nth_data(extra_headers, i), 1));
}
/* Stylesheets are included two different ways. The first is via a built-in
* default (see the stylesheet_default const above). The second is via the
* html-stylesheet option, and this should obviously be a link to a
* stylesheet. The second can override the first. At least one should be
* given.
*/
pcmk_create_xml_text_node(head_node, "style", stylesheet_default);
if (stylesheet_link != NULL) {
htmlNodePtr link_node = create_xml_node(head_node, "link");
xmlSetProp(link_node, (pcmkXmlStr) "rel", (pcmkXmlStr) "stylesheet");
xmlSetProp(link_node, (pcmkXmlStr) "href", (pcmkXmlStr) stylesheet_link);
}
xmlAddPrevSibling(priv->root->children, head_node);
if (g_slist_length(priv->errors) > 0) {
out->begin_list(out, "Errors", NULL, NULL);
g_slist_foreach(priv->errors, add_error_node, (gpointer) out);
out->end_list(out);
}
if (print) {
htmlDocDump(out->dest, priv->root->doc);
}
}
static void
html_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest) {
private_data_t *priv = out->priv;
/* If root is NULL, html_init failed and we are being called from pcmk__output_free
* in the pcmk__output_new path.
*/
if (priv == NULL || priv->root == NULL) {
return;
}
finish_reset_common(out, exit_status, print);
if (copy_dest != NULL) {
*copy_dest = copy_xml(priv->root);
}
g_slist_free_full(extra_headers, (GDestroyNotify) xmlFreeNode);
}
static void
html_reset(pcmk__output_t *out) {
CRM_ASSERT(out != NULL);
out->dest = freopen(NULL, "w", out->dest);
CRM_ASSERT(out->dest != NULL);
if (out->priv != NULL) {
finish_reset_common(out, CRM_EX_OK, true);
}
html_free_priv(out);
html_init(out);
}
static void
html_subprocess_output(pcmk__output_t *out, int exit_status,
const char *proc_stdout, const char *proc_stderr) {
char *rc_buf = NULL;
private_data_t *priv = out->priv;
CRM_ASSERT(priv != NULL);
rc_buf = crm_strdup_printf("Return code: %d", exit_status);
pcmk__output_create_xml_text_node(out, "h2", "Command Output");
pcmk__output_create_html_node(out, "div", NULL, NULL, rc_buf);
if (proc_stdout != NULL) {
pcmk__output_create_html_node(out, "div", NULL, NULL, "Stdout");
pcmk__output_create_html_node(out, "div", NULL, "output", proc_stdout);
}
if (proc_stderr != NULL) {
pcmk__output_create_html_node(out, "div", NULL, NULL, "Stderr");
pcmk__output_create_html_node(out, "div", NULL, "output", proc_stderr);
}
free(rc_buf);
}
static void
html_version(pcmk__output_t *out, bool extended) {
private_data_t *priv = out->priv;
CRM_ASSERT(priv != NULL);
pcmk__output_create_xml_text_node(out, "h2", "Version Information");
pcmk__output_create_html_node(out, "div", NULL, NULL, "Program: Pacemaker");
pcmk__output_create_html_node(out, "div", NULL, NULL, crm_strdup_printf("Version: %s", PACEMAKER_VERSION));
pcmk__output_create_html_node(out, "div", NULL, NULL, "Author: Andrew Beekhof");
pcmk__output_create_html_node(out, "div", NULL, NULL, crm_strdup_printf("Build: %s", BUILD_VERSION));
pcmk__output_create_html_node(out, "div", NULL, NULL, crm_strdup_printf("Features: %s", CRM_FEATURES));
}
G_GNUC_PRINTF(2, 3)
static void
html_err(pcmk__output_t *out, const char *format, ...) {
private_data_t *priv = out->priv;
int len = 0;
char *buf = NULL;
va_list ap;
CRM_ASSERT(priv != NULL);
va_start(ap, format);
len = vasprintf(&buf, format, ap);
CRM_ASSERT(len >= 0);
va_end(ap);
priv->errors = g_slist_append(priv->errors, buf);
}
G_GNUC_PRINTF(2, 3)
static void
html_info(pcmk__output_t *out, const char *format, ...) {
/* This function intentially left blank */
}
static void
html_output_xml(pcmk__output_t *out, const char *name, const char *buf) {
htmlNodePtr node = NULL;
private_data_t *priv = out->priv;
CRM_ASSERT(priv != NULL);
node = pcmk__output_create_html_node(out, "pre", NULL, NULL, buf);
xmlSetProp(node, (pcmkXmlStr) "lang", (pcmkXmlStr) "xml");
}
G_GNUC_PRINTF(4, 5)
static void
html_begin_list(pcmk__output_t *out, const char *singular_noun,
const char *plural_noun, const char *format, ...) {
int q_len = 0;
private_data_t *priv = out->priv;
xmlNodePtr node = NULL;
CRM_ASSERT(priv != NULL);
/* If we are already in a list (the queue depth is always at least
* one because of the <html> element), first create a <li> element
* to hold the <h2> and the new list.
*/
q_len = g_queue_get_length(priv->parent_q);
if (q_len > 2) {
pcmk__output_xml_create_parent(out, "li");
}
if (format != NULL) {
va_list ap;
char *buf = NULL;
int len;
va_start(ap, format);
len = vasprintf(&buf, format, ap);
va_end(ap);
CRM_ASSERT(len >= 0);
if (q_len > 2) {
pcmk__output_create_xml_text_node(out, "h3", buf);
} else {
pcmk__output_create_xml_text_node(out, "h2", buf);
}
free(buf);
}
node = pcmk__output_xml_create_parent(out, "ul");
g_queue_push_tail(priv->parent_q, node);
}
G_GNUC_PRINTF(3, 4)
static void
html_list_item(pcmk__output_t *out, const char *name, const char *format, ...) {
private_data_t *priv = out->priv;
htmlNodePtr item_node = NULL;
va_list ap;
char *buf = NULL;
int len;
CRM_ASSERT(priv != NULL);
va_start(ap, format);
len = vasprintf(&buf, format, ap);
CRM_ASSERT(len >= 0);
va_end(ap);
item_node = pcmk__output_create_xml_text_node(out, "li", buf);
free(buf);
if (name != NULL) {
xmlSetProp(item_node, (pcmkXmlStr) "class", (pcmkXmlStr) name);
}
}
static void
html_increment_list(pcmk__output_t *out) {
/* This function intentially left blank */
}
static void
html_end_list(pcmk__output_t *out) {
private_data_t *priv = out->priv;
CRM_ASSERT(priv != NULL);
/* Remove the <ul> tag. */
g_queue_pop_tail(priv->parent_q);
pcmk__output_xml_pop_parent(out);
/* Remove the <li> created for nested lists. */
if (g_queue_get_length(priv->parent_q) > 2) {
pcmk__output_xml_pop_parent(out);
}
}
+static bool
+html_is_quiet(pcmk__output_t *out) {
+ return false;
+}
+
pcmk__output_t *
pcmk__mk_html_output(char **argv) {
pcmk__output_t *retval = calloc(1, sizeof(pcmk__output_t));
if (retval == NULL) {
return NULL;
}
retval->fmt_name = "html";
retval->request = argv == NULL ? NULL : g_strjoinv(" ", argv);
- retval->supports_quiet = false;
retval->init = html_init;
retval->free_priv = html_free_priv;
retval->finish = html_finish;
retval->reset = html_reset;
retval->register_message = pcmk__register_message;
retval->message = pcmk__call_message;
retval->subprocess_output = html_subprocess_output;
retval->version = html_version;
retval->info = html_info;
retval->err = html_err;
retval->output_xml = html_output_xml;
retval->begin_list = html_begin_list;
retval->list_item = html_list_item;
retval->increment_list = html_increment_list;
retval->end_list = html_end_list;
+ retval->is_quiet = html_is_quiet;
+
return retval;
}
xmlNodePtr
pcmk__output_create_html_node(pcmk__output_t *out, const char *element_name, const char *id,
const char *class_name, const char *text) {
htmlNodePtr node = pcmk__output_create_xml_text_node(out, element_name, text);
if (class_name != NULL) {
xmlSetProp(node, (pcmkXmlStr) "class", (pcmkXmlStr) class_name);
}
if (id != NULL) {
xmlSetProp(node, (pcmkXmlStr) "id", (pcmkXmlStr) id);
}
return node;
}
void
pcmk__html_add_header(const char *name, ...) {
htmlNodePtr header_node;
va_list ap;
va_start(ap, name);
header_node = xmlNewNode(NULL, (pcmkXmlStr) name);
while (1) {
char *key = va_arg(ap, char *);
char *value;
if (key == NULL) {
break;
}
value = va_arg(ap, char *);
xmlSetProp(header_node, (pcmkXmlStr) key, (pcmkXmlStr) value);
}
extra_headers = g_slist_append(extra_headers, header_node);
va_end(ap);
}
diff --git a/lib/common/output_log.c b/lib/common/output_log.c
index d6a3900bfd..fd13c89684 100644
--- a/lib/common/output_log.c
+++ b/lib/common/output_log.c
@@ -1,255 +1,261 @@
/*
* Copyright 2019-2020 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU Lesser General Public License
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#include <ctype.h>
#include <libxml/HTMLtree.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <crm/crm.h>
#include <crm/common/xml.h>
#include <crm/common/output_internal.h>
GOptionEntry pcmk__log_output_entries[] = {
{ NULL }
};
typedef struct private_data_s {
/* gathered in log_begin_list */
GQueue/*<char*>*/ *prefixes;
} private_data_t;
static void
log_subprocess_output(pcmk__output_t *out, int exit_status,
const char *proc_stdout, const char *proc_stderr) {
/* This function intentionally left blank */
}
static void
log_free_priv(pcmk__output_t *out) {
private_data_t *priv = out->priv;
if (priv == NULL) {
return;
}
g_queue_free(priv->prefixes);
free(priv);
out->priv = NULL;
}
static bool
log_init(pcmk__output_t *out) {
/* If log_init was previously called on this output struct, just return. */
if (out->priv != NULL) {
return true;
}
out->priv = calloc(1, sizeof(private_data_t));
if (out->priv == NULL) {
return false;
}
((private_data_t *)out->priv)->prefixes = g_queue_new();
return true;
}
static void
log_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest) {
/* This function intentionally left blank */
}
static void
log_reset(pcmk__output_t *out) {
CRM_ASSERT(out != NULL);
out->dest = freopen(NULL, "w", out->dest);
CRM_ASSERT(out->dest != NULL);
log_free_priv(out);
log_init(out);
}
static void
log_version(pcmk__output_t *out, bool extended) {
if (extended) {
crm_info("Pacemaker %s (Build: %s): %s",
PACEMAKER_VERSION, BUILD_VERSION, CRM_FEATURES);
} else {
crm_info("Pacemaker %s", PACEMAKER_VERSION);
crm_info("Written by Andrew Beekhof");
}
}
G_GNUC_PRINTF(2, 3)
static void
log_err(pcmk__output_t *out, const char *format, ...) {
va_list ap;
char* buffer = NULL;
int len = 0;
va_start(ap, format);
/* Informational output does not get indented, to separate it from other
* potentially indented list output.
*/
len = vasprintf(&buffer, format, ap);
CRM_ASSERT(len >= 0);
va_end(ap);
crm_err("%s", buffer);
free(buffer);
}
static void
log_output_xml(pcmk__output_t *out, const char *name, const char *buf) {
xmlNodePtr node = NULL;
private_data_t *priv = out->priv;
CRM_ASSERT(priv != NULL);
node = create_xml_node(NULL, name);
xmlNodeSetContent(node, (pcmkXmlStr) buf);
crm_log_xml_info(node, name);
free(node);
}
G_GNUC_PRINTF(4, 5)
static void
log_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plural_noun,
const char *format, ...) {
int len = 0;
va_list ap;
char* buffer = NULL;
private_data_t *priv = out->priv;
CRM_ASSERT(priv != NULL);
va_start(ap, format);
len = vasprintf(&buffer, format, ap);
CRM_ASSERT(len >= 0);
va_end(ap);
/* Don't skip empty prefixes,
* otherwise there will be mismatch
* in the log_end_list */
if(strcmp(buffer, "") == 0) {
/* nothing */
}
g_queue_push_tail(priv->prefixes, buffer);
}
G_GNUC_PRINTF(3, 4)
static void
log_list_item(pcmk__output_t *out, const char *name, const char *format, ...) {
int len = 0;
va_list ap;
private_data_t *priv = out->priv;
char prefix[LINE_MAX] = { 0 };
int offset = 0;
char* buffer = NULL;
CRM_ASSERT(priv != NULL);
for (GList* gIter = priv->prefixes->head; gIter; gIter = gIter->next) {
if (strcmp(prefix, "") != 0) {
offset += snprintf(prefix + offset, LINE_MAX - offset, ": %s", (char *)gIter->data);
} else {
offset = snprintf(prefix, LINE_MAX, "%s", (char *)gIter->data);
}
}
va_start(ap, format);
len = vasprintf(&buffer, format, ap);
CRM_ASSERT(len >= 0);
va_end(ap);
if (strcmp(buffer, "") != 0) { /* We don't want empty messages */
if ((name != NULL) && (strcmp(name, "") != 0)) {
if (strcmp(prefix, "") != 0) {
crm_info("%s: %s: %s", prefix, name, buffer);
} else {
crm_info("%s: %s", name, buffer);
}
} else {
if (strcmp(prefix, "") != 0) {
crm_info("%s: %s", prefix, buffer);
} else {
crm_info("%s", buffer);
}
}
}
free(buffer);
}
static void
log_end_list(pcmk__output_t *out) {
private_data_t *priv = out->priv;
CRM_ASSERT(priv != NULL);
if (priv->prefixes == NULL) {
return;
}
CRM_ASSERT(priv->prefixes->tail != NULL);
free((char *)priv->prefixes->tail->data);
g_queue_pop_tail(priv->prefixes);
}
G_GNUC_PRINTF(2, 3)
static void
log_info(pcmk__output_t *out, const char *format, ...) {
int len = 0;
va_list ap;
char* buffer = NULL;
va_start(ap, format);
len = vasprintf(&buffer, format, ap);
CRM_ASSERT(len >= 0);
va_end(ap);
crm_info("%s", buffer);
free(buffer);
}
+static bool
+log_is_quiet(pcmk__output_t *out) {
+ return false;
+}
+
pcmk__output_t *
pcmk__mk_log_output(char **argv) {
pcmk__output_t *retval = calloc(1, sizeof(pcmk__output_t));
if (retval == NULL) {
return NULL;
}
retval->fmt_name = "log";
retval->request = argv == NULL ? NULL : g_strjoinv(" ", argv);
- retval->supports_quiet = false;
retval->init = log_init;
retval->free_priv = log_free_priv;
retval->finish = log_finish;
retval->reset = log_reset;
retval->register_message = pcmk__register_message;
retval->message = pcmk__call_message;
retval->subprocess_output = log_subprocess_output;
retval->version = log_version;
retval->info = log_info;
retval->err = log_err;
retval->output_xml = log_output_xml;
retval->begin_list = log_begin_list;
retval->list_item = log_list_item;
retval->end_list = log_end_list;
+ retval->is_quiet = log_is_quiet;
+
return retval;
}
diff --git a/lib/common/output_none.c b/lib/common/output_none.c
index 04792e4966..3d1d8647dc 100644
--- a/lib/common/output_none.c
+++ b/lib/common/output_none.c
@@ -1,125 +1,131 @@
/*
* Copyright 2019-2020 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU Lesser General Public License
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#include <stdlib.h>
#include <glib.h>
#include <crm/crm.h>
#include <crm/common/output_internal.h>
GOptionEntry pcmk__none_output_entries[] = {
{ NULL }
};
static void
none_free_priv(pcmk__output_t *out) {
/* This function intentionally left blank */
}
static bool
none_init(pcmk__output_t *out) {
return true;
}
static void
none_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest) {
/* This function intentionally left blank */
}
static void
none_reset(pcmk__output_t *out) {
CRM_ASSERT(out != NULL);
none_free_priv(out);
none_init(out);
}
static void
none_subprocess_output(pcmk__output_t *out, int exit_status,
const char *proc_stdout, const char *proc_stderr) {
/* This function intentionally left blank */
}
static void
none_version(pcmk__output_t *out, bool extended) {
/* This function intentionally left blank */
}
G_GNUC_PRINTF(2, 3)
static void
none_err(pcmk__output_t *out, const char *format, ...) {
/* This function intentionally left blank */
}
G_GNUC_PRINTF(2, 3)
static void
none_info(pcmk__output_t *out, const char *format, ...) {
/* This function intentionally left blank */
}
static void
none_output_xml(pcmk__output_t *out, const char *name, const char *buf) {
/* This function intentionally left blank */
}
G_GNUC_PRINTF(4, 5)
static void
none_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plural_noun,
const char *format, ...) {
/* This function intentionally left blank */
}
G_GNUC_PRINTF(3, 4)
static void
none_list_item(pcmk__output_t *out, const char *id, const char *format, ...) {
/* This function intentionally left blank */
}
static void
none_increment_list(pcmk__output_t *out) {
/* This function intentionally left blank */
}
static void
none_end_list(pcmk__output_t *out) {
/* This function intentionally left blank */
}
+static bool
+none_is_quiet(pcmk__output_t *out) {
+ return out->quiet;
+}
+
pcmk__output_t *
pcmk__mk_none_output(char **argv) {
pcmk__output_t *retval = calloc(1, sizeof(pcmk__output_t));
if (retval == NULL) {
return NULL;
}
retval->fmt_name = "none";
retval->request = argv == NULL ? NULL : g_strjoinv(" ", argv);
- retval->supports_quiet = true;
retval->init = none_init;
retval->free_priv = none_free_priv;
retval->finish = none_finish;
retval->reset = none_reset;
retval->register_message = pcmk__register_message;
retval->message = pcmk__call_message;
retval->subprocess_output = none_subprocess_output;
retval->version = none_version;
retval->info = none_info;
retval->err = none_err;
retval->output_xml = none_output_xml;
retval->begin_list = none_begin_list;
retval->list_item = none_list_item;
retval->increment_list = none_increment_list;
retval->end_list = none_end_list;
+ retval->is_quiet = none_is_quiet;
+
return retval;
}
diff --git a/lib/common/output_text.c b/lib/common/output_text.c
index d9ee9ef7f6..9b3c09a8d2 100644
--- a/lib/common/output_text.c
+++ b/lib/common/output_text.c
@@ -1,310 +1,316 @@
/*
* Copyright 2019-2020 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU Lesser General Public License
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#include <stdarg.h>
#include <stdlib.h>
#include <glib.h>
#include <crm/crm.h>
#include <crm/common/output_internal.h>
static gboolean fancy = FALSE;
GOptionEntry pcmk__text_output_entries[] = {
{ "text-fancy", 0, 0, G_OPTION_ARG_NONE, &fancy,
"Use more highly formatted output (requires --output-as=text)",
NULL },
{ NULL }
};
typedef struct text_list_data_s {
unsigned int len;
char *singular_noun;
char *plural_noun;
} text_list_data_t;
typedef struct private_data_s {
GQueue *parent_q;
} private_data_t;
static void
text_free_priv(pcmk__output_t *out) {
private_data_t *priv = out->priv;
if (priv == NULL) {
return;
}
g_queue_free(priv->parent_q);
free(priv);
out->priv = NULL;
}
static bool
text_init(pcmk__output_t *out) {
private_data_t *priv = NULL;
/* If text_init was previously called on this output struct, just return. */
if (out->priv != NULL) {
return true;
} else {
out->priv = calloc(1, sizeof(private_data_t));
if (out->priv == NULL) {
return false;
}
priv = out->priv;
}
priv->parent_q = g_queue_new();
return true;
}
static void
text_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest) {
/* This function intentionally left blank */
}
static void
text_reset(pcmk__output_t *out) {
CRM_ASSERT(out != NULL);
out->dest = freopen(NULL, "w", out->dest);
CRM_ASSERT(out->dest != NULL);
text_free_priv(out);
text_init(out);
}
static void
text_subprocess_output(pcmk__output_t *out, int exit_status,
const char *proc_stdout, const char *proc_stderr) {
if (proc_stdout != NULL) {
fprintf(out->dest, "%s\n", proc_stdout);
}
if (proc_stderr != NULL) {
fprintf(out->dest, "%s\n", proc_stderr);
}
}
static void
text_version(pcmk__output_t *out, bool extended) {
if (extended) {
fprintf(out->dest, "Pacemaker %s (Build: %s): %s\n", PACEMAKER_VERSION, BUILD_VERSION, CRM_FEATURES);
} else {
fprintf(out->dest, "Pacemaker %s\n", PACEMAKER_VERSION);
fprintf(out->dest, "Written by Andrew Beekhof\n");
}
}
G_GNUC_PRINTF(2, 3)
static void
text_err(pcmk__output_t *out, const char *format, ...) {
va_list ap;
int len = 0;
va_start(ap, format);
/* Informational output does not get indented, to separate it from other
* potentially indented list output.
*/
len = vfprintf(stderr, format, ap);
CRM_ASSERT(len >= 0);
va_end(ap);
/* Add a newline. */
fprintf(stderr, "\n");
}
G_GNUC_PRINTF(2, 3)
static void
text_info(pcmk__output_t *out, const char *format, ...) {
va_list ap;
int len = 0;
va_start(ap, format);
/* Informational output does not get indented, to separate it from other
* potentially indented list output.
*/
len = vfprintf(out->dest, format, ap);
CRM_ASSERT(len >= 0);
va_end(ap);
/* Add a newline. */
fprintf(out->dest, "\n");
}
static void
text_output_xml(pcmk__output_t *out, const char *name, const char *buf) {
private_data_t *priv = out->priv;
CRM_ASSERT(priv != NULL);
pcmk__indented_printf(out, "%s", buf);
}
G_GNUC_PRINTF(4, 5)
static void
text_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plural_noun,
const char *format, ...) {
private_data_t *priv = out->priv;
text_list_data_t *new_list = NULL;
va_list ap;
CRM_ASSERT(priv != NULL);
va_start(ap, format);
if (fancy && format) {
pcmk__indented_vprintf(out, format, ap);
fprintf(out->dest, ":\n");
}
va_end(ap);
new_list = calloc(1, sizeof(text_list_data_t));
new_list->len = 0;
new_list->singular_noun = singular_noun == NULL ? NULL : strdup(singular_noun);
new_list->plural_noun = plural_noun == NULL ? NULL : strdup(plural_noun);
g_queue_push_tail(priv->parent_q, new_list);
}
G_GNUC_PRINTF(3, 4)
static void
text_list_item(pcmk__output_t *out, const char *id, const char *format, ...) {
private_data_t *priv = out->priv;
va_list ap;
CRM_ASSERT(priv != NULL);
va_start(ap, format);
if (fancy) {
if (id != NULL) {
/* Not really a good way to do this all in one call, so make it two.
* The first handles the indentation and list styling. The second
* just prints right after that one.
*/
pcmk__indented_printf(out, "%s: ", id);
vfprintf(out->dest, format, ap);
} else {
pcmk__indented_vprintf(out, format, ap);
}
} else {
pcmk__indented_vprintf(out, format, ap);
}
fputc('\n', out->dest);
va_end(ap);
out->increment_list(out);
}
static void
text_increment_list(pcmk__output_t *out) {
private_data_t *priv = out->priv;
gpointer tail;
CRM_ASSERT(priv != NULL);
tail = g_queue_peek_tail(priv->parent_q);
CRM_ASSERT(tail != NULL);
((text_list_data_t *) tail)->len++;
}
static void
text_end_list(pcmk__output_t *out) {
private_data_t *priv = out->priv;
text_list_data_t *node = NULL;
CRM_ASSERT(priv != NULL);
node = g_queue_pop_tail(priv->parent_q);
if (node->singular_noun != NULL && node->plural_noun != NULL) {
if (node->len == 1) {
pcmk__indented_printf(out, "%d %s found\n", node->len, node->singular_noun);
} else {
pcmk__indented_printf(out, "%d %s found\n", node->len, node->plural_noun);
}
}
free(node);
}
+static bool
+text_is_quiet(pcmk__output_t *out) {
+ return out->quiet;
+}
+
pcmk__output_t *
pcmk__mk_text_output(char **argv) {
pcmk__output_t *retval = calloc(1, sizeof(pcmk__output_t));
if (retval == NULL) {
return NULL;
}
retval->fmt_name = "text";
retval->request = argv == NULL ? NULL : g_strjoinv(" ", argv);
- retval->supports_quiet = true;
retval->init = text_init;
retval->free_priv = text_free_priv;
retval->finish = text_finish;
retval->reset = text_reset;
retval->register_message = pcmk__register_message;
retval->message = pcmk__call_message;
retval->subprocess_output = text_subprocess_output;
retval->version = text_version;
retval->info = text_info;
retval->err = text_err;
retval->output_xml = text_output_xml;
retval->begin_list = text_begin_list;
retval->list_item = text_list_item;
retval->increment_list = text_increment_list;
retval->end_list = text_end_list;
+ retval->is_quiet = text_is_quiet;
+
return retval;
}
G_GNUC_PRINTF(2, 0)
void
pcmk__indented_vprintf(pcmk__output_t *out, const char *format, va_list args) {
int len = 0;
if (fancy) {
int level = 0;
private_data_t *priv = out->priv;
CRM_ASSERT(priv != NULL);
level = g_queue_get_length(priv->parent_q);
for (int i = 0; i < level; i++) {
fprintf(out->dest, " ");
}
if (level > 0) {
fprintf(out->dest, "* ");
}
}
len = vfprintf(out->dest, format, args);
CRM_ASSERT(len >= 0);
}
G_GNUC_PRINTF(2, 3)
void
pcmk__indented_printf(pcmk__output_t *out, const char *format, ...) {
va_list ap;
va_start(ap, format);
pcmk__indented_vprintf(out, format, ap);
va_end(ap);
}
diff --git a/lib/common/output_xml.c b/lib/common/output_xml.c
index 633ea5ce3c..af6888bfca 100644
--- a/lib/common/output_xml.c
+++ b/lib/common/output_xml.c
@@ -1,465 +1,471 @@
/*
* Copyright 2019-2020 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU Lesser General Public License
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#include <config.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <glib.h>
#include <crm/crm.h>
#include <crm/common/xml.h>
#include <crm/common/output_internal.h>
static gboolean legacy_xml = FALSE;
static gboolean simple_list = FALSE;
static gboolean substitute = FALSE;
GOptionEntry pcmk__xml_output_entries[] = {
{ "xml-legacy", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &legacy_xml,
NULL,
NULL },
{ "xml-simple-list", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &simple_list,
NULL,
NULL },
{ "xml-substitute", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &substitute,
NULL,
NULL },
{ NULL }
};
typedef struct subst_s {
const char *from;
const char *to;
} subst_t;
static subst_t substitutions[] = {
{ "Attributes", "attributes" },
{ "Active Resources", "resources" },
{ "Full List of Resources", "resources" },
{ "Inactive Resources", "resources" },
{ "Cluster Summary", "summary" },
{ "Failed Resource Actions", "failures" },
{ "Fencing History", "fence_history" },
{ "Migration Summary", "node_history" },
{ "Operations", "node_history" },
{ "Negative Location Constraints", "bans" },
{ "Node Attributes", "node_attributes" },
{ "Resources", "resources" },
{ "Tickets", "tickets" },
{ NULL, NULL }
};
typedef struct private_data_s {
xmlNode *root;
GQueue *parent_q;
GSList *errors;
bool legacy_xml;
} private_data_t;
static void
xml_free_priv(pcmk__output_t *out) {
private_data_t *priv = out->priv;
if (priv == NULL) {
return;
}
xmlFreeNode(priv->root);
g_queue_free(priv->parent_q);
g_slist_free(priv->errors);
free(priv);
out->priv = NULL;
}
static bool
xml_init(pcmk__output_t *out) {
private_data_t *priv = NULL;
/* If xml_init was previously called on this output struct, just return. */
if (out->priv != NULL) {
return true;
} else {
out->priv = calloc(1, sizeof(private_data_t));
if (out->priv == NULL) {
return false;
}
priv = out->priv;
}
if (legacy_xml) {
priv->root = create_xml_node(NULL, "crm_mon");
xmlSetProp(priv->root, (pcmkXmlStr) "version", (pcmkXmlStr) VERSION);
} else {
priv->root = create_xml_node(NULL, "pacemaker-result");
xmlSetProp(priv->root, (pcmkXmlStr) "api-version", (pcmkXmlStr) PCMK__API_VERSION);
if (out->request != NULL) {
xmlSetProp(priv->root, (pcmkXmlStr) "request", (pcmkXmlStr) out->request);
}
}
priv->parent_q = g_queue_new();
priv->errors = NULL;
g_queue_push_tail(priv->parent_q, priv->root);
/* Copy this from the file-level variable. This means that it is only settable
* as a command line option, and that pcmk__output_new must be called after all
* command line processing is completed.
*/
priv->legacy_xml = legacy_xml;
return true;
}
static void
add_error_node(gpointer data, gpointer user_data) {
char *str = (char *) data;
xmlNodePtr node = (xmlNodePtr) user_data;
pcmk_create_xml_text_node(node, "error", str);
}
static void
finish_reset_common(pcmk__output_t *out, crm_exit_t exit_status, bool print) {
xmlNodePtr node;
private_data_t *priv = out->priv;
if (legacy_xml) {
GSList *node = priv->errors;
if (exit_status != CRM_EX_OK) {
fprintf(stderr, "%s\n", crm_exit_str(exit_status));
}
while (node != NULL) {
fprintf(stderr, "%s\n", (char *) node->data);
node = node->next;
}
} else {
char *rc_as_str = crm_itoa(exit_status);
node = create_xml_node(priv->root, "status");
xmlSetProp(node, (pcmkXmlStr) "code", (pcmkXmlStr) rc_as_str);
xmlSetProp(node, (pcmkXmlStr) "message", (pcmkXmlStr) crm_exit_str(exit_status));
if (g_slist_length(priv->errors) > 0) {
xmlNodePtr errors_node = create_xml_node(node, "errors");
g_slist_foreach(priv->errors, add_error_node, (gpointer) errors_node);
}
free(rc_as_str);
}
if (print) {
char *buf = dump_xml_formatted_with_text(priv->root);
fprintf(out->dest, "%s", buf);
free(buf);
}
}
static void
xml_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest) {
private_data_t *priv = out->priv;
/* If root is NULL, xml_init failed and we are being called from pcmk__output_free
* in the pcmk__output_new path.
*/
if (priv == NULL || priv->root == NULL) {
return;
}
finish_reset_common(out, exit_status, print);
if (copy_dest != NULL) {
*copy_dest = copy_xml(priv->root);
}
}
static void
xml_reset(pcmk__output_t *out) {
CRM_ASSERT(out != NULL);
out->dest = freopen(NULL, "w", out->dest);
CRM_ASSERT(out->dest != NULL);
if (out->priv != NULL) {
finish_reset_common(out, CRM_EX_OK, true);
}
xml_free_priv(out);
xml_init(out);
}
static void
xml_subprocess_output(pcmk__output_t *out, int exit_status,
const char *proc_stdout, const char *proc_stderr) {
xmlNodePtr node, child_node;
char *rc_as_str = NULL;
rc_as_str = crm_itoa(exit_status);
node = pcmk__output_xml_create_parent(out, "command");
xmlSetProp(node, (pcmkXmlStr) "code", (pcmkXmlStr) rc_as_str);
if (proc_stdout != NULL) {
child_node = pcmk_create_xml_text_node(node, "output", proc_stdout);
xmlSetProp(child_node, (pcmkXmlStr) "source", (pcmkXmlStr) "stdout");
}
if (proc_stderr != NULL) {
child_node = pcmk_create_xml_text_node(node, "output", proc_stderr);
xmlSetProp(child_node, (pcmkXmlStr) "source", (pcmkXmlStr) "stderr");
}
pcmk__output_xml_add_node(out, node);
free(rc_as_str);
}
static void
xml_version(pcmk__output_t *out, bool extended) {
xmlNodePtr node;
private_data_t *priv = out->priv;
CRM_ASSERT(priv != NULL);
node = pcmk__output_create_xml_node(out, "version");
xmlSetProp(node, (pcmkXmlStr) "program", (pcmkXmlStr) "Pacemaker");
xmlSetProp(node, (pcmkXmlStr) "version", (pcmkXmlStr) PACEMAKER_VERSION);
xmlSetProp(node, (pcmkXmlStr) "author", (pcmkXmlStr) "Andrew Beekhof");
xmlSetProp(node, (pcmkXmlStr) "build", (pcmkXmlStr) BUILD_VERSION);
xmlSetProp(node, (pcmkXmlStr) "features", (pcmkXmlStr) CRM_FEATURES);
}
G_GNUC_PRINTF(2, 3)
static void
xml_err(pcmk__output_t *out, const char *format, ...) {
private_data_t *priv = out->priv;
int len = 0;
char *buf = NULL;
va_list ap;
CRM_ASSERT(priv != NULL);
va_start(ap, format);
len = vasprintf(&buf, format, ap);
CRM_ASSERT(len > 0);
va_end(ap);
priv->errors = g_slist_append(priv->errors, buf);
}
G_GNUC_PRINTF(2, 3)
static void
xml_info(pcmk__output_t *out, const char *format, ...) {
/* This function intentially left blank */
}
static void
xml_output_xml(pcmk__output_t *out, const char *name, const char *buf) {
xmlNodePtr parent = NULL;
xmlNodePtr cdata_node = NULL;
private_data_t *priv = out->priv;
CRM_ASSERT(priv != NULL);
parent = pcmk__output_create_xml_node(out, name);
cdata_node = xmlNewCDataBlock(getDocPtr(parent), (pcmkXmlStr) buf, strlen(buf));
xmlAddChild(parent, cdata_node);
}
G_GNUC_PRINTF(4, 5)
static void
xml_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plural_noun,
const char *format, ...) {
va_list ap;
const char *name = NULL;
char *buf = NULL;
int len;
va_start(ap, format);
len = vasprintf(&buf, format, ap);
CRM_ASSERT(len >= 0);
va_end(ap);
if (substitute) {
for (subst_t *s = substitutions; s->from != NULL; s++) {
if (!strcmp(s->from, buf)) {
name = s->to;
break;
}
}
}
if (name == NULL) {
name = buf;
}
if (legacy_xml || simple_list) {
pcmk__output_xml_create_parent(out, name);
} else {
xmlNodePtr list_node = NULL;
list_node = pcmk__output_xml_create_parent(out, "list");
xmlSetProp(list_node, (pcmkXmlStr) "name", (pcmkXmlStr) name);
}
free(buf);
}
G_GNUC_PRINTF(3, 4)
static void
xml_list_item(pcmk__output_t *out, const char *name, const char *format, ...) {
private_data_t *priv = out->priv;
xmlNodePtr item_node = NULL;
va_list ap;
char *buf = NULL;
int len;
CRM_ASSERT(priv != NULL);
va_start(ap, format);
len = vasprintf(&buf, format, ap);
CRM_ASSERT(len >= 0);
va_end(ap);
item_node = pcmk__output_create_xml_text_node(out, "item", buf);
if (name != NULL) {
xmlSetProp(item_node, (pcmkXmlStr) "name", (pcmkXmlStr) name);
}
free(buf);
}
static void
xml_increment_list(pcmk__output_t *out) {
/* This function intentially left blank */
}
static void
xml_end_list(pcmk__output_t *out) {
private_data_t *priv = out->priv;
CRM_ASSERT(priv != NULL);
if (priv->legacy_xml || simple_list) {
g_queue_pop_tail(priv->parent_q);
} else {
char *buf = NULL;
xmlNodePtr node;
node = g_queue_pop_tail(priv->parent_q);
buf = crm_strdup_printf("%lu", xmlChildElementCount(node));
xmlSetProp(node, (pcmkXmlStr) "count", (pcmkXmlStr) buf);
free(buf);
}
}
+static bool
+xml_is_quiet(pcmk__output_t *out) {
+ return false;
+}
+
pcmk__output_t *
pcmk__mk_xml_output(char **argv) {
pcmk__output_t *retval = calloc(1, sizeof(pcmk__output_t));
if (retval == NULL) {
return NULL;
}
retval->fmt_name = "xml";
retval->request = argv == NULL ? NULL : g_strjoinv(" ", argv);
- retval->supports_quiet = false;
retval->init = xml_init;
retval->free_priv = xml_free_priv;
retval->finish = xml_finish;
retval->reset = xml_reset;
retval->register_message = pcmk__register_message;
retval->message = pcmk__call_message;
retval->subprocess_output = xml_subprocess_output;
retval->version = xml_version;
retval->info = xml_info;
retval->err = xml_err;
retval->output_xml = xml_output_xml;
retval->begin_list = xml_begin_list;
retval->list_item = xml_list_item;
retval->increment_list = xml_increment_list;
retval->end_list = xml_end_list;
+ retval->is_quiet = xml_is_quiet;
+
return retval;
}
xmlNodePtr
pcmk__output_xml_create_parent(pcmk__output_t *out, const char *name) {
xmlNodePtr node = pcmk__output_create_xml_node(out, name);
pcmk__output_xml_push_parent(out, node);
return node;
}
void
pcmk__output_xml_add_node(pcmk__output_t *out, xmlNodePtr node) {
private_data_t *priv = out->priv;
CRM_ASSERT(priv != NULL);
CRM_ASSERT(node != NULL);
xmlAddChild(g_queue_peek_tail(priv->parent_q), node);
}
xmlNodePtr
pcmk__output_create_xml_node(pcmk__output_t *out, const char *name) {
private_data_t *priv = out->priv;
CRM_ASSERT(priv != NULL);
return create_xml_node(g_queue_peek_tail(priv->parent_q), name);
}
xmlNodePtr
pcmk__output_create_xml_text_node(pcmk__output_t *out, const char *name, const char *content) {
xmlNodePtr node = pcmk__output_create_xml_node(out, name);
xmlNodeSetContent(node, (pcmkXmlStr) content);
return node;
}
void
pcmk__output_xml_push_parent(pcmk__output_t *out, xmlNodePtr parent) {
private_data_t *priv = out->priv;
CRM_ASSERT(priv != NULL);
CRM_ASSERT(parent != NULL);
g_queue_push_tail(priv->parent_q, parent);
}
void
pcmk__output_xml_pop_parent(pcmk__output_t *out) {
private_data_t *priv = out->priv;
CRM_ASSERT(priv != NULL);
CRM_ASSERT(g_queue_get_length(priv->parent_q) > 0);
g_queue_pop_tail(priv->parent_q);
}
xmlNodePtr
pcmk__output_xml_peek_parent(pcmk__output_t *out) {
private_data_t *priv = out->priv;
CRM_ASSERT(priv != NULL);
/* If queue is empty NULL will be returned */
return g_queue_peek_tail(priv->parent_q);
}
diff --git a/lib/pacemaker/pcmk_fence.c b/lib/pacemaker/pcmk_fence.c
index 06dbcd181e..7beedff3f1 100644
--- a/lib/pacemaker/pcmk_fence.c
+++ b/lib/pacemaker/pcmk_fence.c
@@ -1,507 +1,508 @@
/*
* Copyright 2009-2020 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU General Public License version 2
* or later (GPLv2+) WITHOUT ANY WARRANTY.
*/
#include <crm_internal.h>
#include <crm/common/mainloop.h>
#include <crm/common/results.h>
#include <crm/common/output_internal.h>
#include <crm/stonith-ng.h>
#include <crm/fencing/internal.h>
#include <glib.h>
#include <libxml/tree.h>
#include <pacemaker.h>
#include <pcmki/pcmki_output.h>
#include <pcmki/pcmki_fence.h>
static const int st_opts = st_opt_sync_call | st_opt_allow_suicide;
static GMainLoop *mainloop = NULL;
static struct {
stonith_t *st;
const char *target;
const char *action;
char *name;
unsigned int timeout;
unsigned int tolerance;
int delay;
int rc;
} async_fence_data;
static int
handle_level(stonith_t *st, char *target, int fence_level,
stonith_key_value_t *devices, bool added) {
char *node = NULL;
char *pattern = NULL;
char *name = NULL;
char *value = NULL;
int rc = pcmk_rc_ok;
if (target == NULL) {
// Not really possible, but makes static analysis happy
return EINVAL;
}
/* Determine if targeting by attribute, node name pattern or node name */
value = strchr(target, '=');
if (value != NULL) {
name = target;
*value++ = '\0';
} else if (*target == '@') {
pattern = target + 1;
} else {
node = target;
}
/* Register or unregister level as appropriate */
if (added) {
rc = st->cmds->register_level_full(st, st_opts, node, pattern,
name, value, fence_level,
devices);
} else {
rc = st->cmds->remove_level_full(st, st_opts, node, pattern,
name, value, fence_level);
}
return pcmk_legacy2rc(rc);
}
static void
notify_callback(stonith_t * st, stonith_event_t * e)
{
if (e->result != pcmk_ok) {
return;
}
if (pcmk__str_eq(async_fence_data.target, e->target, pcmk__str_casei) &&
pcmk__str_eq(async_fence_data.action, e->action, pcmk__str_casei)) {
async_fence_data.rc = e->result;
g_main_loop_quit(mainloop);
}
}
static void
fence_callback(stonith_t * stonith, stonith_callback_data_t * data)
{
async_fence_data.rc = data->rc;
g_main_loop_quit(mainloop);
}
static gboolean
async_fence_helper(gpointer user_data)
{
stonith_t *st = async_fence_data.st;
int call_id = 0;
int rc = stonith_api_connect_retry(st, async_fence_data.name, 10);
if (rc != pcmk_ok) {
fprintf(stderr, "Could not connect to fencer: %s\n", pcmk_strerror(rc));
g_main_loop_quit(mainloop);
return TRUE;
}
st->cmds->register_notification(st, T_STONITH_NOTIFY_FENCE, notify_callback);
call_id = st->cmds->fence_with_delay(st,
st_opt_allow_suicide,
async_fence_data.target,
async_fence_data.action,
async_fence_data.timeout/1000,
async_fence_data.tolerance/1000,
async_fence_data.delay);
if (call_id < 0) {
g_main_loop_quit(mainloop);
return TRUE;
}
st->cmds->register_callback(st,
call_id,
async_fence_data.timeout/1000,
st_opt_timeout_updates, NULL, "callback", fence_callback);
return TRUE;
}
int
pcmk__fence_action(stonith_t *st, const char *target, const char *action,
const char *name, unsigned int timeout, unsigned int tolerance,
int delay)
{
crm_trigger_t *trig;
async_fence_data.st = st;
async_fence_data.name = strdup(name);
async_fence_data.target = target;
async_fence_data.action = action;
async_fence_data.timeout = timeout;
async_fence_data.tolerance = tolerance;
async_fence_data.delay = delay;
async_fence_data.rc = pcmk_err_generic;
trig = mainloop_add_trigger(G_PRIORITY_HIGH, async_fence_helper, NULL);
mainloop_set_trigger(trig);
mainloop = g_main_loop_new(NULL, FALSE);
g_main_loop_run(mainloop);
free(async_fence_data.name);
return pcmk_legacy2rc(async_fence_data.rc);
}
#ifdef BUILD_PUBLIC_LIBPACEMAKER
int
pcmk_fence_action(stonith_t *st, const char *target, const char *action,
const char *name, unsigned int timeout, unsigned int tolerance,
int delay)
{
return pcmk__fence_action(st, target, action, name, timeout, tolerance, delay);
}
#endif
int
pcmk__fence_history(pcmk__output_t *out, stonith_t *st, char *target,
- unsigned int timeout, bool quiet, int verbose,
- bool broadcast, bool cleanup) {
+ unsigned int timeout, int verbose, bool broadcast,
+ bool cleanup) {
stonith_history_t *history = NULL, *hp, *latest = NULL;
int rc = pcmk_rc_ok;
int opts = 0;
- if (!quiet) {
+ if (!out->is_quiet(out)) {
if (cleanup) {
out->info(out, "cleaning up fencing-history%s%s",
target ? " for node " : "", target ? target : "");
}
if (broadcast) {
out->info(out, "gather fencing-history from all nodes");
}
}
stonith__set_call_options(opts, target, st_opts);
if (cleanup) {
stonith__set_call_options(opts, target, st_opt_cleanup);
}
if (broadcast) {
stonith__set_call_options(opts, target, st_opt_broadcast);
}
rc = st->cmds->history(st, opts,
pcmk__str_eq(target, "*", pcmk__str_none)? NULL : target,
&history, timeout/1000);
if (cleanup) {
// Cleanup doesn't return a history list
stonith_history_free(history);
return pcmk_legacy2rc(rc);
}
out->begin_list(out, "event", "events", "Fencing history");
history = stonith__sort_history(history);
for (hp = history; hp; hp = hp->next) {
if (hp->state == st_done) {
latest = hp;
}
- if (quiet || !verbose) {
+ if (out->is_quiet(out) || !verbose) {
continue;
}
out->message(out, "stonith-event", hp, 1, stonith__later_succeeded(hp, history));
out->increment_list(out);
}
if (latest) {
- if (quiet && out->supports_quiet) {
+ if (out->is_quiet(out)) {
out->info(out, "%lld", (long long) latest->completed);
} else if (!verbose) { // already printed if verbose
out->message(out, "stonith-event", latest, 0, FALSE);
out->increment_list(out);
}
}
out->end_list(out);
stonith_history_free(history);
return pcmk_legacy2rc(rc);
}
#ifdef BUILD_PUBLIC_LIBPACEMAKER
int
pcmk_fence_history(xmlNodePtr *xml, stonith_t *st, char *target, unsigned int timeout,
bool quiet, int verbose, bool broadcast, bool cleanup) {
pcmk__output_t *out = NULL;
int rc = pcmk_rc_ok;
rc = pcmk__out_prologue(&out, xml);
if (rc != pcmk_rc_ok) {
return rc;
}
- rc = pcmk__fence_history(out, st, target, timeout, quiet, verbose,
- broadcast, cleanup);
+ out->quiet = quiet;
+
+ rc = pcmk__fence_history(out, st, target, timeout, verbose, broadcast, cleanup);
pcmk__out_epilogue(out, xml, rc);
return rc;
}
#endif
int
pcmk__fence_installed(pcmk__output_t *out, stonith_t *st, unsigned int timeout) {
stonith_key_value_t *devices = NULL;
int rc = pcmk_rc_ok;
rc = st->cmds->list_agents(st, st_opt_sync_call, NULL, &devices, timeout/1000);
/* list_agents returns a negative error code or a positive number of agents. */
if (rc < 0) {
return pcmk_legacy2rc(rc);
}
out->begin_list(out, "fence device", "fence devices", "Installed fence devices");
for (stonith_key_value_t *dIter = devices; dIter; dIter = dIter->next) {
out->list_item(out, "device", "%s", dIter->value);
}
out->end_list(out);
stonith_key_value_freeall(devices, 1, 1);
return pcmk_rc_ok;
}
#ifdef BUILD_PUBLIC_LIBPACEMAKER
int
pcmk_fence_installed(xmlNodePtr *xml, stonith_t *st, unsigned int timeout) {
pcmk__output_t *out = NULL;
int rc = pcmk_rc_ok;
rc = pcmk__out_prologue(&out, xml);
if (rc != pcmk_rc_ok) {
return rc;
}
rc = pcmk__fence_installed(out, st, timeout);
pcmk__out_epilogue(out, xml, rc);
return rc;
}
#endif
int
pcmk__fence_last(pcmk__output_t *out, const char *target, bool as_nodeid) {
time_t when = 0;
if (target == NULL) {
return pcmk_rc_ok;
}
if (as_nodeid) {
when = stonith_api_time(atol(target), NULL, FALSE);
} else {
when = stonith_api_time(0, target, FALSE);
}
return out->message(out, "last-fenced", target, when);
}
#ifdef BUILD_PUBLIC_LIBPACEMAKER
int
pcmk_fence_last(xmlNodePtr *xml, const char *target, bool as_nodeid) {
pcmk__output_t *out = NULL;
int rc = pcmk_rc_ok;
rc = pcmk__out_prologue(&out, xml);
if (rc != pcmk_rc_ok) {
return rc;
}
rc = pcmk__fence_last(out, target, as_nodeid);
pcmk__out_epilogue(out, xml, rc);
return rc;
}
#endif
int
pcmk__fence_list_targets(pcmk__output_t *out, stonith_t *st,
const char *device_id, unsigned int timeout) {
GList *targets = NULL;
char *lists = NULL;
int rc = pcmk_rc_ok;
rc = st->cmds->list(st, st_opts, device_id, &lists, timeout/1000);
if (rc != pcmk_rc_ok) {
return pcmk_legacy2rc(rc);
}
targets = stonith__parse_targets(lists);
out->begin_list(out, "fence target", "fence targets", "Fence Targets");
while (targets != NULL) {
out->list_item(out, NULL, "%s", (const char *) targets->data);
targets = targets->next;
}
out->end_list(out);
free(lists);
return rc;
}
#ifdef BUILD_PUBLIC_LIBPACEMAKER
int
pcmk_fence_list_targets(xmlNodePtr *xml, stonith_t *st, const char *device_id,
unsigned int timeout) {
pcmk__output_t *out = NULL;
int rc = pcmk_rc_ok;
rc = pcmk__out_prologue(&out, xml);
if (rc != pcmk_rc_ok) {
return rc;
}
rc = pcmk__fence_list_targets(out, st, device_id, timeout);
pcmk__out_epilogue(out, xml, rc);
return rc;
}
#endif
int
pcmk__fence_metadata(pcmk__output_t *out, stonith_t *st, char *agent,
unsigned int timeout) {
char *buffer = NULL;
int rc = st->cmds->metadata(st, st_opt_sync_call, agent, NULL, &buffer,
timeout/1000);
if (rc != pcmk_rc_ok) {
return pcmk_legacy2rc(rc);
}
out->output_xml(out, "metadata", buffer);
free(buffer);
return rc;
}
#ifdef BUILD_PUBLIC_LIBPACEMAKER
int
pcmk_fence_metadata(xmlNodePtr *xml, stonith_t *st, char *agent,
unsigned int timeout) {
pcmk__output_t *out = NULL;
int rc = pcmk_rc_ok;
rc = pcmk__out_prologue(&out, xml);
if (rc != pcmk_rc_ok) {
return rc;
}
rc = pcmk__fence_metadata(out, st, agent, timeout);
pcmk__out_epilogue(out, xml, rc);
return rc;
}
#endif
int
pcmk__fence_registered(pcmk__output_t *out, stonith_t *st, char *target,
unsigned int timeout) {
stonith_key_value_t *devices = NULL;
int rc = pcmk_rc_ok;
rc = st->cmds->query(st, st_opts, target, &devices, timeout/1000);
/* query returns a negative error code or a positive number of results. */
if (rc < 0) {
return pcmk_legacy2rc(rc);
}
out->begin_list(out, "fence device", "fence devices", "Registered fence devices");
for (stonith_key_value_t *dIter = devices; dIter; dIter = dIter->next) {
out->list_item(out, "device", "%s", dIter->value);
}
out->end_list(out);
stonith_key_value_freeall(devices, 1, 1);
/* Return pcmk_rc_ok here, not the number of results. Callers probably
* don't care.
*/
return pcmk_rc_ok;
}
#ifdef BUILD_PUBLIC_LIBPACEMAKER
int
pcmk_fence_registered(xmlNodePtr *xml, stonith_t *st, char *target,
unsigned int timeout) {
pcmk__output_t *out = NULL;
int rc = pcmk_rc_ok;
rc = pcmk__out_prologue(&out, xml);
if (rc != pcmk_rc_ok) {
return rc;
}
rc = pcmk__fence_registered(out, st, target, timeout);
pcmk__out_epilogue(out, xml, rc);
return rc;
}
#endif
int
pcmk__fence_register_level(stonith_t *st, char *target, int fence_level,
stonith_key_value_t *devices) {
return handle_level(st, target, fence_level, devices, true);
}
#ifdef BUILD_PUBLIC_LIBPACEMAKER
int
pcmk_fence_register_level(stonith_t *st, char *target, int fence_level,
stonith_key_value_t *devices) {
return pcmk__fence_register_level(st, target, fence_level, devices);
}
#endif
int
pcmk__fence_unregister_level(stonith_t *st, char *target, int fence_level) {
return handle_level(st, target, fence_level, NULL, false);
}
#ifdef BUILD_PUBLIC_LIBPACEMAKER
int
pcmk_fence_unregister_level(stonith_t *st, char *target, int fence_level) {
return pcmk__fence_unregister_level(st, target, fence_level);
}
#endif
int
pcmk__fence_validate(pcmk__output_t *out, stonith_t *st, const char *agent,
const char *id, stonith_key_value_t *params,
unsigned int timeout) {
char *output = NULL;
char *error_output = NULL;
int rc;
rc = st->cmds->validate(st, st_opt_sync_call, id, NULL, agent, params,
timeout/1000, &output, &error_output);
out->message(out, "validate", agent, id, output, error_output, rc);
return pcmk_legacy2rc(rc);
}
#ifdef BUILD_PUBLIC_LIBPACEMAKER
int
pcmk_fence_validate(xmlNodePtr *xml, stonith_t *st, const char *agent,
const char *id, stonith_key_value_t *params,
unsigned int timeout) {
pcmk__output_t *out = NULL;
int rc = pcmk_rc_ok;
rc = pcmk__out_prologue(&out, xml);
if (rc != pcmk_rc_ok) {
return rc;
}
rc = pcmk__fence_validate(out, st, agent, id, params, timeout);
pcmk__out_epilogue(out, xml, rc);
return rc;
}
#endif
diff --git a/tools/crm_mon_curses.c b/tools/crm_mon_curses.c
index db7ca61c2f..ef0b0c7bb1 100644
--- a/tools/crm_mon_curses.c
+++ b/tools/crm_mon_curses.c
@@ -1,426 +1,432 @@
/*
* Copyright 2019-2020 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU Lesser General Public License
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#include <crm_internal.h>
#include <stdarg.h>
#include <stdlib.h>
#include <crm/crm.h>
#include <crm/common/curses_internal.h>
#include <crm/common/output_internal.h>
#include <crm/stonith-ng.h>
#include <crm/fencing/internal.h>
#include <crm/pengine/internal.h>
#include <glib.h>
#include "crm_mon.h"
#if CURSES_ENABLED
GOptionEntry crm_mon_curses_output_entries[] = {
{ NULL }
};
typedef struct curses_list_data_s {
unsigned int len;
char *singular_noun;
char *plural_noun;
} curses_list_data_t;
typedef struct private_data_s {
GQueue *parent_q;
} private_data_t;
static void
curses_free_priv(pcmk__output_t *out) {
private_data_t *priv = out->priv;
if (priv == NULL) {
return;
}
g_queue_free(priv->parent_q);
free(priv);
out->priv = NULL;
}
static bool
curses_init(pcmk__output_t *out) {
private_data_t *priv = NULL;
/* If curses_init was previously called on this output struct, just return. */
if (out->priv != NULL) {
return true;
} else {
out->priv = calloc(1, sizeof(private_data_t));
if (out->priv == NULL) {
return false;
}
priv = out->priv;
}
priv->parent_q = g_queue_new();
initscr();
cbreak();
noecho();
return true;
}
static void
curses_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest) {
echo();
nocbreak();
endwin();
}
static void
curses_reset(pcmk__output_t *out) {
CRM_ASSERT(out != NULL);
curses_free_priv(out);
curses_init(out);
}
static void
curses_subprocess_output(pcmk__output_t *out, int exit_status,
const char *proc_stdout, const char *proc_stderr) {
if (proc_stdout != NULL) {
printw("%s\n", proc_stdout);
}
if (proc_stderr != NULL) {
printw("%s\n", proc_stderr);
}
clrtoeol();
refresh();
}
/* curses_version is defined in curses.h, so we can't use that name here.
* Note that this function prints out via text, not with curses.
*/
static void
curses_ver(pcmk__output_t *out, bool extended) {
if (extended) {
printf("Pacemaker %s (Build: %s): %s\n", PACEMAKER_VERSION, BUILD_VERSION, CRM_FEATURES);
} else {
printf("Pacemaker %s\n", PACEMAKER_VERSION);
printf("Written by Andrew Beekhof\n");
}
}
G_GNUC_PRINTF(2, 3)
static void
curses_error(pcmk__output_t *out, const char *format, ...) {
va_list ap;
/* Informational output does not get indented, to separate it from other
* potentially indented list output.
*/
va_start(ap, format);
vw_printw(stdscr, format, ap);
va_end(ap);
/* Add a newline. */
addch('\n');
clrtoeol();
refresh();
sleep(2);
}
G_GNUC_PRINTF(2, 3)
static void
curses_info(pcmk__output_t *out, const char *format, ...) {
va_list ap;
/* Informational output does not get indented, to separate it from other
* potentially indented list output.
*/
va_start(ap, format);
vw_printw(stdscr, format, ap);
va_end(ap);
/* Add a newline. */
addch('\n');
clrtoeol();
refresh();
}
static void
curses_output_xml(pcmk__output_t *out, const char *name, const char *buf) {
private_data_t *priv = out->priv;
CRM_ASSERT(priv != NULL);
curses_indented_printf(out, "%s", buf);
}
G_GNUC_PRINTF(4, 5)
static void
curses_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plural_noun,
const char *format, ...) {
private_data_t *priv = out->priv;
curses_list_data_t *new_list = NULL;
va_list ap;
CRM_ASSERT(priv != NULL);
va_start(ap, format);
curses_indented_vprintf(out, format, ap);
printw(":\n");
va_end(ap);
new_list = calloc(1, sizeof(curses_list_data_t));
new_list->len = 0;
new_list->singular_noun = singular_noun == NULL ? NULL : strdup(singular_noun);
new_list->plural_noun = plural_noun == NULL ? NULL : strdup(plural_noun);
g_queue_push_tail(priv->parent_q, new_list);
}
G_GNUC_PRINTF(3, 4)
static void
curses_list_item(pcmk__output_t *out, const char *id, const char *format, ...) {
private_data_t *priv = out->priv;
va_list ap;
CRM_ASSERT(priv != NULL);
va_start(ap, format);
if (id != NULL) {
curses_indented_printf(out, "%s: ", id);
vw_printw(stdscr, format, ap);
} else {
curses_indented_vprintf(out, format, ap);
}
addch('\n');
va_end(ap);
out->increment_list(out);
}
static void
curses_increment_list(pcmk__output_t *out) {
private_data_t *priv = out->priv;
gpointer tail;
CRM_ASSERT(priv != NULL);
tail = g_queue_peek_tail(priv->parent_q);
CRM_ASSERT(tail != NULL);
((curses_list_data_t *) tail)->len++;
}
static void
curses_end_list(pcmk__output_t *out) {
private_data_t *priv = out->priv;
curses_list_data_t *node = NULL;
CRM_ASSERT(priv != NULL);
node = g_queue_pop_tail(priv->parent_q);
if (node->singular_noun != NULL && node->plural_noun != NULL) {
if (node->len == 1) {
curses_indented_printf(out, "%d %s found\n", node->len, node->singular_noun);
} else {
curses_indented_printf(out, "%d %s found\n", node->len, node->plural_noun);
}
}
free(node);
}
+static bool
+curses_is_quiet(pcmk__output_t *out) {
+ return out->quiet;
+}
+
pcmk__output_t *
crm_mon_mk_curses_output(char **argv) {
pcmk__output_t *retval = calloc(1, sizeof(pcmk__output_t));
if (retval == NULL) {
return NULL;
}
retval->fmt_name = "console";
retval->request = argv == NULL ? NULL : g_strjoinv(" ", argv);
- retval->supports_quiet = true;
retval->init = curses_init;
retval->free_priv = curses_free_priv;
retval->finish = curses_finish;
retval->reset = curses_reset;
retval->register_message = pcmk__register_message;
retval->message = pcmk__call_message;
retval->subprocess_output = curses_subprocess_output;
retval->version = curses_ver;
retval->err = curses_error;
retval->info = curses_info;
retval->output_xml = curses_output_xml;
retval->begin_list = curses_begin_list;
retval->list_item = curses_list_item;
retval->increment_list = curses_increment_list;
retval->end_list = curses_end_list;
+ retval->is_quiet = curses_is_quiet;
+
return retval;
}
G_GNUC_PRINTF(2, 0)
void
curses_indented_vprintf(pcmk__output_t *out, const char *format, va_list args) {
int level = 0;
private_data_t *priv = out->priv;
CRM_ASSERT(priv != NULL);
level = g_queue_get_length(priv->parent_q);
for (int i = 0; i < level; i++) {
printw(" ");
}
if (level > 0) {
printw("* ");
}
vw_printw(stdscr, format, args);
clrtoeol();
refresh();
}
G_GNUC_PRINTF(2, 3)
void
curses_indented_printf(pcmk__output_t *out, const char *format, ...) {
va_list ap;
va_start(ap, format);
curses_indented_vprintf(out, format, ap);
va_end(ap);
}
PCMK__OUTPUT_ARGS("stonith-event", "stonith_history_t *", "gboolean", "gboolean")
static int
stonith_event_console(pcmk__output_t *out, va_list args) {
stonith_history_t *event = va_arg(args, stonith_history_t *);
gboolean full_history = va_arg(args, gboolean);
gboolean later_succeeded = va_arg(args, gboolean);
crm_time_t *crm_when = crm_time_new(NULL);
char *buf = NULL;
crm_time_set_timet(crm_when, &(event->completed));
buf = crm_time_as_string(crm_when, crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
switch (event->state) {
case st_failed:
curses_indented_printf(out, "%s of %s failed: delegate=%s, client=%s, origin=%s, %s='%s'%s\n",
stonith_action_str(event->action), event->target,
event->delegate ? event->delegate : "",
event->client, event->origin,
full_history ? "completed" : "last-failed", buf,
later_succeeded ? " (a later attempt succeeded)" : "");
break;
case st_done:
curses_indented_printf(out, "%s of %s successful: delegate=%s, client=%s, origin=%s, %s='%s'\n",
stonith_action_str(event->action), event->target,
event->delegate ? event->delegate : "",
event->client, event->origin,
full_history ? "completed" : "last-successful", buf);
break;
default:
curses_indented_printf(out, "%s of %s pending: client=%s, origin=%s\n",
stonith_action_str(event->action), event->target,
event->client, event->origin);
break;
}
free(buf);
crm_time_free(crm_when);
return pcmk_rc_ok;
}
PCMK__OUTPUT_ARGS("maint-mode")
static int
cluster_maint_mode_console(pcmk__output_t *out, va_list args) {
printw("\n *** Resource management is DISABLED ***");
printw("\n The cluster will not attempt to start, stop or recover services");
printw("\n");
clrtoeol();
refresh();
return pcmk_rc_ok;
}
static pcmk__message_entry_t fmt_functions[] = {
{ "ban", "console", pe__ban_text },
{ "bundle", "console", pe__bundle_text },
{ "clone", "console", pe__clone_text },
{ "cluster-counts", "console", pe__cluster_counts_text },
{ "cluster-dc", "console", pe__cluster_dc_text },
{ "cluster-options", "console", pe__cluster_options_text },
{ "cluster-stack", "console", pe__cluster_stack_text },
{ "cluster-summary", "console", pe__cluster_summary },
{ "cluster-times", "console", pe__cluster_times_text },
{ "failed-action", "console", pe__failed_action_text },
{ "failed-fencing-history", "console", stonith__failed_history },
{ "fencing-history", "console", stonith__history },
{ "full-fencing-history", "console", stonith__full_history },
{ "group", "console", pe__group_text },
{ "maint-mode", "console", cluster_maint_mode_console },
{ "node", "console", pe__node_text },
{ "node-attribute", "console", pe__node_attribute_text },
{ "node-list", "console", pe__node_list_text },
{ "op-history", "console", pe__op_history_text },
{ "pending-fencing-actions", "console", stonith__pending_actions },
{ "primitive", "console", pe__resource_text },
{ "resource-history", "console", pe__resource_history_text },
{ "stonith-event", "console", stonith_event_console },
{ "ticket", "console", pe__ticket_text },
{ NULL, NULL, NULL }
};
void
crm_mon_register_messages(pcmk__output_t *out) {
pcmk__register_messages(out, fmt_functions);
}
#else
pcmk__output_t *
crm_mon_mk_curses_output(char **argv) {
/* curses was disabled in the build, so fall back to text. */
return pcmk__mk_text_output(argv);
}
G_GNUC_PRINTF(2, 0)
void
curses_indented_vprintf(pcmk__output_t *out, const char *format, va_list args) {
return;
}
G_GNUC_PRINTF(2, 3)
void
curses_indented_printf(pcmk__output_t *out, const char *format, ...) {
return;
}
void
crm_mon_register_messages(pcmk__output_t *out) {
return;
}
#endif
diff --git a/tools/stonith_admin.c b/tools/stonith_admin.c
index 96996d0728..1453a20530 100644
--- a/tools/stonith_admin.c
+++ b/tools/stonith_admin.c
@@ -1,630 +1,632 @@
/*
* Copyright 2009-2020 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU General Public License version 2
* or later (GPLv2+) WITHOUT ANY WARRANTY.
*/
#include <crm_internal.h>
#include <sys/param.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/utsname.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <crm/crm.h>
#include <crm/msg_xml.h>
#include <crm/common/ipc.h>
#include <crm/cluster/internal.h>
#include <crm/common/cmdline_internal.h>
#include <crm/common/output_internal.h>
#include <crm/stonith-ng.h>
#include <crm/fencing/internal.h>
#include <crm/cib.h>
#include <crm/pengine/status.h>
#include <crm/common/xml.h>
#include <pacemaker-internal.h>
#define SUMMARY "stonith_admin - Access the Pacemaker fencing API"
char action = 0;
struct {
gboolean as_nodeid;
gboolean broadcast;
gboolean cleanup;
gboolean installed;
gboolean metadata;
gboolean registered;
gboolean validate_cfg;
stonith_key_value_t *devices;
stonith_key_value_t *params;
int fence_level;
int timeout ;
int tolerance;
int delay;
char *agent;
char *confirm_host;
char *fence_host;
char *history;
char *last_fenced;
char *query;
char *reboot_host;
char *register_dev;
char *register_level;
char *targets;
char *terminate;
char *unfence_host;
char *unregister_dev;
char *unregister_level;
} options = {
.timeout = 120,
.delay = 0
};
gboolean add_env_params(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
gboolean add_stonith_device(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
gboolean add_stonith_params(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
gboolean add_tolerance(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
gboolean set_tag(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
#define INDENT " "
/* *INDENT-OFF* */
static GOptionEntry defn_entries[] = {
{ "register", 'R', 0, G_OPTION_ARG_STRING, &options.register_dev,
"Register the named stonith device. Requires: --agent.\n"
INDENT "Optional: --option, --env-option.",
"DEVICE" },
{ "deregister", 'D', 0, G_OPTION_ARG_STRING, &options.unregister_dev,
"De-register the named stonith device.",
"DEVICE" },
{ "register-level", 'r', 0, G_OPTION_ARG_STRING, &options.register_level,
"Register a stonith level for the named target,\n"
INDENT "specified as one of NAME, @PATTERN, or ATTR=VALUE.\n"
INDENT "Requires: --index and one or more --device entries.",
"TARGET" },
{ "deregister-level", 'd', 0, G_OPTION_ARG_STRING, &options.unregister_level,
"Unregister a stonith level for the named target,\n"
INDENT "specified as for --register-level. Requires: --index",
"TARGET" },
{ NULL }
};
static GOptionEntry query_entries[] = {
{ "list", 'l', 0, G_OPTION_ARG_STRING, &options.terminate,
"List devices that can terminate the specified host.\n"
INDENT "Optional: --timeout",
"HOST" },
{ "list-registered", 'L', 0, G_OPTION_ARG_NONE, &options.registered,
"List all registered devices. Optional: --timeout.",
NULL },
{ "list-installed", 'I', 0, G_OPTION_ARG_NONE, &options.installed,
"List all installed devices. Optional: --timeout.",
NULL },
{ "list-targets", 's', 0, G_OPTION_ARG_STRING, &options.targets,
"List the targets that can be fenced by the\n"
INDENT "named device. Optional: --timeout.",
"DEVICE" },
{ "metadata", 'M', 0, G_OPTION_ARG_NONE, &options.metadata,
"Show agent metadata. Requires: --agent.\n"
INDENT "Optional: --timeout.",
NULL },
{ "query", 'Q', 0, G_OPTION_ARG_STRING, &options.query,
"Check the named device's status. Optional: --timeout.",
"DEVICE" },
{ "history", 'H', 0, G_OPTION_ARG_STRING, &options.history,
"Show last successful fencing operation for named node\n"
INDENT "(or '*' for all nodes). Optional: --timeout, --cleanup,\n"
INDENT "--quiet (show only the operation's epoch timestamp),\n"
INDENT "--verbose (show all recorded and pending operations),\n"
INDENT "--broadcast (update history from all nodes available).",
"NODE" },
{ "last", 'h', 0, G_OPTION_ARG_STRING, &options.last_fenced,
"Indicate when the named node was last fenced.\n"
INDENT "Optional: --as-node-id.",
"NODE" },
{ "validate", 'K', 0, G_OPTION_ARG_NONE, &options.validate_cfg,
"Validate a fence device configuration.\n"
INDENT "Requires: --agent. Optional: --option, --env-option,\n"
INDENT "--quiet (print no output, only return status).",
NULL },
{ NULL }
};
static GOptionEntry fence_entries[] = {
{ "fence", 'F', 0, G_OPTION_ARG_STRING, &options.fence_host,
"Fence named host. Optional: --timeout, --tolerance, --delay.",
"HOST" },
{ "unfence", 'U', 0, G_OPTION_ARG_STRING, &options.unfence_host,
"Unfence named host. Optional: --timeout, --tolerance, --delay.",
"HOST" },
{ "reboot", 'B', 0, G_OPTION_ARG_STRING, &options.reboot_host,
"Reboot named host. Optional: --timeout, --tolerance, --delay.",
"HOST" },
{ "confirm", 'C', 0, G_OPTION_ARG_STRING, &options.confirm_host,
"Tell cluster that named host is now safely down.",
"HOST", },
{ NULL }
};
static GOptionEntry addl_entries[] = {
{ "cleanup", 'c', 0, G_OPTION_ARG_NONE, &options.cleanup,
"Cleanup wherever appropriate. Requires --history.",
NULL },
{ "broadcast", 'b', 0, G_OPTION_ARG_NONE, &options.broadcast,
"Broadcast wherever appropriate.",
NULL },
{ "agent", 'a', 0, G_OPTION_ARG_STRING, &options.agent,
"The agent to use (for example, fence_xvm;\n"
INDENT "with --register, --metadata, --validate).",
"AGENT" },
{ "option", 'o', 0, G_OPTION_ARG_CALLBACK, add_stonith_params,
"Specify a device configuration parameter as NAME=VALUE\n"
INDENT "(may be specified multiple times; with --register,\n"
INDENT "--validate).",
"PARAM" },
{ "env-option", 'e', 0, G_OPTION_ARG_CALLBACK, add_env_params,
"Specify a device configuration parameter with the\n"
INDENT "specified name, using the value of the\n"
INDENT "environment variable of the same name prefixed with\n"
INDENT "OCF_RESKEY_ (may be specified multiple times;\n"
INDENT "with --register, --validate).",
"PARAM" },
{ "tag", 'T', 0, G_OPTION_ARG_CALLBACK, set_tag,
"Identify fencing operations in logs with the specified\n"
INDENT "tag; useful when multiple entities might invoke\n"
INDENT "stonith_admin (used with most commands).",
"TAG" },
{ "device", 'v', 0, G_OPTION_ARG_CALLBACK, add_stonith_device,
"Device ID (with --register-level, device to associate with\n"
INDENT "a given host and level; may be specified multiple times)"
#if SUPPORT_CIBSECRETS
"\n" INDENT "(with --validate, name to use to load CIB secrets)"
#endif
".",
"DEVICE" },
{ "index", 'i', 0, G_OPTION_ARG_INT, &options.fence_level,
"The stonith level (1-9) (with --register-level,\n"
INDENT "--deregister-level).",
"LEVEL" },
{ "timeout", 't', 0, G_OPTION_ARG_INT, &options.timeout,
"Operation timeout in seconds (default 120;\n"
INDENT "used with most commands).",
"SECONDS" },
{ "delay", 'y', 0, G_OPTION_ARG_INT, &options.delay,
"Apply a fencing delay in seconds. Any static/random delays from\n"
INDENT "pcmk_delay_base/max will be added, otherwise all\n"
INDENT "disabled with the value -1\n"
INDENT "(default 0; with --fence, --reboot, --unfence).",
"SECONDS" },
{ "as-node-id", 'n', 0, G_OPTION_ARG_NONE, &options.as_nodeid,
"(Advanced) The supplied node is the corosync node ID\n"
INDENT "(with --last).",
NULL },
{ "tolerance", 0, 0, G_OPTION_ARG_CALLBACK, add_tolerance,
"(Advanced) Do nothing if an equivalent --fence request\n"
INDENT "succeeded less than this many seconds earlier\n"
INDENT "(with --fence, --unfence, --reboot).",
"SECONDS" },
{ NULL }
};
/* *INDENT-ON* */
static pcmk__supported_format_t formats[] = {
PCMK__SUPPORTED_FORMAT_HTML,
PCMK__SUPPORTED_FORMAT_TEXT,
PCMK__SUPPORTED_FORMAT_XML,
{ NULL, NULL, NULL }
};
static const int st_opts = st_opt_sync_call | st_opt_allow_suicide;
static char *name = NULL;
gboolean
add_env_params(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
char *key = crm_strdup_printf("OCF_RESKEY_%s", optarg);
const char *env = getenv(key);
gboolean retval = TRUE;
if (env == NULL) {
g_set_error(error, PCMK__EXITC_ERROR, CRM_EX_INVALID_PARAM, "Invalid option: -e %s", optarg);
retval = FALSE;
} else {
crm_info("Got: '%s'='%s'", optarg, env);
options.params = stonith_key_value_add(options.params, optarg, env);
}
free(key);
return retval;
}
gboolean
add_stonith_device(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
options.devices = stonith_key_value_add(options.devices, NULL, optarg);
return TRUE;
}
gboolean
add_tolerance(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
options.tolerance = crm_get_msec(optarg) / 1000;
return TRUE;
}
gboolean
add_stonith_params(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
char *name = NULL;
char *value = NULL;
int rc = 0;
gboolean retval = TRUE;
crm_info("Scanning: -o %s", optarg);
rc = pcmk_scan_nvpair(optarg, &name, &value);
if (rc != 2) {
rc = pcmk_legacy2rc(rc);
g_set_error(error, PCMK__RC_ERROR, rc, "Invalid option: -o %s: %s", optarg, pcmk_rc_str(rc));
retval = FALSE;
} else {
crm_info("Got: '%s'='%s'", name, value);
options.params = stonith_key_value_add(options.params, name, value);
}
free(name);
free(value);
return retval;
}
gboolean
set_tag(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
free(name);
name = crm_strdup_printf("%s.%s", crm_system_name, optarg);
return TRUE;
}
static GOptionContext *
build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
GOptionContext *context = NULL;
GOptionEntry extra_prog_entries[] = {
{ "quiet", 'q', 0, G_OPTION_ARG_NONE, &(args->quiet),
"Be less descriptive in output.",
NULL },
{ NULL }
};
context = pcmk__build_arg_context(args, "text (default), html, xml", group, NULL);
/* Add the -q option, which cannot be part of the globally supported options
* because some tools use that flag for something else.
*/
pcmk__add_main_args(context, extra_prog_entries);
pcmk__add_arg_group(context, "definition", "Device Definition Commands:",
"Show device definition help", defn_entries);
pcmk__add_arg_group(context, "queries", "Queries:",
"Show query help", query_entries);
pcmk__add_arg_group(context, "fence", "Fencing Commands:",
"Show fence help", fence_entries);
pcmk__add_arg_group(context, "additional", "Additional Options:",
"Show additional options", addl_entries);
return context;
}
int
main(int argc, char **argv)
{
int rc = 0;
bool no_connect = false;
bool required_agent = false;
char *target = NULL;
const char *device = NULL;
crm_exit_t exit_code = CRM_EX_OK;
stonith_t *st = NULL;
pcmk__output_t *out = NULL;
pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
GError *error = NULL;
GOptionContext *context = NULL;
GOptionGroup *output_group = NULL;
gchar **processed_args = NULL;
context = build_arg_context(args, &output_group);
pcmk__register_formats(output_group, formats);
crm_log_cli_init("stonith_admin");
name = strdup(crm_system_name);
processed_args = pcmk__cmdline_preproc(argv, "adehilorstvBCDFHQRTU");
if (!g_option_context_parse_strv(context, &processed_args, &error)) {
fprintf(stderr, "%s: %s\n", g_get_prgname(), error->message);
exit_code = CRM_EX_USAGE;
goto done;
}
for (int i = 0; i < args->verbosity; i++) {
crm_bump_log_level(argc, argv);
}
rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
if (rc != pcmk_rc_ok) {
fprintf(stderr, "Error creating output format %s: %s\n",
args->output_ty, pcmk_rc_str(rc));
exit_code = CRM_EX_ERROR;
goto done;
}
stonith__register_messages(out);
if (args->version) {
out->version(out, false);
goto done;
}
if (options.validate_cfg) {
required_agent = true;
no_connect = true;
action = 'K';
}
if (options.installed) {
no_connect = true;
action = 'I';
}
if (options.registered) {
action = 'L';
}
if (options.register_dev != NULL) {
required_agent = true;
action = 'R';
device = options.register_dev;
}
if (options.query != NULL) {
action = 'Q';
device = options.query;
}
if (options.unregister_dev != NULL) {
action = 'D';
device = options.unregister_dev;
}
if (options.targets != NULL) {
action = 's';
device = options.targets;
}
if (options.terminate != NULL) {
action = 'L';
target = options.terminate;
}
if (options.metadata) {
no_connect = true;
required_agent = true;
action = 'M';
}
if (options.reboot_host != NULL) {
no_connect = true;
action = 'B';
target = options.reboot_host;
crm_log_args(argc, argv);
}
if (options.fence_host != NULL) {
no_connect = true;
action = 'F';
target = options.fence_host;
crm_log_args(argc, argv);
}
if (options.unfence_host != NULL) {
no_connect = true;
action = 'U';
target = options.unfence_host;
crm_log_args(argc, argv);
}
if (options.confirm_host != NULL) {
action = 'C';
target = options.confirm_host;
crm_log_args(argc, argv);
}
if (options.last_fenced != NULL) {
action = 'h';
target = options.last_fenced;
}
if (options.history != NULL) {
action = 'H';
target = options.history;
}
if (options.register_level != NULL) {
action = 'r';
target = options.register_level;
}
if (options.unregister_level != NULL) {
action = 'd';
target = options.unregister_level;
}
if (optind > argc || action == 0) {
char *help = g_option_context_get_help(context, TRUE, NULL);
out->err(out, "%s", help);
g_free(help);
exit_code = CRM_EX_USAGE;
goto done;
}
if (required_agent && options.agent == NULL) {
char *help = g_option_context_get_help(context, TRUE, NULL);
out->err(out, "Please specify an agent to query using -a,--agent [value]");
out->err(out, "%s", help);
g_free(help);
exit_code = CRM_EX_USAGE;
goto done;
}
+ out->quiet = args->quiet;
+
st = stonith_api_new();
if (st == NULL) {
rc = -ENOMEM;
} else if (!no_connect) {
rc = st->cmds->connect(st, name, NULL);
}
if (rc < 0) {
out->err(out, "Could not connect to fencer: %s", pcmk_strerror(rc));
exit_code = CRM_EX_DISCONNECT;
goto done;
}
switch (action) {
case 'I':
rc = pcmk__fence_installed(out, st, options.timeout*1000);
if (rc != pcmk_rc_ok) {
out->err(out, "Failed to list installed devices: %s", pcmk_strerror(rc));
}
break;
case 'L':
rc = pcmk__fence_registered(out, st, target, options.timeout*1000);
if (rc != pcmk_rc_ok) {
out->err(out, "Failed to list registered devices: %s", pcmk_strerror(rc));
}
break;
case 'Q':
rc = st->cmds->monitor(st, st_opts, device, options.timeout);
if (rc != pcmk_rc_ok) {
rc = st->cmds->list(st, st_opts, device, NULL, options.timeout);
}
rc = pcmk_legacy2rc(rc);
break;
case 's':
rc = pcmk__fence_list_targets(out, st, device, options.timeout*1000);
if (rc != pcmk_rc_ok) {
out->err(out, "Couldn't list targets: %s", pcmk_strerror(rc));
}
break;
case 'R':
rc = st->cmds->register_device(st, st_opts, device, NULL, options.agent,
options.params);
rc = pcmk_legacy2rc(rc);
break;
case 'D':
rc = st->cmds->remove_device(st, st_opts, device);
rc = pcmk_legacy2rc(rc);
break;
case 'd':
rc = pcmk__fence_unregister_level(st, target, options.fence_level);
break;
case 'r':
rc = pcmk__fence_register_level(st, target, options.fence_level, options.devices);
break;
case 'M':
rc = pcmk__fence_metadata(out, st, options.agent, options.timeout*1000);
if (rc != pcmk_rc_ok) {
out->err(out, "Can't get fence agent meta-data: %s", pcmk_strerror(rc));
}
break;
case 'C':
rc = st->cmds->confirm(st, st_opts, target);
rc = pcmk_legacy2rc(rc);
break;
case 'B':
rc = pcmk__fence_action(st, target, "reboot", name, options.timeout*1000,
options.tolerance*1000, options.delay);
break;
case 'F':
rc = pcmk__fence_action(st, target, "off", name, options.timeout*1000,
options.tolerance*1000, options.delay);
break;
case 'U':
rc = pcmk__fence_action(st, target, "on", name, options.timeout*1000,
options.tolerance*1000, options.delay);
break;
case 'h':
rc = pcmk__fence_last(out, target, options.as_nodeid);
break;
case 'H':
- rc = pcmk__fence_history(out, st, target, options.timeout*1000, args->quiet,
- args->verbosity, options.broadcast, options.cleanup);
+ rc = pcmk__fence_history(out, st, target, options.timeout*1000, args->verbosity,
+ options.broadcast, options.cleanup);
break;
case 'K':
device = options.devices ? options.devices->key : NULL;
rc = pcmk__fence_validate(out, st, options.agent, device, options.params,
options.timeout*1000);
break;
}
crm_info("Command returned: %s (%d)", pcmk_rc_str(rc), rc);
exit_code = pcmk_rc2exitc(rc);
done:
g_strfreev(processed_args);
g_clear_error(&error);
pcmk__free_arg_context(context);
if (out != NULL) {
out->finish(out, exit_code, true, NULL);
pcmk__output_free(out);
}
free(name);
stonith_key_value_freeall(options.params, 1, 1);
if (st != NULL) {
st->cmds->disconnect(st);
stonith_api_delete(st);
}
return exit_code;
}

File Metadata

Mime Type
text/x-diff
Expires
Sat, Nov 23, 3:53 AM (4 h, 16 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1018178
Default Alt Text
(134 KB)

Event Timeline