This source file includes following definitions.
- build_arg_context
- G_GNUC_PRINTF
- G_GNUC_PRINTF
- main
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11 #include <crm/crm.h>
12 #include <crm/common/cmdline_internal.h>
13 #include <crm/common/output_internal.h>
14
15 #include <stdio.h>
16 #include <sys/types.h>
17 #include <unistd.h>
18
19 #include <stdlib.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <libgen.h>
23 #include <glib.h>
24
25 #include <crm/common/xml.h>
26 #include <crm/common/util.h>
27 #include <crm/cib.h>
28 #include <crm/cib/internal.h>
29 #include <crm/pengine/status.h>
30 #include <pacemaker-internal.h>
31
32 const char *SUMMARY = "Check a Pacemaker configuration for errors\n\n"
33 "Check the well-formedness of a complete Pacemaker XML configuration,\n"
34 "its conformance to the configured schema, and the presence of common\n"
35 "misconfigurations. Problems reported as errors must be fixed before the\n"
36 "cluster will work properly. It is left to the administrator to decide\n"
37 "whether to fix problems reported as warnings.";
38
39 struct {
40 char *cib_save;
41 gboolean use_live_cib;
42 char *xml_file;
43 gboolean xml_stdin;
44 char *xml_string;
45 unsigned int verbosity;
46 } options;
47
48 static GOptionEntry data_entries[] = {
49 { "live-check", 'L', 0, G_OPTION_ARG_NONE,
50 &options.use_live_cib, "Check the configuration used by the running cluster",
51 NULL },
52 { "xml-file", 'x', 0, G_OPTION_ARG_FILENAME,
53 &options.xml_file, "Check the configuration in the named file",
54 "FILE" },
55 { "xml-pipe", 'p', 0, G_OPTION_ARG_NONE,
56 &options.xml_stdin, "Check the configuration piped in via stdin",
57 NULL },
58 { "xml-text", 'X', 0, G_OPTION_ARG_STRING,
59 &options.xml_string, "Check the configuration in the supplied string",
60 "XML" },
61
62 { NULL }
63 };
64
65 static GOptionEntry addl_entries[] = {
66 { "save-xml", 'S', G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME,
67 &options.cib_save, "Save verified XML to named file (most useful with -L)",
68 "FILE" },
69
70 { NULL }
71 };
72
73 static pcmk__supported_format_t formats[] = {
74 PCMK__SUPPORTED_FORMAT_NONE,
75 PCMK__SUPPORTED_FORMAT_TEXT,
76 PCMK__SUPPORTED_FORMAT_XML,
77 { NULL, NULL, NULL }
78 };
79
80 static GOptionContext *
81 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
82 GOptionContext *context = NULL;
83
84 const char *description = "Examples:\n\n"
85 "Check the consistency of the configuration in the running cluster:\n\n"
86 "\tcrm_verify --live-check\n\n"
87 "Check the consistency of the configuration in a given file and "
88 "produce quiet output:\n\n"
89 "\tcrm_verify --xml-file file.xml --quiet\n\n"
90 "Check the consistency of the configuration in a given file and "
91 "produce verbose output:\n\n"
92 "\tcrm_verify --xml-file file.xml --verbose\n\n";
93
94 GOptionEntry extra_prog_entries[] = {
95 { "quiet", 'q', 0, G_OPTION_ARG_NONE, &(args->quiet),
96 "Don't print verify information",
97 NULL },
98 { NULL }
99 };
100
101 context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
102
103 pcmk__add_main_args(context, extra_prog_entries);
104
105 g_option_context_set_description(context, description);
106
107 pcmk__add_arg_group(context, "data", "Data sources:",
108 "Show data options", data_entries);
109 pcmk__add_arg_group(context, "additional", "Additional options:",
110 "Show additional options", addl_entries);
111
112 return context;
113 }
114
115
116
117
118
119
120
121
122
123 G_GNUC_PRINTF(2, 3)
124 static void
125 output_config_error(void *ctx, const char *msg, ...)
126 {
127 va_list ap;
128 char *buf = NULL;
129 pcmk__output_t *out = ctx;
130
131 va_start(ap, msg);
132 pcmk__assert(vasprintf(&buf, msg, ap) > 0);
133 if (options.verbosity > 0) {
134 out->err(out, "error: %s", buf);
135 }
136 va_end(ap);
137 }
138
139
140
141
142
143
144
145
146
147 G_GNUC_PRINTF(2, 3)
148 static void
149 output_config_warning(void *ctx, const char *msg, ...)
150 {
151 va_list ap;
152 char *buf = NULL;
153 pcmk__output_t *out = ctx;
154
155 va_start(ap, msg);
156 pcmk__assert(vasprintf(&buf, msg, ap) > 0);
157 if (options.verbosity > 0) {
158 out->err(out, "warning: %s", buf);
159 }
160 va_end(ap);
161 }
162
163 int
164 main(int argc, char **argv)
165 {
166
167 pcmk_scheduler_t *scheduler = NULL;
168
169 int rc = pcmk_rc_ok;
170 crm_exit_t exit_code = CRM_EX_OK;
171
172 GError *error = NULL;
173
174 pcmk__output_t *out = NULL;
175
176 const char *cib_source = NULL;
177 xmlNode *cib_object = NULL;
178
179 GOptionGroup *output_group = NULL;
180
181 const char *failure_type = NULL;
182
183 pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
184 gchar **processed_args = pcmk__cmdline_preproc(argv, "xSX");
185 GOptionContext *context = build_arg_context(args, &output_group);
186
187 pcmk__register_formats(output_group, formats);
188 if (!g_option_context_parse_strv(context, &processed_args, &error)) {
189 exit_code = CRM_EX_USAGE;
190 goto done;
191 }
192
193 if (args->verbosity > 0) {
194 args->verbosity -= args->quiet;
195 }
196
197 pcmk__cli_init_logging("crm_verify", args->verbosity);
198
199 rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
200 if (rc != pcmk_rc_ok) {
201 exit_code = CRM_EX_ERROR;
202 g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Error creating output format %s: %s",
203 args->output_ty, pcmk_rc_str(rc));
204 goto done;
205 }
206
207 if (args->version) {
208 out->version(out, false);
209 goto done;
210 }
211
212 pcmk__register_lib_messages(out);
213
214 pcmk__set_config_error_handler(output_config_error, out);
215 pcmk__set_config_warning_handler(output_config_warning, out);
216
217 if (pcmk__str_eq(args->output_ty, "xml", pcmk__str_none)) {
218 args->verbosity = 1;
219 }
220 options.verbosity = args->verbosity;
221
222 if (options.xml_file != NULL) {
223 cib_source = options.xml_file;
224 } else if (options.xml_string != NULL) {
225 cib_source = options.xml_string;
226 } else if (options.xml_stdin) {
227 cib_source = "-";
228 } else if (options.use_live_cib) {
229 cib_source = NULL;
230 } else {
231 rc = ENODATA;
232 g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "No input specified");
233 goto done;
234 }
235
236 rc = pcmk__parse_cib(out, cib_source, &cib_object);
237
238 if (rc != pcmk_rc_ok) {
239 g_set_error(&error, PCMK__EXITC_ERROR, rc, "Couldn't parse input");
240 goto done;
241 }
242
243 if (options.cib_save != NULL) {
244 pcmk__xml_write_file(cib_object, options.cib_save, false, NULL);
245 }
246
247 scheduler = pe_new_working_set();
248
249 if (scheduler == NULL) {
250 rc = errno;
251 g_set_error(&error, PCMK__RC_ERROR, rc,
252 "Could not allocate scheduler data: %s", pcmk_rc_str(rc));
253 goto done;
254 }
255
256 scheduler->priv = out;
257
258 rc = pcmk__verify(scheduler, out, &cib_object);
259
260 if (rc == pcmk_rc_schema_validation) {
261 if (crm_config_error) {
262 failure_type = "Errors found during check: ";
263 } else if (crm_config_warning) {
264 failure_type = "Warnings found during check: ";
265 } else {
266 failure_type = "";
267 }
268
269 if (args->quiet) {
270
271
272 } else if (options.verbosity > 0) {
273 out->err(out, "%sconfig not valid", failure_type);
274
275 } else {
276 out->err(out, "%sconfig not valid\n-V may provide more details", failure_type);
277 }
278 }
279
280 pe_free_working_set(scheduler);
281
282 done:
283 g_strfreev(processed_args);
284 pcmk__free_arg_context(context);
285 free(options.cib_save);
286 free(options.xml_file);
287 free(options.xml_string);
288
289 if (cib_object != NULL) {
290 free_xml(cib_object);
291 }
292
293 if (exit_code == CRM_EX_OK) {
294 exit_code = pcmk_rc2exitc(rc);
295 }
296
297 pcmk__output_and_clear_error(&error, out);
298
299 if (out != NULL) {
300 out->finish(out, exit_code, true, NULL);
301 pcmk__output_free(out);
302 }
303
304 pcmk__unregister_formats();
305 crm_exit(exit_code);
306 }