mirror of
https://github.com/systemd/systemd.git
synced 2024-11-27 12:13:33 +08:00
shared: add timer_get_dst()
This commit is contained in:
parent
465b1686ec
commit
b7f1542c8b
@ -801,7 +801,9 @@ libsystemd_shared_la_SOURCES = \
|
||||
src/shared/spawn-polkit-agent.c \
|
||||
src/shared/spawn-polkit-agent.h \
|
||||
src/shared/hwclock.c \
|
||||
src/shared/hwclock.h
|
||||
src/shared/hwclock.h \
|
||||
src/shared/time-dst.c \
|
||||
src/shared/time-dst.h
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
noinst_LTLIBRARIES += \
|
||||
|
338
src/shared/time-dst.c
Normal file
338
src/shared/time-dst.c
Normal file
@ -0,0 +1,338 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Timezone file reading code from glibc 2.16.
|
||||
|
||||
Copyright (C) 1991-2012 Free Software Foundation, Inc.
|
||||
Copyright 2012 Kay Sievers
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <endian.h>
|
||||
#include <byteswap.h>
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "time-dst.h"
|
||||
|
||||
/*
|
||||
* If tzh_version is '2' or greater, the above is followed by a second instance
|
||||
* of tzhead and a second instance of the data in which each coded transition
|
||||
* time uses 8 rather than 4 chars, then a POSIX-TZ-environment-variable-style
|
||||
* string for use in handling instants after the last transition time stored in
|
||||
* the file * (with nothing between the newlines if there is no POSIX
|
||||
* representation for such instants).
|
||||
*/
|
||||
#define TZ_MAGIC "TZif"
|
||||
struct tzhead {
|
||||
char tzh_magic[4]; /* TZ_MAGIC */
|
||||
char tzh_version[1]; /* '\0' or '2' as of 2005 */
|
||||
char tzh_reserved[15]; /* reserved--must be zero */
|
||||
char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */
|
||||
char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
|
||||
char tzh_leapcnt[4]; /* coded number of leap seconds */
|
||||
char tzh_timecnt[4]; /* coded number of transition times */
|
||||
char tzh_typecnt[4]; /* coded number of local time types */
|
||||
char tzh_charcnt[4]; /* coded number of abbr. chars */
|
||||
};
|
||||
|
||||
struct ttinfo {
|
||||
long int offset; /* Seconds east of GMT. */
|
||||
unsigned char isdst; /* Used to set tm_isdst. */
|
||||
unsigned char idx; /* Index into `zone_names'. */
|
||||
unsigned char isstd; /* Transition times are in standard time. */
|
||||
unsigned char isgmt; /* Transition times are in GMT. */
|
||||
};
|
||||
|
||||
struct leap {
|
||||
time_t transition; /* Time the transition takes effect. */
|
||||
long int change; /* Seconds of correction to apply. */
|
||||
};
|
||||
|
||||
static inline int decode(const void *ptr) {
|
||||
return be32toh(*(int *)ptr);
|
||||
}
|
||||
|
||||
static inline int64_t decode64(const void *ptr) {
|
||||
return be64toh(*(int64_t *)ptr);
|
||||
}
|
||||
|
||||
int time_get_dst(time_t date, const char *tzfile,
|
||||
time_t *switch_cur, char **zone_cur, bool *dst_cur,
|
||||
time_t *switch_next, char **zone_next, bool *dst_next) {
|
||||
time_t *transitions = NULL;
|
||||
size_t num_transitions = 0;
|
||||
unsigned char *type_idxs = 0;
|
||||
size_t num_types = 0;
|
||||
struct ttinfo *types = NULL;
|
||||
char *zone_names = NULL;
|
||||
struct stat st;
|
||||
size_t num_isstd, num_isgmt;
|
||||
FILE *f;
|
||||
struct tzhead tzhead;
|
||||
size_t chars;
|
||||
size_t i;
|
||||
size_t total_size;
|
||||
size_t types_idx;
|
||||
int trans_width = 4;
|
||||
size_t tzspec_len;
|
||||
size_t num_leaps;
|
||||
size_t lo, hi;
|
||||
int err = 0;
|
||||
|
||||
f = fopen(tzfile, "re");
|
||||
if (f == NULL)
|
||||
return -errno;
|
||||
|
||||
if (fstat(fileno(f), &st) < 0) {
|
||||
err = -errno;
|
||||
fclose(f);
|
||||
return err;
|
||||
}
|
||||
|
||||
read_again:
|
||||
if (fread((void *)&tzhead, sizeof(tzhead), 1, f) != 1 ||
|
||||
memcmp(tzhead.tzh_magic, TZ_MAGIC, sizeof(tzhead.tzh_magic)) != 0)
|
||||
goto lose;
|
||||
|
||||
num_transitions = (size_t)decode(tzhead.tzh_timecnt);
|
||||
num_types = (size_t)decode(tzhead.tzh_typecnt);
|
||||
chars = (size_t)decode(tzhead.tzh_charcnt);
|
||||
num_leaps = (size_t)decode(tzhead.tzh_leapcnt);
|
||||
num_isstd = (size_t)decode(tzhead.tzh_ttisstdcnt);
|
||||
num_isgmt = (size_t)decode(tzhead.tzh_ttisgmtcnt);
|
||||
|
||||
/* For platforms with 64-bit time_t we use the new format if available. */
|
||||
if (sizeof(time_t) == 8 && trans_width == 4 && tzhead.tzh_version[0] != '\0') {
|
||||
size_t to_skip;
|
||||
|
||||
/* We use the 8-byte format. */
|
||||
trans_width = 8;
|
||||
|
||||
/* Position the stream before the second header. */
|
||||
to_skip = (num_transitions * (4 + 1)
|
||||
+ num_types * 6
|
||||
+ chars
|
||||
+ num_leaps * 8 + num_isstd + num_isgmt);
|
||||
if (fseek(f, to_skip, SEEK_CUR) != 0)
|
||||
goto lose;
|
||||
|
||||
goto read_again;
|
||||
}
|
||||
|
||||
if (num_transitions > ((SIZE_MAX - (__alignof__(struct ttinfo) - 1)) / (sizeof(time_t) + 1)))
|
||||
goto lose;
|
||||
|
||||
total_size = num_transitions * (sizeof(time_t) + 1);
|
||||
total_size = ((total_size + __alignof__(struct ttinfo) - 1) & ~(__alignof__(struct ttinfo) - 1));
|
||||
types_idx = total_size;
|
||||
if (num_leaps > (SIZE_MAX - total_size) / sizeof(struct ttinfo))
|
||||
goto lose;
|
||||
|
||||
total_size += num_types * sizeof(struct ttinfo);
|
||||
if (chars > SIZE_MAX - total_size)
|
||||
goto lose;
|
||||
|
||||
total_size += chars;
|
||||
if (__alignof__(struct leap) - 1 > SIZE_MAX - total_size)
|
||||
goto lose;
|
||||
|
||||
total_size = ((total_size + __alignof__(struct leap) - 1) & ~(__alignof__(struct leap) - 1));
|
||||
if (num_leaps > (SIZE_MAX - total_size) / sizeof(struct leap))
|
||||
goto lose;
|
||||
|
||||
total_size += num_leaps * sizeof(struct leap);
|
||||
tzspec_len = 0;
|
||||
if (sizeof(time_t) == 8 && trans_width == 8) {
|
||||
off_t rem = st.st_size - ftello(f);
|
||||
|
||||
if (rem < 0 || (size_t) rem < (num_transitions * (8 + 1) + num_types * 6 + chars))
|
||||
goto lose;
|
||||
tzspec_len = (size_t) rem - (num_transitions * (8 + 1) + num_types * 6 + chars);
|
||||
if (num_leaps > SIZE_MAX / 12 || tzspec_len < num_leaps * 12)
|
||||
goto lose;
|
||||
tzspec_len -= num_leaps * 12;
|
||||
if (tzspec_len < num_isstd)
|
||||
goto lose;
|
||||
tzspec_len -= num_isstd;
|
||||
if (tzspec_len == 0 || tzspec_len - 1 < num_isgmt)
|
||||
goto lose;
|
||||
tzspec_len -= num_isgmt + 1;
|
||||
if (SIZE_MAX - total_size < tzspec_len)
|
||||
goto lose;
|
||||
}
|
||||
|
||||
transitions = (time_t *)calloc(total_size + tzspec_len, 1);
|
||||
if (transitions == NULL)
|
||||
goto lose;
|
||||
|
||||
type_idxs = (unsigned char *)transitions + (num_transitions
|
||||
* sizeof(time_t));
|
||||
types = (struct ttinfo *)((char *)transitions + types_idx);
|
||||
zone_names = (char *)types + num_types * sizeof(struct ttinfo);
|
||||
|
||||
if (sizeof(time_t) == 4 || trans_width == 8) {
|
||||
if (fread(transitions, trans_width + 1, num_transitions, f) != num_transitions)
|
||||
goto lose;
|
||||
} else {
|
||||
if (fread(transitions, 4, num_transitions, f) != num_transitions ||
|
||||
fread(type_idxs, 1, num_transitions, f) != num_transitions)
|
||||
goto lose;
|
||||
}
|
||||
|
||||
/* Check for bogus indices in the data file, so we can hereafter
|
||||
safely use type_idxs[T] as indices into `types' and never crash. */
|
||||
for (i = 0; i < num_transitions; ++i)
|
||||
if (type_idxs[i] >= num_types)
|
||||
goto lose;
|
||||
|
||||
if ((BYTE_ORDER != BIG_ENDIAN && (sizeof(time_t) == 4 || trans_width == 4)) ||
|
||||
(BYTE_ORDER == BIG_ENDIAN && sizeof(time_t) == 8 && trans_width == 4)) {
|
||||
/* Decode the transition times, stored as 4-byte integers in
|
||||
network (big-endian) byte order. We work from the end of
|
||||
the array so as not to clobber the next element to be
|
||||
processed when sizeof (time_t) > 4. */
|
||||
i = num_transitions;
|
||||
while (i-- > 0)
|
||||
transitions[i] = decode((char *)transitions + i * 4);
|
||||
} else if (BYTE_ORDER != BIG_ENDIAN && sizeof(time_t) == 8) {
|
||||
/* Decode the transition times, stored as 8-byte integers in
|
||||
network (big-endian) byte order. */
|
||||
for (i = 0; i < num_transitions; ++i)
|
||||
transitions[i] = decode64((char *)transitions + i * 8);
|
||||
}
|
||||
|
||||
for (i = 0; i < num_types; ++i) {
|
||||
unsigned char x[4];
|
||||
int c;
|
||||
|
||||
if (fread(x, 1, sizeof(x), f) != sizeof(x))
|
||||
goto lose;
|
||||
c = getc(f);
|
||||
if ((unsigned int)c > 1u)
|
||||
goto lose;
|
||||
types[i].isdst = c;
|
||||
c = getc(f);
|
||||
if ((size_t) c > chars)
|
||||
/* Bogus index in data file. */
|
||||
goto lose;
|
||||
types[i].idx = c;
|
||||
types[i].offset = (long int)decode(x);
|
||||
}
|
||||
|
||||
if (fread(zone_names, 1, chars, f) != chars)
|
||||
goto lose;
|
||||
|
||||
for (i = 0; i < num_isstd; ++i) {
|
||||
int c = getc(f);
|
||||
if (c == EOF)
|
||||
goto lose;
|
||||
types[i].isstd = c != 0;
|
||||
}
|
||||
|
||||
while (i < num_types)
|
||||
types[i++].isstd = 0;
|
||||
|
||||
for (i = 0; i < num_isgmt; ++i) {
|
||||
int c = getc(f);
|
||||
if (c == EOF)
|
||||
goto lose;
|
||||
types[i].isgmt = c != 0;
|
||||
}
|
||||
|
||||
while (i < num_types)
|
||||
types[i++].isgmt = 0;
|
||||
|
||||
if (num_transitions == 0)
|
||||
goto lose;
|
||||
|
||||
if (date < transitions[0] || date >= transitions[num_transitions - 1])
|
||||
goto lose;
|
||||
|
||||
/* Find the first transition after TIMER, and
|
||||
then pick the type of the transition before it. */
|
||||
lo = 0;
|
||||
hi = num_transitions - 1;
|
||||
|
||||
/* Assume that DST is changing twice a year and guess initial
|
||||
search spot from it.
|
||||
Half of a gregorian year has on average 365.2425 * 86400 / 2
|
||||
= 15778476 seconds. */
|
||||
i = (transitions[num_transitions - 1] - date) / 15778476;
|
||||
if (i < num_transitions) {
|
||||
i = num_transitions - 1 - i;
|
||||
if (date < transitions[i]) {
|
||||
if (i < 10 || date >= transitions[i - 10]) {
|
||||
/* Linear search. */
|
||||
while (date < transitions[i - 1])
|
||||
i--;
|
||||
goto found;
|
||||
}
|
||||
hi = i - 10;
|
||||
} else {
|
||||
if (i + 10 >= num_transitions || date < transitions[i + 10]) {
|
||||
/* Linear search. */
|
||||
while (date >= transitions[i])
|
||||
i++;
|
||||
goto found;
|
||||
}
|
||||
lo = i + 10;
|
||||
}
|
||||
}
|
||||
|
||||
/* Binary search. */
|
||||
while (lo + 1 < hi) {
|
||||
i = (lo + hi) / 2;
|
||||
if (date < transitions[i])
|
||||
hi = i;
|
||||
else
|
||||
lo = i;
|
||||
}
|
||||
i = hi;
|
||||
|
||||
found:
|
||||
if (switch_cur)
|
||||
*switch_cur = transitions[i-1];
|
||||
if (zone_cur)
|
||||
*zone_cur = strdup(&zone_names[types[type_idxs[i - 1]].idx]);
|
||||
if (dst_cur)
|
||||
*dst_cur = types[type_idxs[i-1]].isdst;
|
||||
if (switch_next)
|
||||
*switch_next = transitions[i];
|
||||
if (zone_next)
|
||||
*zone_next = strdup(&zone_names[types[type_idxs[i]].idx]);
|
||||
if (dst_next)
|
||||
*dst_next = types[type_idxs[i]].isdst;
|
||||
|
||||
free(transitions);
|
||||
fclose(f);
|
||||
return 0;
|
||||
lose:
|
||||
free(transitions);
|
||||
fclose(f);
|
||||
return err;
|
||||
}
|
29
src/shared/time-dst.h
Normal file
29
src/shared/time-dst.h
Normal file
@ -0,0 +1,29 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
#ifndef footimedst
|
||||
#define footimedst
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2012 Kay Sievers
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
int time_get_dst(time_t date, const char *tzfile,
|
||||
time_t *switch_cur, char **zone_cur, bool *dst_cur,
|
||||
time_t *switch_next, char **zone_next, bool *dst_next);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user