1/*
2 * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
3 * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this list of
9 *    conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
12 *    of conditions and the following disclaimer in the documentation and/or other materials
13 *    provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
16 *    to endorse or promote products derived from this software without specific prior written
17 *    permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include "shcmd.h"
33#include "shell.h"
34#include "stdlib.h"
35#include "time.h"
36#include "los_typedef.h"
37#include "sys/stat.h"
38#include "securec.h"
39
40#if defined(__LP64__)
41#define  timeval64      timeval
42#define  settimeofday64 settimeofday
43#define  gettimeofday64 gettimeofday
44#endif
45
46#define  localtime64    localtime
47#define  ctime64        ctime
48#define  mktime64       mktime
49
50#define  DATE_ERR_INFO      1
51#define  DATE_HELP_INFO     0
52#define  DATE_ERR           (-1)
53#define  DATE_OK            0
54#define  DATE_BASE_YEAR     1900
55#define  LEAPYEAR(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
56
57STATIC const INT32 g_monLengths[2][12] = { /* 2: 2 Column,Contains leap year; 12: 12 months */
58    {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
59    {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
60};
61
62STATIC VOID OsCopyTm(struct tm *destTm, const struct tm *srcTm)
63{
64    if (srcTm == NULL) {
65        (VOID)memset_s(destTm, sizeof(struct tm), 0, sizeof(struct tm));
66    } else {
67        destTm->tm_sec = srcTm->tm_sec;
68        destTm->tm_min = srcTm->tm_min;
69        destTm->tm_hour = srcTm->tm_hour;
70        destTm->tm_mday = srcTm->tm_mday;
71        destTm->tm_mon = srcTm->tm_mon;
72        destTm->tm_year = srcTm->tm_year;
73        destTm->tm_wday = srcTm->tm_wday;
74        destTm->tm_yday = srcTm->tm_yday;
75        destTm->tm_isdst = srcTm->tm_isdst;
76        destTm->tm_gmtoff = srcTm->tm_gmtoff;
77        destTm->tm_zone = srcTm->tm_zone;
78    }
79}
80
81STATIC VOID OsCmdUsageDate(INT32 order)
82{
83    if (order) {
84        PRINTK("date: invalid option or parameter.\n");
85        PRINTK("Try 'date --help' for more information.\n");
86        return;
87    }
88    PRINTK("\nUsage: date [+FORMAT]\n");
89    PRINTK("   or: date [-s] [YY/MM/DD] [hh:mm:ss]\n");
90    PRINTK("Display the current time in the given FORMAT, or set the system date.\n");
91    PRINTK("FORMAT controls the output.  Interpreted sequences are:\n");
92    PRINTK("  %%b     The abbreviated month name according to the current locale.\n");
93    PRINTK("  %%B     The full month name according to the current locale.\n");
94    PRINTK("  %%C     The century number (year/100) as a 2-digit integer. (SU)\n");
95    PRINTK("  %%d     The day of the month as a decimal number (range 01 to 31).\n");
96    PRINTK("  %%e     Like %%d, the day of the month as a decimal number, \n");
97    PRINTK("         but a leading zero is replaced by a space.\n");
98    PRINTK("  %%h     Equivalent to %%b.  (SU)\n");
99    PRINTK("  %%H     The hour as a decimal number using a 24-hour clock (range 00 to 23).\n");
100    PRINTK("  %%I     The  hour as a decimal number using a 12-hour clock (range 01 to 12).\n");
101    PRINTK("  %%j     The day of the year as a decimal number (range 001 to 366).\n");
102    PRINTK("  %%k     The hour (24-hour clock) as a decimal number (range  0  to  23); \n");
103    PRINTK("         single digits are preceded by a blank.  (See also %H.)  (TZ)\n");
104    PRINTK("  %%l     The  hour  (12-hour  clock) as a decimal number (range 1 to 12); \n");
105    PRINTK("         single digits are preceded by a blank.  (See also %I.)  (TZ)\n");
106    PRINTK("  %%m     The month as a decimal number (range 01 to 12).\n");
107    PRINTK("  %%M     The minute as a decimal number (range 00 to 59).\n");
108    PRINTK("  %%n     A newline character. (SU)\n");
109    PRINTK("  %%p     Either \"AM\" or \"PM\" according to the given time  value, \n");
110    PRINTK("         or the corresponding  strings  for the current locale.\n");
111    PRINTK("         Noon is treated as \"PM\" and midnight as \"AM\".\n");
112    PRINTK("  %%P     Like %%p but in lowercase: \"am\" or \"pm\" \n");
113    PRINTK("         or a corresponding string for the current locale. (GNU)\n");
114    PRINTK("  %%s     The number of seconds since the Epoch, that is,\n");
115    PRINTK("         since 1970-01-01 00:00:00 UTC. (TZ)\n");
116    PRINTK("  %%S     The second as a decimal number (range 00 to 60).\n");
117    PRINTK("         (The range is up to 60 to allow for occasional leap seconds.)\n");
118    PRINTK("  %%t     A tab character. (SU)\n");
119    PRINTK("  %%y     The year as a decimal number without a century (range 00 to 99).\n");
120    PRINTK("  %%Y     The year as a decimal number including the century.\n");
121    PRINTK("  %%%%     A literal '%%' character.\n");
122    PRINTK("\nExamples:\n");
123    PRINTK("Set system date (2017-01-01)\n");
124    PRINTK("$ date -s 20170101\n");
125    PRINTK("Set system time (12:00:00)\n");
126    PRINTK("$ date -s 12:00:00\n");
127    PRINTK("Show the time with format Year-Month-Day\n");
128    PRINTK("$ date +%%Y-%%m-%%d\n");
129}
130
131STATIC INT32 OsStrToTm(const CHAR *str, struct tm *tm)
132{
133    CHAR *ret = NULL;
134    UINT32 strLen = strlen(str);
135    if (strLen == 8) { /* 8:Time format string length, such as hh:mm:ss or yyyymmdd */
136        if (str[2] == ':') { /* 2:Index of Eigenvalues */
137            ret = strptime(str, "%H:%M:%S", tm);
138        } else {
139            ret = strptime(str, "%Y%m%d", tm);
140        }
141    } else if (strLen == 10) { /* 10:Time format string length,such as yyyy/mm/dd  */
142        ret = strptime(str, "%Y/%m/%d", tm);
143    } else if (strLen == 5) { /* 5:Time format string length,such as hh:mm or mm/dd */
144        if (str[2] == ':') { /* 2:Index of Eigenvalues */
145            ret = strptime(str, "%H:%M", tm);
146        } else if (str[2] == '/') { /* 2:Index of Eigenvalues */
147            ret = strptime(str, "%m/%d", tm);
148        }
149    } else if (strLen == 7) { /* 7:Time format string length,such as yyyy/mm */
150        if (str[4] == '/') { /* 4:Index of Eigenvalues */
151            ret = strptime(str, "%Y/%m", tm);
152        }
153    }
154
155    if (tm->tm_year < 70) { /* 70:the year is starting in 1970,tm_year must be greater than 70 */
156        PRINTK("\nUsage: date -s set system time starting from 1970.\n");
157        return DATE_ERR;
158    }
159
160    if (tm->tm_mday > g_monLengths[(INT32)LEAPYEAR(tm->tm_year + DATE_BASE_YEAR)][tm->tm_mon]) {
161        return DATE_ERR;
162    }
163
164    if ((tm->tm_sec < 0) || (tm->tm_sec > 59)) { /* Seconds (0-59), leap seconds shall not be used when set time. */
165        return DATE_ERR;
166    }
167    return (ret == NULL) ? DATE_ERR : DATE_OK;
168}
169
170STATIC INT32 OsFormatPrintTime(const CHAR *formatStr)
171{
172    CHAR timebuf[SHOW_MAX_LEN] = {0};
173    struct tm *tm = NULL;
174    struct timeval64 nowTime = {0};
175
176    if (strlen(formatStr) < 2) { /* 2:check format string length */
177        OsCmdUsageDate(DATE_ERR_INFO);
178        return DATE_ERR;
179    }
180
181    if (gettimeofday64(&nowTime, NULL)) {
182        return DATE_ERR;
183    }
184    tm = localtime64(&nowTime.tv_sec);
185    if (tm == NULL) {
186        return DATE_ERR;
187    }
188
189    if (strftime(timebuf, SHOW_MAX_LEN - 1, formatStr + 1, tm)) {
190        PRINTK("%s\n", timebuf);
191    } else {
192        OsCmdUsageDate(DATE_ERR_INFO);
193        return DATE_ERR;
194    }
195    return DATE_OK;
196}
197
198STATIC INT32 OsDateSetTime(const CHAR *timeStr)
199{
200    struct tm tm = {0};
201    struct timeval64 nowTime = {0};
202    struct timeval64 setTime = {0};
203
204    if (gettimeofday64(&nowTime, NULL)) {
205        PRINTK("Setting time failed...\n");
206        return DATE_ERR;
207    }
208
209    setTime.tv_usec = nowTime.tv_usec;
210    OsCopyTm(&tm, localtime64(&nowTime.tv_sec));
211
212    if (OsStrToTm(timeStr, &tm)) {
213        OsCmdUsageDate(DATE_ERR_INFO);
214        return DATE_ERR;
215    }
216
217    setTime.tv_sec = mktime64(&tm);
218
219    if (settimeofday64(&setTime, NULL)) {
220        PRINTK("setting time failed...\n");
221        return DATE_ERR;
222    }
223
224    return DATE_OK;
225}
226
227#ifdef  LOSCFG_FS_VFS
228STATIC INT32 OsViewFileTime(const CHAR *filename)
229{
230#define BUFFER_SIZE 26 /* The buffer size is equal to the size used by the asctime_r interface */
231    struct stat statBuf = {0};
232    CHAR *fullpath = NULL;
233    INT32 ret;
234    CHAR buf[BUFFER_SIZE];
235    CHAR *shellWorkingDirectory = OsShellGetWorkingDirectory();
236
237    ret = vfs_normalize_path(shellWorkingDirectory, filename, &fullpath);
238    if (ret < 0) {
239        set_errno(-ret);
240        perror("date error");
241        return DATE_ERR;
242    }
243
244    if (stat(fullpath, &statBuf) != 0) {
245        OsCmdUsageDate(DATE_ERR_INFO);
246        free(fullpath);
247        return DATE_ERR;
248    }
249    PRINTK("%s\n", ctime_r(&(statBuf.st_mtim.tv_sec), buf));
250    free(fullpath);
251    return DATE_OK;
252}
253#endif
254
255INT32 OsShellCmdDate(INT32 argc, const CHAR **argv)
256{
257    struct timeval64 nowTime = {0};
258
259    if (argc == 1) { /* 1:count of parameters */
260        if (gettimeofday64(&nowTime, NULL)) {
261            return DATE_ERR;
262        }
263        PRINTK("%s\n", ctime64(&nowTime.tv_sec));
264        return DATE_OK;
265    }
266
267    if (argc == 2) { /* 2:count of parameters */
268        if (argv == NULL) {
269            OsCmdUsageDate(DATE_HELP_INFO);
270            return DATE_ERR;
271        }
272
273        if (!(strcmp(argv[1], "--help"))) {
274            OsCmdUsageDate(DATE_HELP_INFO);
275            return DATE_OK;
276        }
277        if (!(strncmp(argv[1], "+", 1))) {
278            return OsFormatPrintTime(argv[1]);
279        }
280    }
281
282    if (argc > 2) { /* 2:count of parameters */
283        if (argv == NULL) {
284            OsCmdUsageDate(DATE_HELP_INFO);
285            return DATE_ERR;
286        }
287
288        if (!(strcmp(argv[1], "-s"))) {
289            return OsDateSetTime(argv[2]); /* 2:index of parameters */
290        } else if (!(strcmp(argv[1], "-r"))) {
291#ifdef  LOSCFG_FS_VFS
292            return OsViewFileTime(argv[2]); /* 2:index of parameters */
293#endif
294        }
295    }
296
297    OsCmdUsageDate(DATE_ERR_INFO);
298    return DATE_OK;
299}
300
301SHELLCMD_ENTRY(date_shellcmd, CMD_TYPE_STD, "date", XARGS, (CmdCallBackFunc)OsShellCmdDate);
302