573 lines
12 KiB
C++
573 lines
12 KiB
C++
/* times.cc
|
|
|
|
This file is part of Cygwin.
|
|
|
|
This software is a copyrighted work licensed under the terms of the
|
|
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
|
|
details. */
|
|
|
|
#define __timezonefunc__
|
|
#include "winsup.h"
|
|
#include <sys/times.h>
|
|
#include <sys/timeb.h>
|
|
#include <sys/param.h>
|
|
#include <utime.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include "cygerrno.h"
|
|
#include "security.h"
|
|
#include "path.h"
|
|
#include "fhandler.h"
|
|
#include "dtable.h"
|
|
#include "cygheap.h"
|
|
#include "pinfo.h"
|
|
#include "thread.h"
|
|
#include "cygtls.h"
|
|
#include "ntdll.h"
|
|
#include "spinlock.h"
|
|
|
|
static inline void __attribute__ ((always_inline))
|
|
get_system_time (PLARGE_INTEGER systime)
|
|
{
|
|
GetSystemTimePreciseAsFileTime ((LPFILETIME) systime);
|
|
}
|
|
|
|
/* Cygwin internal */
|
|
static uint64_t
|
|
__to_clock_t (PLARGE_INTEGER src, int flag)
|
|
{
|
|
uint64_t total = src->QuadPart;
|
|
/* Convert into clock ticks - the total is in 10ths of a usec. */
|
|
if (flag)
|
|
total -= FACTOR;
|
|
|
|
total /= NS100PERSEC / CLOCKS_PER_SEC;
|
|
return total;
|
|
}
|
|
|
|
/* times: POSIX 4.5.2.1 */
|
|
extern "C" clock_t
|
|
times (struct tms *buf)
|
|
{
|
|
static SYSTEM_TIMEOFDAY_INFORMATION stodi;
|
|
KERNEL_USER_TIMES kut;
|
|
LARGE_INTEGER ticks;
|
|
clock_t tc = (clock_t) -1;
|
|
|
|
__try
|
|
{
|
|
/* Fetch boot time if we haven't already. */
|
|
if (!stodi.BootTime.QuadPart)
|
|
NtQuerySystemInformation (SystemTimeOfDayInformation,
|
|
&stodi, sizeof stodi, NULL);
|
|
|
|
NtQueryInformationProcess (NtCurrentProcess (), ProcessTimes,
|
|
&kut, sizeof kut, NULL);
|
|
get_system_time (&ticks);
|
|
|
|
/* uptime */
|
|
ticks.QuadPart -= stodi.BootTime.QuadPart;
|
|
/* ticks is in in 100ns, convert to clock ticks. */
|
|
tc = (clock_t) (ticks.QuadPart * CLOCKS_PER_SEC / NS100PERSEC);
|
|
|
|
/* Linux allows a NULL buf and just returns tc in that case, so
|
|
mimic that */
|
|
if (buf)
|
|
{
|
|
buf->tms_stime = __to_clock_t (&kut.KernelTime, 0);
|
|
buf->tms_utime = __to_clock_t (&kut.UserTime, 0);
|
|
timeval_to_filetime (&myself->rusage_children.ru_stime, &kut.KernelTime);
|
|
buf->tms_cstime = __to_clock_t (&kut.KernelTime, 1);
|
|
timeval_to_filetime (&myself->rusage_children.ru_utime, &kut.UserTime);
|
|
buf->tms_cutime = __to_clock_t (&kut.UserTime, 1);
|
|
}
|
|
}
|
|
__except (EFAULT)
|
|
{
|
|
tc = (clock_t) -1;
|
|
}
|
|
__endtry
|
|
syscall_printf ("%D = times(%p)", tc, buf);
|
|
return tc;
|
|
}
|
|
|
|
/* settimeofday: BSD */
|
|
extern "C" int
|
|
settimeofday (const struct timeval *tv, const struct timezone *tz)
|
|
{
|
|
SYSTEMTIME st;
|
|
struct tm *ptm;
|
|
int res = -1;
|
|
|
|
__try
|
|
{
|
|
if (tv->tv_usec < 0 || tv->tv_usec >= USPERSEC)
|
|
{
|
|
set_errno (EINVAL);
|
|
return -1;
|
|
}
|
|
|
|
ptm = gmtime (&tv->tv_sec);
|
|
st.wYear = ptm->tm_year + 1900;
|
|
st.wMonth = ptm->tm_mon + 1;
|
|
st.wDayOfWeek = ptm->tm_wday;
|
|
st.wDay = ptm->tm_mday;
|
|
st.wHour = ptm->tm_hour;
|
|
st.wMinute = ptm->tm_min;
|
|
st.wSecond = ptm->tm_sec;
|
|
st.wMilliseconds = tv->tv_usec / (USPERSEC / MSPERSEC);
|
|
|
|
res = -!SetSystemTime (&st);
|
|
if (res)
|
|
set_errno (EPERM);
|
|
}
|
|
__except (EFAULT)
|
|
{
|
|
res = -1;
|
|
}
|
|
__endtry
|
|
syscall_printf ("%R = settimeofday(%p, %p)", res, tv, tz);
|
|
return res;
|
|
}
|
|
|
|
/* stime: SVr4 */
|
|
extern "C" int
|
|
stime (const time_t *t)
|
|
{
|
|
struct timeval tv = { *t, 0 };
|
|
return settimeofday(&tv, NULL);
|
|
}
|
|
|
|
/* timezone: standards? */
|
|
extern "C" char *
|
|
timezone (void)
|
|
{
|
|
char *b = _my_tls.locals.timezone_buf;
|
|
|
|
tzset ();
|
|
__small_sprintf (b,"GMT%+d:%02d", (int) (-_timezone / 3600), (int) (abs (_timezone / 60) % 60));
|
|
return b;
|
|
}
|
|
|
|
/* Cygwin internal */
|
|
void
|
|
totimeval (struct timeval *dst, PLARGE_INTEGER src, int sub, int flag)
|
|
{
|
|
int64_t x = __to_clock_t (src, flag);
|
|
|
|
x *= (int64_t) USPERSEC / CLOCKS_PER_SEC; /* Turn x into usecs */
|
|
x -= (int64_t) sub * USPERSEC;
|
|
|
|
dst->tv_usec = x % USPERSEC; /* And split */
|
|
dst->tv_sec = x / USPERSEC;
|
|
}
|
|
|
|
/* FIXME: Make thread safe */
|
|
extern "C" int
|
|
gettimeofday (struct timeval *__restrict tv, void *__restrict tzvp)
|
|
{
|
|
struct timezone *tz = (struct timezone *) tzvp;
|
|
static bool tzflag;
|
|
LONGLONG now = get_clock (CLOCK_REALTIME)->usecs ();
|
|
|
|
if (tv)
|
|
{
|
|
tv->tv_sec = now / USPERSEC;
|
|
tv->tv_usec = now % USPERSEC;
|
|
}
|
|
|
|
if (tz)
|
|
{
|
|
if (!tzflag)
|
|
{
|
|
tzset ();
|
|
tzflag = true;
|
|
}
|
|
tz->tz_minuteswest = _timezone / 60;
|
|
tz->tz_dsttime = _daylight;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Cygwin internal */
|
|
void
|
|
timespec_to_filetime (const struct timespec *time_in, PLARGE_INTEGER out)
|
|
{
|
|
if (time_in->tv_nsec == UTIME_OMIT)
|
|
out->QuadPart = 0;
|
|
else
|
|
out->QuadPart = time_in->tv_sec * NS100PERSEC
|
|
+ time_in->tv_nsec / (NSPERSEC/NS100PERSEC) + FACTOR;
|
|
}
|
|
|
|
/* Cygwin internal */
|
|
void
|
|
timeval_to_filetime (const struct timeval *time_in, PLARGE_INTEGER out)
|
|
{
|
|
out->QuadPart = time_in->tv_sec * NS100PERSEC
|
|
+ time_in->tv_usec * (NS100PERSEC/USPERSEC) + FACTOR;
|
|
}
|
|
|
|
/* Cygwin internal */
|
|
bool
|
|
timeval_to_ms (const struct timeval *time_in, DWORD &ms)
|
|
{
|
|
if (time_in->tv_sec < 0 || time_in->tv_usec < 0
|
|
|| time_in->tv_usec >= USPERSEC)
|
|
return false;
|
|
if ((time_in->tv_sec == 0 && time_in->tv_usec == 0)
|
|
|| time_in->tv_sec >= (time_t) (INFINITE / MSPERSEC))
|
|
ms = INFINITE;
|
|
else
|
|
ms = time_in->tv_sec * MSPERSEC
|
|
+ (time_in->tv_usec + (USPERSEC / MSPERSEC) - 1)
|
|
/ (USPERSEC / MSPERSEC);
|
|
return true;
|
|
}
|
|
|
|
/* Cygwin internal */
|
|
static timeval
|
|
time_t_to_timeval (time_t in)
|
|
{
|
|
timeval res;
|
|
res.tv_sec = in;
|
|
res.tv_usec = 0;
|
|
return res;
|
|
}
|
|
|
|
/* Cygwin internal */
|
|
static const struct timespec *
|
|
timeval_to_timespec (const struct timeval *tvp, struct timespec *tmp)
|
|
{
|
|
if (!tvp)
|
|
return NULL;
|
|
|
|
tmp[0].tv_sec = tvp[0].tv_sec;
|
|
tmp[0].tv_nsec = tvp[0].tv_usec * (NSPERSEC/USPERSEC);
|
|
if (tmp[0].tv_nsec < 0)
|
|
tmp[0].tv_nsec = 0;
|
|
else if (tmp[0].tv_nsec >= NSPERSEC)
|
|
tmp[0].tv_nsec = NSPERSEC - 1;
|
|
|
|
tmp[1].tv_sec = tvp[1].tv_sec;
|
|
tmp[1].tv_nsec = tvp[1].tv_usec * (NSPERSEC/USPERSEC);
|
|
if (tmp[1].tv_nsec < 0)
|
|
tmp[1].tv_nsec = 0;
|
|
else if (tmp[1].tv_nsec >= NSPERSEC)
|
|
tmp[1].tv_nsec = NSPERSEC - 1;
|
|
|
|
return tmp;
|
|
}
|
|
|
|
/* Cygwin internal */
|
|
/* Convert a Win32 time to "UNIX" format. */
|
|
time_t
|
|
to_time_t (PLARGE_INTEGER ptr)
|
|
{
|
|
/* A file time is the number of 100ns since jan 1 1601
|
|
stuffed into two long words.
|
|
A time_t is the number of seconds since jan 1 1970. */
|
|
|
|
int64_t x = ptr->QuadPart;
|
|
|
|
/* pass "no time" as epoch */
|
|
if (x == 0)
|
|
return 0;
|
|
|
|
x -= FACTOR; /* number of 100ns between 1601 and 1970 */
|
|
x /= NS100PERSEC; /* number of 100ns in a second */
|
|
return x;
|
|
}
|
|
|
|
/* Cygwin internal */
|
|
/* Convert a Win32 time to "UNIX" timestruc_t format. */
|
|
void
|
|
to_timestruc_t (PLARGE_INTEGER ptr, timestruc_t *out)
|
|
{
|
|
/* A file time is the number of 100ns since jan 1 1601
|
|
stuffed into two long words.
|
|
A timestruc_t is the number of seconds and microseconds since jan 1 1970
|
|
stuffed into a time_t and a long. */
|
|
int64_t x = ptr->QuadPart;
|
|
|
|
/* pass "no time" as epoch */
|
|
if (x == 0)
|
|
{
|
|
out->tv_sec = 0;
|
|
out->tv_nsec = 0;
|
|
return;
|
|
}
|
|
|
|
x -= FACTOR; /* number of 100ns between 1601 and 1970 */
|
|
out->tv_sec = x / NS100PERSEC;
|
|
out->tv_nsec = (x % NS100PERSEC) * (NSPERSEC/NS100PERSEC);
|
|
}
|
|
|
|
/* Cygwin internal */
|
|
/* Get the current time as a "UNIX" timestruc_t format. */
|
|
void
|
|
time_as_timestruc_t (timestruc_t * out)
|
|
{
|
|
LARGE_INTEGER systime;
|
|
|
|
get_system_time (&systime);
|
|
to_timestruc_t (&systime, out);
|
|
}
|
|
|
|
/* time: POSIX 4.5.1.1, C 4.12.2.4 */
|
|
/* Return number of seconds since 00:00 UTC on jan 1, 1970 */
|
|
extern "C" time_t
|
|
time (time_t * ptr)
|
|
{
|
|
time_t res;
|
|
LARGE_INTEGER systime;
|
|
|
|
get_system_time (&systime);
|
|
res = to_time_t (&systime);
|
|
if (ptr)
|
|
*ptr = res;
|
|
|
|
syscall_printf ("%d = time(%p)", res, ptr);
|
|
|
|
return res;
|
|
}
|
|
|
|
int
|
|
utimens_worker (path_conv &win32, const struct timespec *tvp)
|
|
{
|
|
int res = -1;
|
|
|
|
if (win32.error)
|
|
set_errno (win32.error);
|
|
else
|
|
{
|
|
fhandler_base *fh = NULL;
|
|
bool fromfd = false;
|
|
|
|
cygheap_fdenum cfd (true);
|
|
while (cfd.next () >= 0)
|
|
if (cfd->get_access () & (FILE_WRITE_ATTRIBUTES | GENERIC_WRITE)
|
|
&& RtlEqualUnicodeString (cfd->pc.get_nt_native_path (),
|
|
win32.get_nt_native_path (),
|
|
cfd->pc.objcaseinsensitive ()))
|
|
{
|
|
fh = cfd;
|
|
fromfd = true;
|
|
break;
|
|
}
|
|
|
|
if (!fh)
|
|
{
|
|
if (!(fh = build_fh_pc (win32)))
|
|
goto error;
|
|
|
|
if (fh->error ())
|
|
{
|
|
debug_printf ("got %d error from build_fh_pc", fh->error ());
|
|
set_errno (fh->error ());
|
|
}
|
|
}
|
|
|
|
res = fh->utimens (tvp);
|
|
|
|
if (!fromfd)
|
|
delete fh;
|
|
}
|
|
|
|
error:
|
|
syscall_printf ("%R = utimes(%S, %p)", res, win32.get_nt_native_path (), tvp);
|
|
return res;
|
|
}
|
|
|
|
/* utimes: POSIX/SUSv3 */
|
|
extern "C" int
|
|
utimes (const char *path, const struct timeval tvp[2])
|
|
{
|
|
path_conv win32 (path, PC_POSIX | PC_SYM_FOLLOW, stat_suffixes);
|
|
struct timespec tmp[2];
|
|
return utimens_worker (win32, timeval_to_timespec (tvp, tmp));
|
|
}
|
|
|
|
/* BSD */
|
|
extern "C" int
|
|
lutimes (const char *path, const struct timeval tvp[2])
|
|
{
|
|
path_conv win32 (path, PC_POSIX | PC_SYM_NOFOLLOW, stat_suffixes);
|
|
struct timespec tmp[2];
|
|
return utimens_worker (win32, timeval_to_timespec (tvp, tmp));
|
|
}
|
|
|
|
/* futimens: POSIX/SUSv4 */
|
|
extern "C" int
|
|
futimens (int fd, const struct timespec tvp[2])
|
|
{
|
|
int res;
|
|
|
|
cygheap_fdget cfd (fd);
|
|
if (cfd < 0)
|
|
res = -1;
|
|
else if (cfd->get_access () & (FILE_WRITE_ATTRIBUTES | GENERIC_WRITE))
|
|
res = cfd->utimens (tvp);
|
|
else
|
|
res = utimens_worker (cfd->pc, tvp);
|
|
syscall_printf ("%d = futimens(%d, %p)", res, fd, tvp);
|
|
return res;
|
|
}
|
|
|
|
/* BSD */
|
|
extern "C" int
|
|
futimes (int fd, const struct timeval tvp[2])
|
|
{
|
|
struct timespec tmp[2];
|
|
return futimens (fd, timeval_to_timespec (tvp, tmp));
|
|
}
|
|
|
|
/* utime: POSIX 5.6.6.1 */
|
|
extern "C" int
|
|
utime (const char *path, const struct utimbuf *buf)
|
|
{
|
|
struct timeval tmp[2];
|
|
|
|
if (buf == 0)
|
|
return utimes (path, 0);
|
|
|
|
debug_printf ("incoming utime act %lx", buf->actime);
|
|
tmp[0] = time_t_to_timeval (buf->actime);
|
|
tmp[1] = time_t_to_timeval (buf->modtime);
|
|
|
|
return utimes (path, tmp);
|
|
}
|
|
|
|
/* ftime: standards? */
|
|
extern "C" int
|
|
ftime (struct timeb *tp)
|
|
{
|
|
struct timeval tv;
|
|
struct timezone tz;
|
|
|
|
if (gettimeofday (&tv, &tz) < 0)
|
|
return -1;
|
|
|
|
tp->time = tv.tv_sec;
|
|
tp->millitm = tv.tv_usec / 1000;
|
|
tp->timezone = tz.tz_minuteswest;
|
|
tp->dstflag = tz.tz_dsttime;
|
|
|
|
return 0;
|
|
}
|
|
|
|
extern "C" int
|
|
clock_gettime (clockid_t clk_id, struct timespec *tp)
|
|
{
|
|
clk_t *clock = get_clock (clk_id);
|
|
|
|
if (!clock)
|
|
{
|
|
set_errno (EINVAL);
|
|
return -1;
|
|
}
|
|
__try
|
|
{
|
|
return clock->nsecs (clk_id, tp);
|
|
}
|
|
__except (EFAULT) {}
|
|
__endtry
|
|
return -1;
|
|
}
|
|
|
|
extern "C" int
|
|
clock_settime (clockid_t clk_id, const struct timespec *tp)
|
|
{
|
|
struct timeval tv;
|
|
|
|
if (CLOCKID_IS_PROCESS (clk_id) || CLOCKID_IS_THREAD (clk_id))
|
|
/* According to POSIX, the privileges to set a particular clock
|
|
* are implementation-defined. On Linux, CPU-time clocks are not
|
|
* settable; do the same here.
|
|
*/
|
|
{
|
|
set_errno (EPERM);
|
|
return -1;
|
|
}
|
|
|
|
if (clk_id != CLOCK_REALTIME_COARSE && clk_id != CLOCK_REALTIME)
|
|
{
|
|
set_errno (EINVAL);
|
|
return -1;
|
|
}
|
|
|
|
__try
|
|
{
|
|
tv.tv_sec = tp->tv_sec;
|
|
tv.tv_usec = tp->tv_nsec / 1000;
|
|
}
|
|
__except (EFAULT)
|
|
{
|
|
return -1;
|
|
}
|
|
__endtry
|
|
|
|
return settimeofday (&tv, NULL);
|
|
}
|
|
|
|
extern "C" int
|
|
clock_getres (clockid_t clk_id, struct timespec *tp)
|
|
{
|
|
clk_t *clock = get_clock (clk_id);
|
|
|
|
if (!clock)
|
|
{
|
|
set_errno (EINVAL);
|
|
return -1;
|
|
}
|
|
__try
|
|
{
|
|
clock->resolution (tp);
|
|
}
|
|
__except (EFAULT)
|
|
{
|
|
return -1;
|
|
}
|
|
__endtry
|
|
return 0;
|
|
}
|
|
|
|
extern "C" int
|
|
clock_setres (clockid_t clk_id, struct timespec *tp)
|
|
{
|
|
/* Don't use this function. It only exists in QNX. Just return
|
|
success on CLOCK_REALTIME for backward compat. */
|
|
if (clk_id != CLOCK_REALTIME)
|
|
{
|
|
set_errno (EINVAL);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
extern "C" int
|
|
clock_getcpuclockid (pid_t pid, clockid_t *clk_id)
|
|
{
|
|
if (pid != 0)
|
|
{
|
|
pinfo p (pid);
|
|
if (!p || !p->exists ())
|
|
return (ESRCH);
|
|
}
|
|
*clk_id = (clockid_t) PID_TO_CLOCKID (pid);
|
|
return 0;
|
|
}
|
|
|
|
extern "C" int
|
|
timespec_get (struct timespec *ts, int base)
|
|
{
|
|
if (base != TIME_UTC)
|
|
return 0;
|
|
clock_gettime (CLOCK_REALTIME, ts);
|
|
return base;
|
|
}
|
|
|
|
EXPORT_ALIAS (gettimeofday, _gettimeofday)
|
|
EXPORT_ALIAS (times, _times)
|