diff --git a/apps/date.c b/apps/date.c index 5df3b45f..4a7eafed 100644 --- a/apps/date.c +++ b/apps/date.c @@ -15,6 +15,7 @@ * Copyright (C) 2018 K. Lange */ #include +#include #include #include #include @@ -32,6 +33,45 @@ static void show_usage(int argc, char * argv[]) { "\n", argv[0], argv[0]); } +int digits(const char * s, int len) { + for (int i = 0; i < len; ++i) { + if (s[i] < '0' || s[i] > '9') return 0; + } + return 1; +} + +int mmddhhmm(struct tm * tm, const char * str) { + int month = (str[0]-'0') * 10 + (str[1]-'0'); + int day = (str[2]-'0') * 10 + (str[3]-'0'); + int hour = (str[4]-'0') * 10 + (str[5]-'0'); + int min = (str[6]-'0') * 10 + (str[7]-'0'); + + if (month < 1 || month > 12) return 0; + if (day < 1 || day > 31) return 0; + if (hour < 0 || hour > 23) return 0; + if (min < 0 || min > 59) return 0; + + tm->tm_mon = month - 1; + tm->tm_mday = day; + tm->tm_hour = hour; + tm->tm_min = min; + + return 1; +} + +int ddyy(struct tm * tm, const char * str) { + int year = (str[0]-'0') * 1000 + (str[1]-'0') * 100 + (str[2]-'0') * 10 + (str[3]-'0'); + tm->tm_year = year - 1900; + return 1; +} + +int secs(struct tm * tm, const char * str) { + int sec = (str[0]-'0') * 10 + (str[1]-'0'); + if (sec < 0 || sec > 59) return 0; + tm->tm_sec = sec; + return 1; +} + int main(int argc, char * argv[]) { char * format = "%a %d %b %Y %T %Z"; struct tm * timeinfo; @@ -47,12 +87,47 @@ int main(int argc, char * argv[]) { } } + gettimeofday(&now, NULL); + timeinfo = localtime((time_t *)&now.tv_sec); + if (optind < argc && *argv[optind] == '+') { format = &argv[optind][1]; - } + } else if (optind < argc) { - gettimeofday(&now, NULL); //time(NULL); - timeinfo = localtime((time_t *)&now.tv_sec); + int len =strlen(argv[optind]); + + if (len == 8) { + if (!digits(argv[optind], 8)) goto _invalid; + if (!mmddhhmm(timeinfo, argv[optind])) goto _invalid; + goto set_time; + } else if (len == 11) { + if (argv[optind][8] != '.') goto _invalid; + if (!digits(argv[optind], 8) || !digits(argv[optind]+9,2)) goto _invalid; + if (!mmddhhmm(timeinfo, argv[optind])) goto _invalid; + if (!secs(timeinfo, argv[optind]+9)) goto _invalid; + goto set_time; + } else if (len == 12) { + if (!digits(argv[optind], 12)) goto _invalid; + if (!mmddhhmm(timeinfo, argv[optind])) goto _invalid; + if (!ddyy(timeinfo, argv[optind]+8)) goto _invalid; + goto set_time; + } else if (len == 15) { + if (argv[optind][12] != '.') goto _invalid; + if (!digits(argv[optind], 12) || !digits(argv[optind]+13,2)) goto _invalid; + if (!mmddhhmm(timeinfo, argv[optind])) goto _invalid; + if (!ddyy(timeinfo, argv[optind]+8)) goto _invalid; + if (!secs(timeinfo, argv[optind]+13)) goto _invalid; + goto set_time; + } +_invalid: + fprintf(stderr, "date: only 'MMDDhhmm', 'MMDDhhmm.ss', 'MMDDhhmmCCYY' and 'MMDDhhmmCCYY.ss' are supported for setting time.\n"); + return 1; + +set_time: + now.tv_usec = 0; + now.tv_sec = mktime(timeinfo); + return settimeofday(&now, NULL); + } strftime(buf,BUFSIZ,format,timeinfo); puts(buf); diff --git a/base/usr/include/sys/time.h b/base/usr/include/sys/time.h index 8bfc4fd9..a2dc2404 100644 --- a/base/usr/include/sys/time.h +++ b/base/usr/include/sys/time.h @@ -16,5 +16,6 @@ struct timezone { }; extern int gettimeofday(struct timeval *p, void *z); +extern int settimeofday(struct timeval *p, void *z); _End_C_Header diff --git a/base/usr/include/syscall.h b/base/usr/include/syscall.h index 0dcd68c2..2585bb83 100644 --- a/base/usr/include/syscall.h +++ b/base/usr/include/syscall.h @@ -221,6 +221,7 @@ DECL_SYSCALL2(getgroups, int, int*); DECL_SYSCALL2(setgroups, int, const int*); DECL_SYSCALL1(times, struct tms*); DECL_SYSCALL4(ptrace, int, int, void*, void*); +DECL_SYSCALL2(settimeofday, void *, void *); _End_C_Header diff --git a/base/usr/include/syscall_nums.h b/base/usr/include/syscall_nums.h index e2d15647..ec2464ae 100644 --- a/base/usr/include/syscall_nums.h +++ b/base/usr/include/syscall_nums.h @@ -72,3 +72,4 @@ #define SYS_GETGROUPS 69 #define SYS_SETGROUPS 70 #define SYS_TIMES 71 +#define SYS_SETTIMEOFDAY 72 diff --git a/kernel/arch/aarch64/main.c b/kernel/arch/aarch64/main.c index e70693a6..ccf53e64 100644 --- a/kernel/arch/aarch64/main.c +++ b/kernel/arch/aarch64/main.c @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -122,6 +123,20 @@ uint64_t now(void) { return t.tv_sec; } +static spin_lock_t _time_set_lock; + +int settimeofday(struct timeval * t, void *z) { + if (!t) return -EINVAL; + if (t->tv_sec < 0 || t->tv_usec < 0 || t->tv_usec > 1000000) return -EINVAL; + + spin_lock(_time_set_lock); + uint64_t clock_time = now(); + arch_boot_time += t->tv_sec - clock_time; + spin_unlock(_time_set_lock); + + return 0; +} + void relative_time(unsigned long seconds, unsigned long subseconds, unsigned long * out_seconds, unsigned long * out_subseconds) { if (!arch_boot_time) { *out_seconds = 0; diff --git a/kernel/arch/x86_64/cmos.c b/kernel/arch/x86_64/cmos.c index 1b2e027d..f0c73fa9 100644 --- a/kernel/arch/x86_64/cmos.c +++ b/kernel/arch/x86_64/cmos.c @@ -13,6 +13,7 @@ * of the NCSA / University of Illinois License - see LICENSE.md * Copyright (C) 2021 K. Lange */ +#include #include #include #include @@ -324,6 +325,27 @@ uint64_t now(void) { return t.tv_sec; } +static spin_lock_t _time_set_lock; + +/** + * Set the system clock time + * + * TODO: A lot of this time stuff needs to be made more generic, + * it's shared pretty directly with aarch64... + */ +int settimeofday(struct timeval * t, void *z) { + if (!t) return -EINVAL; + if (t->tv_sec < 0 || t->tv_usec < 0 || t->tv_usec > 1000000) return -EINVAL; + + spin_lock(_time_set_lock); + uint64_t clock_time = now(); + arch_boot_time += t->tv_sec - clock_time; + spin_unlock(_time_set_lock); + + return 0; +} + + /** * @brief Calculate a time in the future. * diff --git a/kernel/sys/syscall.c b/kernel/sys/syscall.c index 1a464239..e16a20d5 100644 --- a/kernel/sys/syscall.c +++ b/kernel/sys/syscall.c @@ -557,6 +557,14 @@ long sys_gettimeofday(struct timeval * tv, void * tz) { return gettimeofday(tv, tz); } +long sys_settimeofday(struct timeval * tv, void * tz) { + extern int settimeofday(struct timeval * t, void *z); + if (this_core->current_process->user != USER_ROOT_UID) return -EPERM; + PTR_VALIDATE(tv); + PTR_VALIDATE(tz); + return settimeofday(tv,tz); +} + long sys_getuid(void) { return (long)this_core->current_process->real_user; } @@ -1146,6 +1154,7 @@ static long (*syscalls[])() = { [SYS_SETGROUPS] = sys_setgroups, [SYS_TIMES] = sys_times, [SYS_PTRACE] = ptrace_handle, + [SYS_SETTIMEOFDAY] = sys_settimeofday, [SYS_SOCKET] = net_socket, [SYS_SETSOCKOPT] = net_setsockopt, diff --git a/libc/time/settimeofday.c b/libc/time/settimeofday.c new file mode 100644 index 00000000..a97205de --- /dev/null +++ b/libc/time/settimeofday.c @@ -0,0 +1,11 @@ +#include +#include +#include + +DEFN_SYSCALL2(settimeofday, SYS_SETTIMEOFDAY, void *, void *); + +int settimeofday(struct timeval *p, void *z){ + return syscall_settimeofday(p,z); +} + +