1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <limits.h>
6 #include <stdbool.h>
7 #include <time.h>
8 #include <linux/rtc.h>
9 #include <stdio.h>
10 #include <sys/ioctl.h>
11 #include <sys/time.h>
12 
13 #include "alloc-util.h"
14 #include "clock-util.h"
15 #include "errno-util.h"
16 #include "fd-util.h"
17 #include "fileio.h"
18 #include "macro.h"
19 #include "string-util.h"
20 
clock_get_hwclock(struct tm * tm)21 int clock_get_hwclock(struct tm *tm) {
22         _cleanup_close_ int fd = -1;
23 
24         assert(tm);
25 
26         fd = open("/dev/rtc", O_RDONLY|O_CLOEXEC);
27         if (fd < 0)
28                 return -errno;
29 
30         /* This leaves the timezone fields of struct tm
31          * uninitialized! */
32         if (ioctl(fd, RTC_RD_TIME, tm) < 0)
33                 return -errno;
34 
35         /* We don't know daylight saving, so we reset this in order not
36          * to confuse mktime(). */
37         tm->tm_isdst = -1;
38 
39         return 0;
40 }
41 
clock_set_hwclock(const struct tm * tm)42 int clock_set_hwclock(const struct tm *tm) {
43         _cleanup_close_ int fd = -1;
44 
45         assert(tm);
46 
47         fd = open("/dev/rtc", O_RDONLY|O_CLOEXEC);
48         if (fd < 0)
49                 return -errno;
50 
51         return RET_NERRNO(ioctl(fd, RTC_SET_TIME, tm));
52 }
53 
clock_is_localtime(const char * adjtime_path)54 int clock_is_localtime(const char* adjtime_path) {
55         _cleanup_fclose_ FILE *f = NULL;
56         int r;
57 
58         if (!adjtime_path)
59                 adjtime_path = "/etc/adjtime";
60 
61         /*
62          * The third line of adjtime is "UTC" or "LOCAL" or nothing.
63          *   # /etc/adjtime
64          *   0.0 0 0
65          *   0
66          *   UTC
67          */
68         f = fopen(adjtime_path, "re");
69         if (f) {
70                 _cleanup_free_ char *line = NULL;
71                 unsigned i;
72 
73                 for (i = 0; i < 2; i++) { /* skip the first two lines */
74                         r = read_line(f, LONG_LINE_MAX, NULL);
75                         if (r < 0)
76                                 return r;
77                         if (r == 0)
78                                 return false; /* less than three lines → default to UTC */
79                 }
80 
81                 r = read_line(f, LONG_LINE_MAX, &line);
82                 if (r < 0)
83                         return r;
84                 if (r == 0)
85                         return false; /* less than three lines → default to UTC */
86 
87                 return streq(line, "LOCAL");
88 
89         } else if (errno != ENOENT)
90                 return -errno;
91 
92         /* adjtime not present → default to UTC */
93         return false;
94 }
95 
clock_set_timezone(int * ret_minutesdelta)96 int clock_set_timezone(int *ret_minutesdelta) {
97         struct timespec ts;
98         struct tm tm;
99         int minutesdelta;
100         struct timezone tz;
101 
102         assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
103         assert_se(localtime_r(&ts.tv_sec, &tm));
104         minutesdelta = tm.tm_gmtoff / 60;
105 
106         tz = (struct timezone) {
107                 .tz_minuteswest = -minutesdelta,
108                 .tz_dsttime = 0, /* DST_NONE */
109         };
110 
111         /* If the RTC does not run in UTC but in local time, the very first call to settimeofday() will set
112          * the kernel's timezone and will warp the system clock, so that it runs in UTC instead of the local
113          * time we have read from the RTC. */
114         if (settimeofday(NULL, &tz) < 0)
115                 return -errno;
116 
117         if (ret_minutesdelta)
118                 *ret_minutesdelta = minutesdelta;
119 
120         return 0;
121 }
122 
clock_reset_timewarp(void)123 int clock_reset_timewarp(void) {
124         static const struct timezone tz = {
125                 .tz_minuteswest = 0,
126                 .tz_dsttime = 0, /* DST_NONE */
127         };
128 
129         /* The very first call to settimeofday() does time warp magic. Do a dummy call here, so the time
130          * warping is sealed and all later calls behave as expected. */
131         return RET_NERRNO(settimeofday(NULL, &tz));
132 }
133 
134 #define EPOCH_FILE "/usr/lib/clock-epoch"
135 
clock_apply_epoch(ClockChangeDirection * ret_attempted_change)136 int clock_apply_epoch(ClockChangeDirection *ret_attempted_change) {
137         usec_t epoch_usec, now_usec;
138         struct stat st;
139 
140         /* NB: we update *ret_attempted_change in *all* cases, both
141          * on success and failure, to indicate what we intended to do! */
142 
143         assert(ret_attempted_change);
144 
145         if (stat(EPOCH_FILE, &st) < 0) {
146                 if (errno != ENOENT)
147                         log_warning_errno(errno, "Cannot stat " EPOCH_FILE ": %m");
148 
149                 epoch_usec = (usec_t) TIME_EPOCH * USEC_PER_SEC;
150         } else
151                 epoch_usec = timespec_load(&st.st_mtim);
152 
153         now_usec = now(CLOCK_REALTIME);
154         if (now_usec < epoch_usec)
155                 *ret_attempted_change = CLOCK_CHANGE_FORWARD;
156         else if (now_usec > usec_add(epoch_usec, CLOCK_VALID_RANGE_USEC_MAX))
157                 *ret_attempted_change = CLOCK_CHANGE_BACKWARD;
158         else {
159                 *ret_attempted_change = CLOCK_CHANGE_NOOP;
160                 return 0;
161         }
162 
163         if (clock_settime(CLOCK_REALTIME, TIMESPEC_STORE(epoch_usec)) < 0)
164                 return -errno;
165 
166         return 1;
167 }
168