/* * Copyright (c) 2020 Nick Ward * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #define HELP_NONE "[none]" #define HELP_DATE_SET "[Y-m-d] " static void date_print(const struct shell *shell, struct tm *tm) { shell_print(shell, "%d-%02u-%02u " "%02u:%02u:%02u UTC", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); } static int get_y_m_d(const struct shell *shell, struct tm *tm, char *date_str) { int year; int month; int day; char *endptr; endptr = NULL; year = strtol(date_str, &endptr, 10); if ((endptr == date_str) || (*endptr != '-')) { return -EINVAL; } date_str = endptr + 1; endptr = NULL; month = strtol(date_str, &endptr, 10); if ((endptr == date_str) || (*endptr != '-')) { return -EINVAL; } if ((month < 1) || (month > 12)) { shell_error(shell, "Invalid month"); return -EINVAL; } date_str = endptr + 1; endptr = NULL; day = strtol(date_str, &endptr, 10); if ((endptr == date_str) || (*endptr != '\0')) { return -EINVAL; } /* Check day against maximum month length */ if ((day < 1) || (day > 31)) { shell_error(shell, "Invalid day"); return -EINVAL; } tm->tm_year = year - 1900; tm->tm_mon = month - 1; tm->tm_mday = day; return 0; } /* * For user convenience of small adjustments to time the time argument will * accept H:M:S, :M:S or ::S where the missing field(s) will be filled in by * the previous time state. */ static int get_h_m_s(const struct shell *shell, struct tm *tm, char *time_str) { char *endptr; if (*time_str == ':') { time_str++; } else { endptr = NULL; tm->tm_hour = strtol(time_str, &endptr, 10); if (endptr == time_str) { return -EINVAL; } else if (*endptr == ':') { if ((tm->tm_hour < 0) || (tm->tm_hour > 23)) { shell_error(shell, "Invalid hour"); return -EINVAL; } time_str = endptr + 1; } else { return -EINVAL; } } if (*time_str == ':') { time_str++; } else { endptr = NULL; tm->tm_min = strtol(time_str, &endptr, 10); if (endptr == time_str) { return -EINVAL; } else if (*endptr == ':') { if ((tm->tm_min < 0) || (tm->tm_min > 59)) { shell_error(shell, "Invalid minute"); return -EINVAL; } time_str = endptr + 1; } else { return -EINVAL; } } endptr = NULL; tm->tm_sec = strtol(time_str, &endptr, 10); if ((endptr == time_str) || (*endptr != '\0')) { return -EINVAL; } /* Note range allows for a leap second */ if ((tm->tm_sec < 0) || (tm->tm_sec > 60)) { shell_error(shell, "Invalid second"); return -EINVAL; } return 0; } static int cmd_date_set(const struct shell *shell, size_t argc, char **argv) { struct timespec tp; struct tm tm; int ret; clock_gettime(CLOCK_REALTIME, &tp); gmtime_r(&tp.tv_sec, &tm); if (argc == 3) { ret = get_y_m_d(shell, &tm, argv[1]); if (ret != 0) { shell_help(shell); return -EINVAL; } ret = get_h_m_s(shell, &tm, argv[2]); if (ret != 0) { shell_help(shell); return -EINVAL; } } else if (argc == 2) { ret = get_h_m_s(shell, &tm, argv[1]); if (ret != 0) { shell_help(shell); return -EINVAL; } } else { shell_help(shell); return -EINVAL; } tp.tv_sec = timeutil_timegm(&tm); if (tp.tv_sec == -1) { shell_error(shell, "Failed to calculate seconds since Epoch"); return -EINVAL; } tp.tv_nsec = 0; ret = clock_settime(CLOCK_REALTIME, &tp); if (ret != 0) { shell_error(shell, "Could not set date %d", ret); return -EINVAL; } date_print(shell, &tm); return 0; } static int cmd_date_get(const struct shell *shell, size_t argc, char **argv) { struct timespec tp; struct tm tm; clock_gettime(CLOCK_REALTIME, &tp); gmtime_r(&tp.tv_sec, &tm); date_print(shell, &tm); return 0; } SHELL_STATIC_SUBCMD_SET_CREATE(sub_date, SHELL_CMD(set, NULL, HELP_DATE_SET, cmd_date_set), SHELL_CMD(get, NULL, HELP_NONE, cmd_date_get), SHELL_SUBCMD_SET_END /* Array terminated. */ ); SHELL_CMD_REGISTER(date, &sub_date, "Date commands", NULL);