This source file includes following definitions.
- date_now_cb
- modifier_cb
- PCMK__OUTPUT_ARGS
- PCMK__OUTPUT_ARGS
- PCMK__OUTPUT_ARGS
- PCMK__OUTPUT_ARGS
- PCMK__OUTPUT_ARGS
- PCMK__OUTPUT_ARGS
- PCMK__OUTPUT_ARGS
- PCMK__OUTPUT_ARGS
- build_arg_context
- 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/iso8601.h>
14 #include <crm/common/util.h>
15 #include <unistd.h>
16
17 #define SUMMARY "Display and parse ISO 8601 dates and times"
18
19 static pcmk__supported_format_t formats[] = {
20 PCMK__SUPPORTED_FORMAT_NONE,
21 PCMK__SUPPORTED_FORMAT_TEXT,
22 PCMK__SUPPORTED_FORMAT_XML,
23 { NULL, NULL, NULL }
24 };
25
26 struct {
27 char *date_time_s;
28 gchar *duration_s;
29 gchar *expected_s;
30 gchar *period_s;
31 int print_options;
32 } options;
33
34 #define INDENT " "
35
36 static gboolean
37 date_now_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
38 if (pcmk__str_any_of(option_name, "--now", "-n", NULL)) {
39 pcmk__str_update(&options.date_time_s, "now");
40 } else if (pcmk__str_any_of(option_name, "--date", "-d", NULL)) {
41 pcmk__str_update(&options.date_time_s, optarg);
42 }
43
44 return TRUE;
45 }
46
47 static gboolean
48 modifier_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
49 if (pcmk__str_any_of(option_name, "--seconds", "-s", NULL)) {
50 options.print_options |= crm_time_seconds;
51 } else if (pcmk__str_any_of(option_name, "--epoch", "-S", NULL)) {
52 options.print_options |= crm_time_epoch;
53 } else if (pcmk__str_any_of(option_name, "--local", "-L", NULL)) {
54 options.print_options |= crm_time_log_with_timezone;
55 } else if (pcmk__str_any_of(option_name, "--ordinal", "-O", NULL)) {
56 options.print_options |= crm_time_ordinal;
57 } else if (pcmk__str_any_of(option_name, "--week", "-W", NULL)) {
58 options.print_options |= crm_time_weeks;
59 }
60
61 return TRUE;
62 }
63
64 static GOptionEntry command_entries[] = {
65 { "now", 'n', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, date_now_cb,
66 "Display the current date/time",
67 NULL },
68
69 { "date", 'd', 0, G_OPTION_ARG_CALLBACK, date_now_cb,
70 "Parse an ISO 8601 date/time (for example,\n"
71 INDENT "'2019-09-24 00:30:00 +01:00' or '2019-040')",
72 "DATE" },
73
74 { "period", 'p', 0, G_OPTION_ARG_STRING, &options.period_s,
75 "Parse an ISO 8601 period (interval) with start time (for example,\n"
76 INDENT "'2005-040/2005-043')",
77 "PERIOD" },
78
79 { "duration", 'D', 0, G_OPTION_ARG_STRING, &options.duration_s,
80 "Parse an ISO 8601 duration (for example, 'P1M')",
81 "DURATION" },
82
83 { "expected", 'E', 0, G_OPTION_ARG_STRING, &options.expected_s,
84 "Exit with error status if result does not match this text.\n"
85 INDENT "Requires: -n or -d",
86 "TEXT" },
87
88 { NULL }
89 };
90
91 static GOptionEntry modifier_entries[] = {
92 { "seconds", 's', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, modifier_cb,
93 "Show result as a seconds since 0000-001 00:00:00Z",
94 NULL },
95
96 { "epoch", 'S', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, modifier_cb,
97 "Show result as a seconds since EPOCH (1970-001 00:00:00Z)",
98 NULL },
99
100 { "local", 'L', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, modifier_cb,
101 "Show result as a 'local' date/time",
102 NULL },
103
104 { "ordinal", 'O', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, modifier_cb,
105 "Show result as an 'ordinal' date/time",
106 NULL },
107
108 { "week", 'W', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, modifier_cb,
109 "Show result as an 'calendar week' date/time",
110 NULL },
111
112 { NULL }
113 };
114
115 PCMK__OUTPUT_ARGS("date", "const char *", "crm_time_t *", "int")
116 static int
117 date_default(pcmk__output_t *out, va_list args)
118 {
119 const char *prefix = va_arg(args, const char *);
120 crm_time_t *date = va_arg(args, crm_time_t *);
121 int opts = va_arg(args, int);
122
123 char *date_s = NULL;
124
125 opts |= crm_time_log_date | crm_time_log_timeofday;
126 date_s = crm_time_as_string(date, opts);
127
128 out->info(out, "%s: %s", prefix, date_s);
129
130 free(date_s);
131 return pcmk_rc_ok;
132 }
133
134 PCMK__OUTPUT_ARGS("date", "const char *", "crm_time_t *", "int")
135 static int
136 date_xml(pcmk__output_t *out, va_list args)
137 {
138 const char *prefix G_GNUC_UNUSED = va_arg(args, const char *);
139 crm_time_t *date = va_arg(args, crm_time_t *);
140 int opts = va_arg(args, int);
141
142 char *date_s = NULL;
143
144 opts |= crm_time_log_date | crm_time_log_timeofday;
145 date_s = crm_time_as_string(date, opts);
146
147 pcmk__output_create_xml_text_node(out, PCMK_XE_DATE, date_s);
148 free(date_s);
149 return pcmk_rc_ok;
150 }
151
152 PCMK__OUTPUT_ARGS("duration", "crm_time_t *", "int")
153 static int
154 duration_default(pcmk__output_t *out, va_list args)
155 {
156 crm_time_t *time = va_arg(args, crm_time_t *);
157 int opts = va_arg(args, int);
158
159 char *date_s = crm_time_as_string(time, opts | crm_time_log_duration);
160
161 out->info(out, "Duration: %s", date_s);
162
163 free(date_s);
164 return pcmk_rc_ok;
165 }
166
167 PCMK__OUTPUT_ARGS("duration", "crm_time_t *", "int")
168 static int
169 duration_xml(pcmk__output_t *out, va_list args)
170 {
171 crm_time_t *time = va_arg(args, crm_time_t *);
172 int opts = va_arg(args, int);
173
174 char *date_s = crm_time_as_string(time, opts | crm_time_log_duration);
175
176 pcmk__output_create_xml_text_node(out, PCMK_XE_DURATION, date_s);
177 free(date_s);
178 return pcmk_rc_ok;
179 }
180
181 PCMK__OUTPUT_ARGS("duration_ends", "crm_time_t *", "int")
182 static int
183 duration_ends_default(pcmk__output_t *out, va_list args)
184 {
185 crm_time_t *time = va_arg(args, crm_time_t *);
186 int opts = va_arg(args, int);
187
188 char *date_s = NULL;
189
190 opts |= crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone;
191 date_s = crm_time_as_string(time, opts);
192
193 out->info(out, "Duration ends at: %s", date_s);
194
195 free(date_s);
196 return pcmk_rc_ok;
197 }
198
199 PCMK__OUTPUT_ARGS("duration_ends", "crm_time_t *", "int")
200 static int
201 duration_ends_xml(pcmk__output_t *out, va_list args)
202 {
203 crm_time_t *time = va_arg(args, crm_time_t *);
204 int opts = va_arg(args, int);
205
206 char *date_s = NULL;
207
208 opts |= crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone;
209 date_s = crm_time_as_string(time, opts);
210
211 pcmk__output_create_xml_text_node(out, PCMK_XE_DURATION_ENDS, date_s);
212 free(date_s);
213 return pcmk_rc_ok;
214 }
215
216 PCMK__OUTPUT_ARGS("period", "crm_time_period_t *", "int")
217 static int
218 period_default(pcmk__output_t *out, va_list args)
219 {
220 crm_time_period_t *period = va_arg(args, crm_time_period_t *);
221 int opts = va_arg(args, int);
222
223 char *start = NULL;
224 char *end = NULL;
225
226 opts |= crm_time_log_date | crm_time_log_timeofday;
227
228 start = crm_time_as_string(period->start, opts);
229 if (start == NULL) {
230 return pcmk_rc_no_output;
231 }
232
233 end = crm_time_as_string(period->end, opts);
234 if (end == NULL) {
235 free(start);
236 return pcmk_rc_no_output;
237 }
238
239 out->info(out, "Period: %s to %s", start, end);
240
241 free(start);
242 free(end);
243 return pcmk_rc_ok;
244 }
245
246 PCMK__OUTPUT_ARGS("period", "crm_time_period_t *", "int")
247 static int
248 period_xml(pcmk__output_t *out, va_list args)
249 {
250 crm_time_period_t *period = va_arg(args, crm_time_period_t *);
251 int opts = va_arg(args, int);
252
253 char *start = NULL;
254 char *end = NULL;
255
256 opts |= crm_time_log_date | crm_time_log_timeofday;
257
258 start = crm_time_as_string(period->start, opts);
259 if (start == NULL) {
260 return pcmk_rc_no_output;
261 }
262
263 end = crm_time_as_string(period->end, opts);
264 if (end == NULL) {
265 free(start);
266 return pcmk_rc_no_output;
267 }
268
269 pcmk__output_xml_create_parent(out, PCMK_XE_PERIOD, NULL);
270 pcmk__output_create_xml_text_node(out, PCMK_XE_START, start);
271 pcmk__output_create_xml_text_node(out, PCMK_XE_END, end);
272
273 free(start);
274 free(end);
275 return pcmk_rc_ok;
276 }
277
278 static GOptionContext *
279 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group)
280 {
281 GOptionContext *context = NULL;
282
283 const char *description = "For more information on the ISO 8601 standard, see " \
284 "https://en.wikipedia.org/wiki/ISO_8601";
285
286 context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
287 g_option_context_set_description(context, description);
288
289 pcmk__add_arg_group(context, "commands", "Commands:",
290 "Show command options", command_entries);
291 pcmk__add_arg_group(context, "modifiers", "Output modifiers:",
292 "Show output modifiers", modifier_entries);
293
294 return context;
295 }
296
297 static pcmk__message_entry_t fmt_functions[] = {
298 { "date", "default", date_default },
299 { "date", "xml", date_xml },
300 { "duration", "default", duration_default },
301 { "duration", "xml", duration_xml },
302 { "duration_ends", "default", duration_ends_default },
303 { "duration_ends", "xml", duration_ends_xml },
304 { "period", "default", period_default },
305 { "period", "xml", period_xml },
306
307 { NULL, NULL, NULL }
308 };
309
310 int
311 main(int argc, char **argv)
312 {
313 int rc = pcmk_rc_ok;
314 crm_exit_t exit_code = CRM_EX_OK;
315 crm_time_t *duration = NULL;
316 crm_time_t *date_time = NULL;
317
318 GError *error = NULL;
319 pcmk__output_t *out = NULL;
320
321 GOptionGroup *output_group = NULL;
322 pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
323 GOptionContext *context = build_arg_context(args, &output_group);
324 gchar **processed_args = pcmk__cmdline_preproc(argv, "dpDE");
325
326 pcmk__register_formats(output_group, formats);
327 if (!g_option_context_parse_strv(context, &processed_args, &error)) {
328 exit_code = CRM_EX_USAGE;
329 goto done;
330 }
331
332 pcmk__cli_init_logging("iso8601", args->verbosity);
333
334 rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
335 if (rc != pcmk_rc_ok) {
336 exit_code = pcmk_rc2exitc(rc);
337 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
338 "Error creating output format %s: %s", args->output_ty,
339 pcmk_rc_str(rc));
340 goto done;
341 }
342
343 if (args->version) {
344 out->version(out, false);
345 goto done;
346 }
347
348 pcmk__register_messages(out, fmt_functions);
349
350 if (pcmk__str_eq("now", options.date_time_s, pcmk__str_casei)) {
351 date_time = crm_time_new(NULL);
352
353 if (date_time == NULL) {
354 exit_code = CRM_EX_SOFTWARE;
355 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
356 "Internal error: couldn't determine 'now'!");
357 goto done;
358 }
359
360 out->message(out, "date", "Current date/time", date_time,
361 options.print_options);
362
363 } else if (options.date_time_s) {
364 date_time = crm_time_new(options.date_time_s);
365
366 if (date_time == NULL) {
367 exit_code = CRM_EX_INVALID_PARAM;
368 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
369 "Invalid date/time specified: %s", options.date_time_s);
370 goto done;
371 }
372
373 out->message(out, "date", "Date", date_time, options.print_options);
374 }
375
376 if (options.duration_s) {
377 duration = crm_time_parse_duration(options.duration_s);
378
379 if (duration == NULL) {
380 exit_code = CRM_EX_INVALID_PARAM;
381 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
382 "Invalid duration specified: %s", options.duration_s);
383 goto done;
384 }
385
386 out->message(out, "duration", duration, options.print_options);
387 }
388
389 if (options.period_s) {
390 crm_time_period_t *period = crm_time_parse_period(options.period_s);
391
392 if (period == NULL) {
393 exit_code = CRM_EX_INVALID_PARAM;
394 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
395 "Invalid interval specified: %s", options.period_s);
396 goto done;
397 }
398
399 out->message(out, "period", period, options.print_options);
400 crm_time_free_period(period);
401 }
402
403 if (date_time && duration) {
404 crm_time_t *later = crm_time_add(date_time, duration);
405
406 if (later == NULL) {
407 exit_code = CRM_EX_SOFTWARE;
408 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
409 "Unable to calculate ending time of %s plus %s",
410 options.date_time_s, options.duration_s);
411 goto done;
412 }
413
414 out->message(out, "duration_ends", later, options.print_options);
415
416 if (options.expected_s) {
417 char *dt_s = crm_time_as_string(later,
418 options.print_options | crm_time_log_date |
419 crm_time_log_timeofday);
420 if (!pcmk__str_eq(options.expected_s, dt_s, pcmk__str_casei)) {
421 exit_code = CRM_EX_ERROR;
422 goto done;
423 }
424 free(dt_s);
425 }
426 crm_time_free(later);
427
428 } else if (date_time && options.expected_s) {
429 char *dt_s = crm_time_as_string(date_time,
430 options.print_options | crm_time_log_date | crm_time_log_timeofday);
431
432 if (!pcmk__str_eq(options.expected_s, dt_s, pcmk__str_casei)) {
433 exit_code = CRM_EX_ERROR;
434 goto done;
435 }
436 free(dt_s);
437 }
438
439 done:
440 crm_time_free(date_time);
441 crm_time_free(duration);
442
443 g_strfreev(processed_args);
444 pcmk__free_arg_context(context);
445
446 free(options.date_time_s);
447 g_free(options.duration_s);
448 g_free(options.expected_s);
449 g_free(options.period_s);
450
451 pcmk__output_and_clear_error(&error, out);
452
453 if (out != NULL) {
454 out->finish(out, exit_code, true, NULL);
455 pcmk__output_free(out);
456 }
457
458 pcmk__unregister_formats();
459 crm_exit(exit_code);
460 }