From 516fec97d9ae7fad5a095a927cb2187e15b2daf3 Mon Sep 17 00:00:00 2001 From: Thomas Cort Date: Sat, 30 Mar 2013 16:59:21 +0000 Subject: [PATCH] libc: add clock_settime() system call. This also adds the sys_settime() kernel call which allows for the adjusting of the clock named realtime in the kernel. The existing sys_stime() function is still needed for a separate job (setting the boottime). The boottime is set in the readclock driver. The sys_settime() interface is meant to be flexible and will support both clock_settime() and adjtime() when adjtime() is implemented later. settimeofday() was adjusted to use the clock_settime() interface. One side note discovered during testing: uptime(1) (part of the last(1)), uses wtmp to determine boottime (not Minix's times(2)). This leads `uptime` to report odd results when you set the time to a time prior to boottime. This isn't a new bug introduced by my changes. It's been there for a while. --- include/minix/callnr.h | 3 +- include/minix/com.h | 7 +++++ include/minix/syslib.h | 1 + include/time.h | 2 -- kernel/clock.c | 8 ++++++ kernel/proto.h | 1 + kernel/system.c | 1 + kernel/system.h | 1 + kernel/system/Makefile.inc | 1 + kernel/system/do_settime.c | 43 +++++++++++++++++++++++++++++ lib/libc/sys-minix/MISSING_SYSCALLS | 1 - lib/libc/sys-minix/Makefile.inc | 2 +- lib/libc/sys-minix/clock_getres.c | 2 +- lib/libc/sys-minix/clock_gettime.c | 2 +- lib/libc/sys-minix/clock_settime.c | 24 ++++++++++++++++ lib/libc/sys-minix/settimeofday.c | 8 ++++-- lib/libsys/Makefile | 1 + lib/libsys/sys_settime.c | 20 ++++++++++++++ servers/pm/param.h | 4 ++- servers/pm/proto.h | 1 + servers/pm/table.c | 1 + servers/pm/time.c | 22 +++++++++++++++ servers/vfs/table.c | 1 + test/test69.c | 33 ++++++++++++++++++++++ 24 files changed, 179 insertions(+), 11 deletions(-) create mode 100644 kernel/system/do_settime.c create mode 100644 lib/libc/sys-minix/clock_settime.c create mode 100644 lib/libsys/sys_settime.c diff --git a/include/minix/callnr.h b/include/minix/callnr.h index d3c9e3529..f4417ddf5 100644 --- a/include/minix/callnr.h +++ b/include/minix/callnr.h @@ -1,4 +1,4 @@ -#define NCALLS 116 /* number of system calls allowed */ +#define NCALLS 117 /* number of system calls allowed */ /* In case it isn't obvious enough: this list is sorted numerically. */ #define EXIT 1 @@ -104,6 +104,7 @@ #define PM_GETSID 113 /* PM getsid() */ #define CLOCK_GETRES 114 /* clock_getres() */ #define CLOCK_GETTIME 115 /* clock_gettime() */ +#define CLOCK_SETTIME 116 /* clock_settime() */ #define TASK_REPLY 121 /* to VFS: reply code from drivers, not * really a standalone call. diff --git a/include/minix/com.h b/include/minix/com.h index 327c3dcba..4ffa407d5 100644 --- a/include/minix/com.h +++ b/include/minix/com.h @@ -335,6 +335,7 @@ # define SYS_PROFBUF (KERNEL_CALL + 38) /* sys_profbuf() */ # define SYS_STIME (KERNEL_CALL + 39) /* sys_stime() */ +# define SYS_SETTIME (KERNEL_CALL + 40) /* sys_settime() */ # define SYS_VMCTL (KERNEL_CALL + 43) /* sys_vmctl() */ # define SYS_SYSCTL (KERNEL_CALL + 44) /* sys_sysctl() */ @@ -491,6 +492,12 @@ #define T_REAL_TICKS m4_l4 /* number of wall clock ticks since boottime */ #define T_BOOT_TICKS m4_l5 /* number of hard clock ticks since boottime */ +/* Field names for SYS_SETTIME. */ +#define T_SETTIME_NOW m4_l2 /* non-zero for immediate, 0 for adjtime */ +#define T_CLOCK_ID m4_l3 /* clock to adjust */ +#define T_TIME_SEC m4_l4 /* time in seconds since 1970 */ +#define T_TIME_NSEC m4_l5 /* number of nano seconds */ + /* Field names for SYS_TRACE, SYS_PRIVCTL, SYS_STATECTL. */ #define CTL_ENDPT m2_i1 /* process number of the caller */ #define CTL_REQUEST m2_i2 /* server control request */ diff --git a/include/minix/syslib.h b/include/minix/syslib.h index f1bc43186..4d373843d 100644 --- a/include/minix/syslib.h +++ b/include/minix/syslib.h @@ -65,6 +65,7 @@ int sys_vmctl_get_memreq(endpoint_t *who, vir_bytes *mem, vir_bytes int sys_vmctl_enable_paging(void * data); int sys_readbios(phys_bytes address, void *buf, size_t size); +int sys_settime(int now, clockid_t clk_id, time_t sec, long nsec); int sys_stime(time_t boottime); int sys_sysctl(int ctl, char *arg1, int arg2); int sys_sysctl_stacktrace(endpoint_t who); diff --git a/include/time.h b/include/time.h index 1a9d42543..711297140 100644 --- a/include/time.h +++ b/include/time.h @@ -162,10 +162,8 @@ int clock_getres(clockid_t, struct timespec *) __RENAME(__clock_getres50); int clock_gettime(clockid_t, struct timespec *) __RENAME(__clock_gettime50); -#ifndef __minix int clock_settime(clockid_t, const struct timespec *) __RENAME(__clock_settime50); -#endif /* !__minix */ int nanosleep(const struct timespec *, struct timespec *) __RENAME(__nanosleep50); #ifndef __minix diff --git a/kernel/clock.c b/kernel/clock.c index a0b796bcc..688ab6695 100644 --- a/kernel/clock.c +++ b/kernel/clock.c @@ -19,6 +19,7 @@ * loop, there are several other minor entry points: * clock_stop: called just before MINIX shutdown * get_realtime: get wall time since boot in clock ticks + * set_realtime: set wall time since boot in clock ticks * get_monotonic: get monotonic time since boot in clock ticks * set_timer: set a watchdog timer (+) * reset_timer: reset a watchdog timer (+) @@ -167,6 +168,13 @@ clock_t get_realtime(void) return(realtime); } +/*===========================================================================* + * set_realtime * + *===========================================================================*/ +void set_realtime(clock_t newrealtime) +{ + realtime = newrealtime; +} /*===========================================================================* * get_monotonic * diff --git a/kernel/proto.h b/kernel/proto.h index 77e53d8b6..120c62051 100644 --- a/kernel/proto.h +++ b/kernel/proto.h @@ -16,6 +16,7 @@ struct timer; /* clock.c */ clock_t get_realtime(void); +void set_realtime(clock_t); clock_t get_monotonic(void); void set_timer(struct timer *tp, clock_t t, tmr_func_t f); void reset_timer(struct timer *tp); diff --git a/kernel/system.c b/kernel/system.c index e8fe0387a..caafd9943 100644 --- a/kernel/system.c +++ b/kernel/system.c @@ -235,6 +235,7 @@ void system_init(void) map(SYS_TIMES, do_times); /* get uptime and process times */ map(SYS_SETALARM, do_setalarm); /* schedule a synchronous alarm */ map(SYS_STIME, do_stime); /* set the boottime */ + map(SYS_SETTIME, do_settime); /* set the system time (realtime) */ map(SYS_VTIMER, do_vtimer); /* set or retrieve a virtual timer */ /* System control. */ diff --git a/kernel/system.h b/kernel/system.h index fa58823b6..9f493ab6f 100644 --- a/kernel/system.h +++ b/kernel/system.h @@ -164,6 +164,7 @@ int do_setalarm(struct proc * caller, message *m_ptr); #endif int do_stime(struct proc * caller, message *m_ptr); +int do_settime(struct proc * caller, message *m_ptr); int do_vtimer(struct proc * caller, message *m_ptr); #if ! USE_VTIMER diff --git a/kernel/system/Makefile.inc b/kernel/system/Makefile.inc index 69efe4e87..e90b47640 100644 --- a/kernel/system/Makefile.inc +++ b/kernel/system/Makefile.inc @@ -13,6 +13,7 @@ SRCS+= \ do_times.c \ do_setalarm.c \ do_stime.c \ + do_settime.c \ do_vtimer.c \ do_irqctl.c \ do_copy.c \ diff --git a/kernel/system/do_settime.c b/kernel/system/do_settime.c new file mode 100644 index 000000000..7c106dd50 --- /dev/null +++ b/kernel/system/do_settime.c @@ -0,0 +1,43 @@ +/* The kernel call implemented in this file: + * m_type: SYS_SETTIME + * + * The parameters for this kernel call are: + * m4_l2: T_SETTIME_NOW + * m4_l3: T_CLOCK_ID + * m4_l4: T_TIME_SEC + * m4_l5: T_TIME_NSEC + */ + +#include "kernel/system.h" +#include +#include + +/*===========================================================================* + * do_settime * + *===========================================================================*/ +int do_settime(struct proc * caller, message * m_ptr) +{ + clock_t newclock; + time_t timediff; + + if (m_ptr->T_CLOCK_ID != CLOCK_REALTIME) /* only realtime can change */ + return EINVAL; + + /* prevent a negative value for realtime */ + if (m_ptr->T_TIME_SEC <= boottime) { + /* boottime was likely wrong, try to correct it. */ + boottime = m_ptr->T_TIME_SEC; + set_realtime(1); + return(OK); + } + + /* calculate the new value of realtime in ticks */ + timediff = m_ptr->T_TIME_SEC - boottime; + newclock = (timediff*system_hz) + (m_ptr->T_TIME_NSEC/(1000000000/system_hz)); + + if (m_ptr->T_SETTIME_NOW) { + set_realtime(newclock); + } /* else used adjtime() method (to be implemented) */ + + return(OK); +} diff --git a/lib/libc/sys-minix/MISSING_SYSCALLS b/lib/libc/sys-minix/MISSING_SYSCALLS index 54b519863..440ab35b5 100644 --- a/lib/libc/sys-minix/MISSING_SYSCALLS +++ b/lib/libc/sys-minix/MISSING_SYSCALLS @@ -4,7 +4,6 @@ adjtime lchmod lchown clone -clock_settime extattr_* fhopen fhstat diff --git a/lib/libc/sys-minix/Makefile.inc b/lib/libc/sys-minix/Makefile.inc index f47cd87e9..211545599 100644 --- a/lib/libc/sys-minix/Makefile.inc +++ b/lib/libc/sys-minix/Makefile.inc @@ -2,7 +2,7 @@ SRCS+= accept.c access.c bind.c brk.c sbrk.c m_closefrom.c getsid.c \ chdir.c chmod.c fchmod.c chown.c fchown.c chroot.c close.c \ - clock_getres.c clock_gettime.c \ + clock_getres.c clock_gettime.c clock_settime.c \ connect.c dup.c dup2.c execve.c fcntl.c flock.c fpathconf.c fork.c \ fstatfs.c fstatvfs.c fsync.c ftruncate.c getdents.c getegid.c getgid.c \ getgroups.c getitimer.c setitimer.c __getlogin.c getpeername.c \ diff --git a/lib/libc/sys-minix/clock_getres.c b/lib/libc/sys-minix/clock_getres.c index a40177e77..3763305c6 100644 --- a/lib/libc/sys-minix/clock_getres.c +++ b/lib/libc/sys-minix/clock_getres.c @@ -12,7 +12,7 @@ int clock_getres(clockid_t clock_id, struct timespec *res) { message m; - m.m2_l1 = (clockid_t) clock_id; + m.m2_i1 = (clockid_t) clock_id; if (_syscall(PM_PROC_NR, CLOCK_GETRES, &m) < 0) return -1; diff --git a/lib/libc/sys-minix/clock_gettime.c b/lib/libc/sys-minix/clock_gettime.c index 01ed16cc8..8ea6d34d0 100644 --- a/lib/libc/sys-minix/clock_gettime.c +++ b/lib/libc/sys-minix/clock_gettime.c @@ -12,7 +12,7 @@ int clock_gettime(clockid_t clock_id, struct timespec *res) { message m; - m.m2_l1 = (clockid_t) clock_id; + m.m2_i1 = (clockid_t) clock_id; if (_syscall(PM_PROC_NR, CLOCK_GETTIME, &m) < 0) return -1; diff --git a/lib/libc/sys-minix/clock_settime.c b/lib/libc/sys-minix/clock_settime.c new file mode 100644 index 000000000..28d518778 --- /dev/null +++ b/lib/libc/sys-minix/clock_settime.c @@ -0,0 +1,24 @@ +#include +#include +#include "namespace.h" + +#include + +#ifdef __weak_alias +__weak_alias(clock_settime, __clock_settime50); +#endif + +int clock_settime(clockid_t clock_id, const struct timespec *ts) +{ + message m; + + m.m2_i1 = (clockid_t) clock_id; + m.m2_l1 = (time_t) ts->tv_sec; + m.m2_l2 = (long) ts->tv_nsec; + + if (_syscall(PM_PROC_NR, CLOCK_SETTIME, &m) < 0) + return -1; + + return 0; +} + diff --git a/lib/libc/sys-minix/settimeofday.c b/lib/libc/sys-minix/settimeofday.c index ae0e6b578..85bef8bc5 100644 --- a/lib/libc/sys-minix/settimeofday.c +++ b/lib/libc/sys-minix/settimeofday.c @@ -7,11 +7,13 @@ int settimeofday(const struct timeval *tp, const void *tzp) { - /* Use intermediate variable because stime param is not const */ - time_t sec = tp->tv_sec; + struct timespec ts; + + ts.tv_sec = tp->tv_sec; + ts.tv_nsec = tp->tv_usec * 1000; /* Ignore time zones */ - return stime(&sec); + return clock_settime(CLOCK_REALTIME, &ts); } #if defined(__minix) && defined(__weak_alias) diff --git a/lib/libsys/Makefile b/lib/libsys/Makefile index 4d36abbaa..f1a4e39b1 100644 --- a/lib/libsys/Makefile +++ b/lib/libsys/Makefile @@ -59,6 +59,7 @@ SRCS+= \ sys_schedule.c \ sys_setalarm.c \ sys_setgrant.c \ + sys_settime.c \ sys_sigreturn.c \ sys_sigsend.c \ sys_sprof.c \ diff --git a/lib/libsys/sys_settime.c b/lib/libsys/sys_settime.c new file mode 100644 index 000000000..6199791a3 --- /dev/null +++ b/lib/libsys/sys_settime.c @@ -0,0 +1,20 @@ +#include "syslib.h" +#include + +int sys_settime(now, clk_id, sec, nsec) +int now; +clockid_t clk_id; +time_t sec; +long nsec; +{ + message m; + int r; + + m.T_SETTIME_NOW = now; + m.T_CLOCK_ID = clk_id; + m.T_TIME_SEC = sec; + m.T_TIME_NSEC = nsec; + + r = _kernel_call(SYS_SETTIME, &m); + return(r); +} diff --git a/servers/pm/param.h b/servers/pm/param.h index 33a15cb71..260910fbd 100644 --- a/servers/pm/param.h +++ b/servers/pm/param.h @@ -30,7 +30,9 @@ #define svrctl_req m2_i1 #define svrctl_argp m2_p1 #define stime m2_l1 -#define clk_id m2_l1 +#define clk_id m2_i1 +#define time_sec m2_l1 +#define time_nsec m2_l2 #define memsize m4_l1 #define membase m4_l2 #define sysuname_req m1_i1 diff --git a/servers/pm/proto.h b/servers/pm/proto.h index 2b86bad66..73e8a4a69 100644 --- a/servers/pm/proto.h +++ b/servers/pm/proto.h @@ -88,6 +88,7 @@ int do_time(void); int do_times(void); int do_getres(void); int do_gettime(void); +int do_settime(void); /* trace.c */ int do_trace(void); diff --git a/servers/pm/table.c b/servers/pm/table.c index 4c253dabc..4a51a8f7c 100644 --- a/servers/pm/table.c +++ b/servers/pm/table.c @@ -127,6 +127,7 @@ int (*call_vec[])(void) = { do_get, /* 113 = getsid */ do_getres, /* 114 = clock_getres */ do_gettime, /* 115 = clock_gettime */ + do_settime, /* 116 = clock_settime */ }; /* This should not fail with "array size is negative": */ extern int dummy[sizeof(call_vec) == NCALLS * sizeof(call_vec[0]) ? 1 : -1]; diff --git a/servers/pm/time.c b/servers/pm/time.c index 61fca33eb..aea2e421b 100644 --- a/servers/pm/time.c +++ b/servers/pm/time.c @@ -3,6 +3,7 @@ * The entry points into this file are * do_getres: perform the CLOCK_GETRES system call * do_gettime: perform the CLOCK_GETTIME system call + * do_settime: perform the CLOCK_SETTIME system call * do_time: perform the TIME system call * do_stime: perform the STIME system call * do_times: perform the TIMES system call @@ -62,6 +63,27 @@ int do_getres() } } +/*===========================================================================* + * do_settime * + *===========================================================================*/ +int do_settime() +{ + int s; + + if (mp->mp_effuid != SUPER_USER) { + return(EPERM); + } + + switch (m_in.clk_id) { + case CLOCK_REALTIME: + s= sys_settime(1, m_in.clk_id, m_in.time_sec, m_in.time_nsec); + return(s); + case CLOCK_MONOTONIC: /* monotonic cannot be changed */ + default: + return EINVAL; /* invalid/unsupported clock_id */ + } +} + /*===========================================================================* * do_time * *===========================================================================*/ diff --git a/servers/vfs/table.c b/servers/vfs/table.c index d27b070c4..b65542635 100644 --- a/servers/vfs/table.c +++ b/servers/vfs/table.c @@ -131,6 +131,7 @@ int (*call_vec[])(void) = { no_sys, /* 113 = (getsid) */ no_sys, /* 114 = (clock_getres) */ no_sys, /* 115 = (clock_gettime) */ + no_sys, /* 116 = (clock_settime) */ }; /* This should not fail with "array size is negative": */ extern int dummy[sizeof(call_vec) == NCALLS * sizeof(call_vec[0]) ? 1 : -1]; diff --git a/test/test69.c b/test/test69.c index 368cb4abb..0282f60bb 100644 --- a/test/test69.c +++ b/test/test69.c @@ -20,6 +20,7 @@ int main(void); void quit(void); static void test_clock_getres(); static void test_clock_gettime(); +static void test_clock_settime(); static void show_timespec(char *msg, struct timespec *ts); static void test_clock_getres() @@ -63,6 +64,37 @@ static void test_clock_gettime() if (clock_gettime(-1, &ts) == 0) e(31); } +static void test_clock_settime(void) +{ + struct timespec ts; + struct timespec ts2; + + /* shouldn't be able to set MONOTONIC */ + if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) e(50); + if (clock_settime(CLOCK_MONOTONIC, &ts) == 0) e(51); + if (errno != EINVAL) e(52); /* reponse should be EINVAL */ + + /* set the time of REALTIME to that of MONOTONIC */ + if (clock_settime(CLOCK_REALTIME, &ts) == -1) e(53); + + ts.tv_sec += 600; /* time travel 10 minutes into the future */ + if (clock_gettime(CLOCK_REALTIME, &ts2) == -1) e(54); + if (clock_settime(CLOCK_REALTIME, &ts) == -1) e(55); + if (clock_gettime(CLOCK_REALTIME, &ts) == -1) e(56); + + /* get the value we set, if it's not about 10 minutes ahead, it failed */ + if (ts.tv_sec - ts2.tv_sec < 500 || + ts.tv_sec - ts2.tv_sec > 700) e(57); + + /* back to current time - don't leave the system time 10 ahead */ + if (clock_gettime(CLOCK_REALTIME, &ts) == -1) e(58); + ts.tv_sec -= 600; + if (clock_settime(CLOCK_REALTIME, &ts) == -1) e(59); + + /* Test with an invalid clock */ + if (clock_settime(-1, &ts) == 0) e(60); +} + static void show_timespec(char *msg, struct timespec *ts) { #if DEBUG == 1 @@ -80,6 +112,7 @@ int main() test_clock_getres(); test_clock_gettime(); + test_clock_settime(); /* get test end time */ if (clock_gettime(CLOCK_MONOTONIC, &endtime) == -1) e(2);