1 /*
2  * Copyright 2005-2020 the Pacemaker project contributors
3  *
4  * The version control history for this file may have further details.
5  *
6  * This source code is licensed under the GNU General Public License version 2
7  * or later (GPLv2+) WITHOUT ANY WARRANTY.
8  */
9 
10 #include <crm_internal.h>
11 #include <crm/crm.h>
12 #include <crm/common/iso8601.h>
13 #include <crm/common/util.h>  /* CRM_ASSERT */
14 #include <unistd.h>
15 
16 char command = 0;
17 
18 static pcmk__cli_option_t long_options[] = {
19     // long option, argument type, storage, short option, description, flags
20     {
21         "help", no_argument, NULL, '?',
22         "\tThis text", pcmk__option_default
23     },
24     {
25         "version", no_argument, NULL, '$',
26         "\tVersion information", pcmk__option_default
27     },
28     {
29         "verbose", no_argument, NULL, 'V',
30         "\tIncrease debug output", pcmk__option_default
31     },
32     {
33         "-spacer-", no_argument, NULL, '-',
34         "\nCommands:", pcmk__option_default
35     },
36     {
37         "now", no_argument, NULL, 'n',
38         "\tDisplay the current date/time", pcmk__option_default
39     },
40     {
41         "date", required_argument, NULL, 'd',
42         "Parse an ISO 8601 date/time (for example, "
43             "'2019-09-24 00:30:00 +01:00' or '2019-040')",
44         pcmk__option_default
45     },
46     {
47         "period", required_argument, NULL, 'p',
48         "Parse an ISO 8601 period (interval) with start time (for example, "
49             "'2005-040/2005-043')",
50         pcmk__option_default
51     },
52     {
53         "duration", required_argument, NULL, 'D',
54         "Parse an ISO 8601 duration (for example, 'P1M')", pcmk__option_default
55     },
56     {
57         "expected", required_argument, NULL, 'E',
58         "Exit with error status if result does not match this text. "
59             "Requires: -n or -d",
60         pcmk__option_default
61     },
62     {
63         "-spacer-", no_argument, NULL, '-',
64         "\nOutput Modifiers:", pcmk__option_default
65     },
66     {
67         "seconds", no_argument, NULL, 's',
68         "\tShow result as a seconds since 0000-001 00:00:00Z",
69         pcmk__option_default
70     },
71     {
72         "epoch", no_argument, NULL, 'S',
73         "\tShow result as a seconds since EPOCH (1970-001 00:00:00Z)",
74         pcmk__option_default
75     },
76     {
77         "local", no_argument, NULL, 'L',
78         "\tShow result as a 'local' date/time", pcmk__option_default
79     },
80     {
81         "ordinal", no_argument, NULL, 'O',
82         "\tShow result as an 'ordinal' date/time", pcmk__option_default
83     },
84     {
85         "week", no_argument, NULL, 'W',
86         "\tShow result as an 'calendar week' date/time", pcmk__option_default
87     },
88     {
89         "-spacer-", no_argument, NULL, '-',
90         "\nFor more information on the ISO 8601 standard, see "
91             "https://en.wikipedia.org/wiki/ISO_8601",
92         pcmk__option_default
93     },
94     { 0, 0, 0, 0 }
95 };
96 
97 static void
log_time_period(int log_level,crm_time_period_t * dtp,int flags)98 log_time_period(int log_level, crm_time_period_t * dtp, int flags)
99 {
100     char *start = crm_time_as_string(dtp->start, flags);
101     char *end = crm_time_as_string(dtp->end, flags);
102 
103     CRM_ASSERT(start != NULL && end != NULL);
104     do_crm_log(log_level, "Period: %s to %s", start, end);
105     free(start);
106     free(end);
107 }
108 
109 int
main(int argc,char ** argv)110 main(int argc, char **argv)
111 {
112     crm_exit_t exit_code = CRM_EX_OK;
113     int argerr = 0;
114     int flag;
115     int index = 0;
116     int print_options = 0;
117     crm_time_t *duration = NULL;
118     crm_time_t *date_time = NULL;
119 
120     const char *period_s = NULL;
121     const char *duration_s = NULL;
122     const char *date_time_s = NULL;
123     const char *expected_s = NULL;
124 
125     pcmk__cli_init_logging("iso8601", 0);
126     pcmk__set_cli_options(NULL, "<command> [options] ", long_options,
127                           "display and parse ISO 8601 dates and times");
128 
129     if (argc < 2) {
130         argerr++;
131     }
132 
133     while (1) {
134         flag = pcmk__next_cli_option(argc, argv, &index, NULL);
135         if (flag == -1)
136             break;
137 
138         switch (flag) {
139             case 'V':
140                 crm_bump_log_level(argc, argv);
141                 break;
142             case '?':
143             case '$':
144                 pcmk__cli_help(flag, CRM_EX_OK);
145                 break;
146             case 'n':
147                 date_time_s = "now";
148                 break;
149             case 'd':
150                 date_time_s = optarg;
151                 break;
152             case 'p':
153                 period_s = optarg;
154                 break;
155             case 'D':
156                 duration_s = optarg;
157                 break;
158             case 'E':
159                 expected_s = optarg;
160                 break;
161             case 'S':
162                 print_options |= crm_time_epoch;
163                 break;
164             case 's':
165                 print_options |= crm_time_seconds;
166                 break;
167             case 'W':
168                 print_options |= crm_time_weeks;
169                 break;
170             case 'O':
171                 print_options |= crm_time_ordinal;
172                 break;
173             case 'L':
174                 print_options |= crm_time_log_with_timezone;
175                 break;
176                 break;
177         }
178     }
179 
180     if (pcmk__str_eq("now", date_time_s, pcmk__str_casei)) {
181         date_time = crm_time_new(NULL);
182 
183         if (date_time == NULL) {
184             fprintf(stderr, "Internal error: couldn't determine 'now'!\n");
185             crm_exit(CRM_EX_SOFTWARE);
186         }
187         crm_time_log(LOG_TRACE, "Current date/time", date_time,
188                      crm_time_ordinal | crm_time_log_date | crm_time_log_timeofday);
189         crm_time_log(LOG_STDOUT, "Current date/time", date_time,
190                      print_options | crm_time_log_date | crm_time_log_timeofday);
191 
192     } else if (date_time_s) {
193         date_time = crm_time_new(date_time_s);
194 
195         if (date_time == NULL) {
196             fprintf(stderr, "Invalid date/time specified: %s\n", date_time_s);
197             crm_exit(CRM_EX_INVALID_PARAM);
198         }
199         crm_time_log(LOG_TRACE, "Date", date_time,
200                      crm_time_ordinal | crm_time_log_date | crm_time_log_timeofday);
201         crm_time_log(LOG_STDOUT, "Date", date_time,
202                      print_options | crm_time_log_date | crm_time_log_timeofday);
203     }
204 
205     if (duration_s) {
206         duration = crm_time_parse_duration(duration_s);
207 
208         if (duration == NULL) {
209             fprintf(stderr, "Invalid duration specified: %s\n", duration_s);
210             crm_exit(CRM_EX_INVALID_PARAM);
211         }
212         crm_time_log(LOG_TRACE, "Duration", duration, crm_time_log_duration);
213         crm_time_log(LOG_STDOUT, "Duration", duration,
214                      print_options | crm_time_log_duration);
215     }
216 
217     if (period_s) {
218         crm_time_period_t *period = crm_time_parse_period(period_s);
219 
220         if (period == NULL) {
221             fprintf(stderr, "Invalid interval specified: %s\n", period_s);
222             crm_exit(CRM_EX_INVALID_PARAM);
223         }
224         log_time_period(LOG_TRACE, period,
225                         print_options | crm_time_log_date | crm_time_log_timeofday);
226         log_time_period(LOG_STDOUT, period,
227                         print_options | crm_time_log_date | crm_time_log_timeofday);
228         crm_time_free_period(period);
229     }
230 
231     if (date_time && duration) {
232         crm_time_t *later = crm_time_add(date_time, duration);
233 
234         if (later == NULL) {
235             fprintf(stderr, "Unable to calculate ending time of %s plus %s",
236                     date_time_s, duration_s);
237             crm_exit(CRM_EX_SOFTWARE);
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                      print_options | crm_time_log_date | crm_time_log_timeofday |
243                      crm_time_log_with_timezone);
244         if (expected_s) {
245             char *dt_s = crm_time_as_string(later,
246                                             print_options | crm_time_log_date |
247                                             crm_time_log_timeofday);
248             if (!pcmk__str_eq(expected_s, dt_s, pcmk__str_casei)) {
249                 exit_code = CRM_EX_ERROR;
250             }
251             free(dt_s);
252         }
253         crm_time_free(later);
254 
255     } else if (date_time && expected_s) {
256         char *dt_s = crm_time_as_string(date_time,
257                                         print_options | crm_time_log_date | crm_time_log_timeofday);
258 
259         if (!pcmk__str_eq(expected_s, dt_s, pcmk__str_casei)) {
260             exit_code = CRM_EX_ERROR;
261         }
262         free(dt_s);
263     }
264 
265     crm_time_free(date_time);
266     crm_time_free(duration);
267     crm_exit(exit_code);
268 }
269