This source file includes following definitions.
- date_now_cb
- modifier_cb
- log_time_period
- 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 struct {
20 char *date_time_s;
21 gchar *duration_s;
22 gchar *expected_s;
23 gchar *period_s;
24 int print_options;
25 } options;
26
27 #define INDENT " "
28
29 static gboolean
30 date_now_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
31 if (pcmk__str_any_of(option_name, "--now", "-n", NULL)) {
32 pcmk__str_update(&options.date_time_s, "now");
33 } else if (pcmk__str_any_of(option_name, "--date", "-d", NULL)) {
34 pcmk__str_update(&options.date_time_s, optarg);
35 }
36
37 return TRUE;
38 }
39
40 static gboolean
41 modifier_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
42 if (pcmk__str_any_of(option_name, "--seconds", "-s", NULL)) {
43 options.print_options |= crm_time_seconds;
44 } else if (pcmk__str_any_of(option_name, "--epoch", "-S", NULL)) {
45 options.print_options |= crm_time_epoch;
46 } else if (pcmk__str_any_of(option_name, "--local", "-L", NULL)) {
47 options.print_options |= crm_time_log_with_timezone;
48 } else if (pcmk__str_any_of(option_name, "--ordinal", "-O", NULL)) {
49 options.print_options |= crm_time_ordinal;
50 } else if (pcmk__str_any_of(option_name, "--week", "-W", NULL)) {
51 options.print_options |= crm_time_weeks;
52 }
53
54 return TRUE;
55 }
56
57 static GOptionEntry command_entries[] = {
58 { "now", 'n', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, date_now_cb,
59 "Display the current date/time",
60 NULL },
61
62 { "date", 'd', 0, G_OPTION_ARG_CALLBACK, date_now_cb,
63 "Parse an ISO 8601 date/time (for example,\n"
64 INDENT "'2019-09-24 00:30:00 +01:00' or '2019-040')",
65 "DATE" },
66
67 { "period", 'p', 0, G_OPTION_ARG_STRING, &options.period_s,
68 "Parse an ISO 8601 period (interval) with start time (for example,\n"
69 INDENT "'2005-040/2005-043')",
70 "PERIOD" },
71
72 { "duration", 'D', 0, G_OPTION_ARG_STRING, &options.duration_s,
73 "Parse an ISO 8601 duration (for example, 'P1M')",
74 "DURATION" },
75
76 { "expected", 'E', 0, G_OPTION_ARG_STRING, &options.expected_s,
77 "Exit with error status if result does not match this text.\n"
78 INDENT "Requires: -n or -d",
79 "TEXT" },
80
81 { NULL }
82 };
83
84 static GOptionEntry modifier_entries[] = {
85 { "seconds", 's', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, modifier_cb,
86 "Show result as a seconds since 0000-001 00:00:00Z",
87 NULL },
88
89 { "epoch", 'S', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, modifier_cb,
90 "Show result as a seconds since EPOCH (1970-001 00:00:00Z)",
91 NULL },
92
93 { "local", 'L', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, modifier_cb,
94 "Show result as a 'local' date/time",
95 NULL },
96
97 { "ordinal", 'O', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, modifier_cb,
98 "Show result as an 'ordinal' date/time",
99 NULL },
100
101 { "week", 'W', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, modifier_cb,
102 "Show result as an 'calendar week' date/time",
103 NULL },
104
105 { NULL }
106 };
107
108 static void
109 log_time_period(int log_level, crm_time_period_t * dtp, int flags)
110 {
111 char *start = crm_time_as_string(dtp->start, flags);
112 char *end = crm_time_as_string(dtp->end, flags);
113
114 CRM_ASSERT(start != NULL && end != NULL);
115 do_crm_log(log_level, "Period: %s to %s", start, end);
116 free(start);
117 free(end);
118 }
119
120 static GOptionContext *
121 build_arg_context(pcmk__common_args_t *args) {
122 GOptionContext *context = NULL;
123
124 const char *description = "For more information on the ISO 8601 standard, see " \
125 "https://en.wikipedia.org/wiki/ISO_8601";
126
127 context = pcmk__build_arg_context(args, NULL, NULL, NULL);
128 g_option_context_set_description(context, description);
129
130 pcmk__add_arg_group(context, "commands", "Commands:",
131 "Show command options", command_entries);
132 pcmk__add_arg_group(context, "modifiers", "Output modifiers:",
133 "Show output modifiers", modifier_entries);
134
135 return context;
136 }
137
138 int
139 main(int argc, char **argv)
140 {
141 crm_exit_t exit_code = CRM_EX_OK;
142 crm_time_t *duration = NULL;
143 crm_time_t *date_time = NULL;
144
145 GError *error = NULL;
146
147 pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
148 GOptionContext *context = build_arg_context(args);
149 gchar **processed_args = pcmk__cmdline_preproc(argv, "dpDE");
150
151 if (!g_option_context_parse_strv(context, &processed_args, &error)) {
152 exit_code = CRM_EX_USAGE;
153 goto done;
154 }
155
156 pcmk__cli_init_logging("iso8601", args->verbosity);
157
158 if (args->version) {
159 g_strfreev(processed_args);
160 pcmk__free_arg_context(context);
161
162 pcmk__cli_help('v', CRM_EX_OK);
163 }
164
165 if (pcmk__str_eq("now", options.date_time_s, pcmk__str_casei)) {
166 date_time = crm_time_new(NULL);
167
168 if (date_time == NULL) {
169 exit_code = CRM_EX_SOFTWARE;
170 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
171 "Internal error: couldn't determine 'now'!");
172 goto done;
173 }
174
175 crm_time_log(LOG_TRACE, "Current date/time", date_time,
176 crm_time_ordinal | crm_time_log_date | crm_time_log_timeofday);
177 crm_time_log(LOG_STDOUT, "Current date/time", date_time,
178 options.print_options | crm_time_log_date | crm_time_log_timeofday);
179
180 } else if (options.date_time_s) {
181 date_time = crm_time_new(options.date_time_s);
182
183 if (date_time == NULL) {
184 exit_code = CRM_EX_INVALID_PARAM;
185 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
186 "Invalid date/time specified: %s", options.date_time_s);
187 goto done;
188 }
189
190 crm_time_log(LOG_TRACE, "Date", date_time,
191 crm_time_ordinal | crm_time_log_date | crm_time_log_timeofday);
192 crm_time_log(LOG_STDOUT, "Date", date_time,
193 options.print_options | crm_time_log_date | crm_time_log_timeofday);
194 }
195
196 if (options.duration_s) {
197 duration = crm_time_parse_duration(options.duration_s);
198
199 if (duration == NULL) {
200 exit_code = CRM_EX_INVALID_PARAM;
201 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
202 "Invalid duration specified: %s", options.duration_s);
203 goto done;
204 }
205
206 crm_time_log(LOG_TRACE, "Duration", duration, crm_time_log_duration);
207 crm_time_log(LOG_STDOUT, "Duration", duration,
208 options.print_options | crm_time_log_duration);
209 }
210
211 if (options.period_s) {
212 crm_time_period_t *period = crm_time_parse_period(options.period_s);
213
214 if (period == NULL) {
215 exit_code = CRM_EX_INVALID_PARAM;
216 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
217 "Invalid interval specified: %s", options.period_s);
218 goto done;
219 }
220
221 log_time_period(LOG_TRACE, period,
222 options.print_options | crm_time_log_date | crm_time_log_timeofday);
223 log_time_period(LOG_STDOUT, period,
224 options.print_options | crm_time_log_date | crm_time_log_timeofday);
225 crm_time_free_period(period);
226 }
227
228 if (date_time && duration) {
229 crm_time_t *later = crm_time_add(date_time, duration);
230
231 if (later == NULL) {
232 exit_code = CRM_EX_SOFTWARE;
233 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
234 "Unable to calculate ending time of %s plus %s",
235 options.date_time_s, options.duration_s);
236 goto done;
237 }
238
239 crm_time_log(LOG_TRACE, "Duration ends at", later,
240 crm_time_ordinal | crm_time_log_date | crm_time_log_timeofday);
241 crm_time_log(LOG_STDOUT, "Duration ends at", later,
242 options.print_options | crm_time_log_date | crm_time_log_timeofday |
243 crm_time_log_with_timezone);
244
245 if (options.expected_s) {
246 char *dt_s = crm_time_as_string(later,
247 options.print_options | crm_time_log_date |
248 crm_time_log_timeofday);
249 if (!pcmk__str_eq(options.expected_s, dt_s, pcmk__str_casei)) {
250 exit_code = CRM_EX_ERROR;
251 goto done;
252 }
253 free(dt_s);
254 }
255 crm_time_free(later);
256
257 } else if (date_time && options.expected_s) {
258 char *dt_s = crm_time_as_string(date_time,
259 options.print_options | crm_time_log_date | crm_time_log_timeofday);
260
261 if (!pcmk__str_eq(options.expected_s, dt_s, pcmk__str_casei)) {
262 exit_code = CRM_EX_ERROR;
263 goto done;
264 }
265 free(dt_s);
266 }
267
268 done:
269 crm_time_free(date_time);
270 crm_time_free(duration);
271
272 g_strfreev(processed_args);
273 pcmk__free_arg_context(context);
274
275 free(options.date_time_s);
276 g_free(options.duration_s);
277 g_free(options.expected_s);
278 g_free(options.period_s);
279
280 pcmk__output_and_clear_error(error, NULL);
281 crm_exit(exit_code);
282 }