date_service.c 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. /*
  2. * Copyright (c) 2020 Nick Ward
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #include <stdlib.h>
  7. #include <shell/shell.h>
  8. #include <init.h>
  9. #include <string.h>
  10. #include <posix/time.h>
  11. #include <sys/timeutil.h>
  12. #define HELP_NONE "[none]"
  13. #define HELP_DATE_SET "[Y-m-d] <H:M:S>"
  14. static void date_print(const struct shell *shell, struct tm *tm)
  15. {
  16. shell_print(shell,
  17. "%d-%02u-%02u "
  18. "%02u:%02u:%02u UTC",
  19. tm->tm_year + 1900,
  20. tm->tm_mon + 1,
  21. tm->tm_mday,
  22. tm->tm_hour,
  23. tm->tm_min,
  24. tm->tm_sec);
  25. }
  26. static int get_y_m_d(const struct shell *shell, struct tm *tm, char *date_str)
  27. {
  28. int year;
  29. int month;
  30. int day;
  31. char *endptr;
  32. endptr = NULL;
  33. year = strtol(date_str, &endptr, 10);
  34. if ((endptr == date_str) || (*endptr != '-')) {
  35. return -EINVAL;
  36. }
  37. date_str = endptr + 1;
  38. endptr = NULL;
  39. month = strtol(date_str, &endptr, 10);
  40. if ((endptr == date_str) || (*endptr != '-')) {
  41. return -EINVAL;
  42. }
  43. if ((month < 1) || (month > 12)) {
  44. shell_error(shell, "Invalid month");
  45. return -EINVAL;
  46. }
  47. date_str = endptr + 1;
  48. endptr = NULL;
  49. day = strtol(date_str, &endptr, 10);
  50. if ((endptr == date_str) || (*endptr != '\0')) {
  51. return -EINVAL;
  52. }
  53. /* Check day against maximum month length */
  54. if ((day < 1) || (day > 31)) {
  55. shell_error(shell, "Invalid day");
  56. return -EINVAL;
  57. }
  58. tm->tm_year = year - 1900;
  59. tm->tm_mon = month - 1;
  60. tm->tm_mday = day;
  61. return 0;
  62. }
  63. /*
  64. * For user convenience of small adjustments to time the time argument will
  65. * accept H:M:S, :M:S or ::S where the missing field(s) will be filled in by
  66. * the previous time state.
  67. */
  68. static int get_h_m_s(const struct shell *shell, struct tm *tm, char *time_str)
  69. {
  70. char *endptr;
  71. if (*time_str == ':') {
  72. time_str++;
  73. } else {
  74. endptr = NULL;
  75. tm->tm_hour = strtol(time_str, &endptr, 10);
  76. if (endptr == time_str) {
  77. return -EINVAL;
  78. } else if (*endptr == ':') {
  79. if ((tm->tm_hour < 0) || (tm->tm_hour > 23)) {
  80. shell_error(shell, "Invalid hour");
  81. return -EINVAL;
  82. }
  83. time_str = endptr + 1;
  84. } else {
  85. return -EINVAL;
  86. }
  87. }
  88. if (*time_str == ':') {
  89. time_str++;
  90. } else {
  91. endptr = NULL;
  92. tm->tm_min = strtol(time_str, &endptr, 10);
  93. if (endptr == time_str) {
  94. return -EINVAL;
  95. } else if (*endptr == ':') {
  96. if ((tm->tm_min < 0) || (tm->tm_min > 59)) {
  97. shell_error(shell, "Invalid minute");
  98. return -EINVAL;
  99. }
  100. time_str = endptr + 1;
  101. } else {
  102. return -EINVAL;
  103. }
  104. }
  105. endptr = NULL;
  106. tm->tm_sec = strtol(time_str, &endptr, 10);
  107. if ((endptr == time_str) || (*endptr != '\0')) {
  108. return -EINVAL;
  109. }
  110. /* Note range allows for a leap second */
  111. if ((tm->tm_sec < 0) || (tm->tm_sec > 60)) {
  112. shell_error(shell, "Invalid second");
  113. return -EINVAL;
  114. }
  115. return 0;
  116. }
  117. static int cmd_date_set(const struct shell *shell, size_t argc, char **argv)
  118. {
  119. struct timespec tp;
  120. struct tm tm;
  121. int ret;
  122. clock_gettime(CLOCK_REALTIME, &tp);
  123. gmtime_r(&tp.tv_sec, &tm);
  124. if (argc == 3) {
  125. ret = get_y_m_d(shell, &tm, argv[1]);
  126. if (ret != 0) {
  127. shell_help(shell);
  128. return -EINVAL;
  129. }
  130. ret = get_h_m_s(shell, &tm, argv[2]);
  131. if (ret != 0) {
  132. shell_help(shell);
  133. return -EINVAL;
  134. }
  135. } else if (argc == 2) {
  136. ret = get_h_m_s(shell, &tm, argv[1]);
  137. if (ret != 0) {
  138. shell_help(shell);
  139. return -EINVAL;
  140. }
  141. } else {
  142. shell_help(shell);
  143. return -EINVAL;
  144. }
  145. tp.tv_sec = timeutil_timegm(&tm);
  146. if (tp.tv_sec == -1) {
  147. shell_error(shell, "Failed to calculate seconds since Epoch");
  148. return -EINVAL;
  149. }
  150. tp.tv_nsec = 0;
  151. ret = clock_settime(CLOCK_REALTIME, &tp);
  152. if (ret != 0) {
  153. shell_error(shell, "Could not set date %d", ret);
  154. return -EINVAL;
  155. }
  156. date_print(shell, &tm);
  157. return 0;
  158. }
  159. static int cmd_date_get(const struct shell *shell, size_t argc, char **argv)
  160. {
  161. struct timespec tp;
  162. struct tm tm;
  163. clock_gettime(CLOCK_REALTIME, &tp);
  164. gmtime_r(&tp.tv_sec, &tm);
  165. date_print(shell, &tm);
  166. return 0;
  167. }
  168. SHELL_STATIC_SUBCMD_SET_CREATE(sub_date,
  169. SHELL_CMD(set, NULL, HELP_DATE_SET, cmd_date_set),
  170. SHELL_CMD(get, NULL, HELP_NONE, cmd_date_get),
  171. SHELL_SUBCMD_SET_END /* Array terminated. */
  172. );
  173. SHELL_CMD_REGISTER(date, &sub_date, "Date commands", NULL);