Line data Source code
1 : /* SPDX-License-Identifier: LGPL-2.1+ */ 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 : 21 0 : int clock_get_hwclock(struct tm *tm) { 22 0 : _cleanup_close_ int fd = -1; 23 : 24 0 : assert(tm); 25 : 26 0 : fd = open("/dev/rtc", O_RDONLY|O_CLOEXEC); 27 0 : if (fd < 0) 28 0 : return -errno; 29 : 30 : /* This leaves the timezone fields of struct tm 31 : * uninitialized! */ 32 0 : if (ioctl(fd, RTC_RD_TIME, tm) < 0) 33 0 : return -errno; 34 : 35 : /* We don't know daylight saving, so we reset this in order not 36 : * to confuse mktime(). */ 37 0 : tm->tm_isdst = -1; 38 : 39 0 : return 0; 40 : } 41 : 42 0 : int clock_set_hwclock(const struct tm *tm) { 43 0 : _cleanup_close_ int fd = -1; 44 : 45 0 : assert(tm); 46 : 47 0 : fd = open("/dev/rtc", O_RDONLY|O_CLOEXEC); 48 0 : if (fd < 0) 49 0 : return -errno; 50 : 51 0 : if (ioctl(fd, RTC_SET_TIME, tm) < 0) 52 0 : return -errno; 53 : 54 0 : return 0; 55 : } 56 : 57 11 : int clock_is_localtime(const char* adjtime_path) { 58 11 : _cleanup_fclose_ FILE *f; 59 : int r; 60 : 61 11 : if (!adjtime_path) 62 1 : adjtime_path = "/etc/adjtime"; 63 : 64 : /* 65 : * The third line of adjtime is "UTC" or "LOCAL" or nothing. 66 : * # /etc/adjtime 67 : * 0.0 0 0 68 : * 0 69 : * UTC 70 : */ 71 11 : f = fopen(adjtime_path, "re"); 72 11 : if (f) { 73 10 : _cleanup_free_ char *line = NULL; 74 : unsigned i; 75 : 76 28 : for (i = 0; i < 2; i++) { /* skip the first two lines */ 77 20 : r = read_line(f, LONG_LINE_MAX, NULL); 78 20 : if (r < 0) 79 0 : return r; 80 20 : if (r == 0) 81 2 : return false; /* less than three lines → default to UTC */ 82 : } 83 : 84 8 : r = read_line(f, LONG_LINE_MAX, &line); 85 8 : if (r < 0) 86 0 : return r; 87 8 : if (r == 0) 88 2 : return false; /* less than three lines → default to UTC */ 89 : 90 6 : return streq(line, "LOCAL"); 91 : 92 1 : } else if (errno != ENOENT) 93 0 : return -errno; 94 : 95 : /* adjtime not present → default to UTC */ 96 1 : return false; 97 : } 98 : 99 0 : int clock_set_timezone(int *min) { 100 0 : const struct timeval *tv_null = NULL; 101 : struct timespec ts; 102 : struct tm tm; 103 : int minutesdelta; 104 : struct timezone tz; 105 : 106 0 : assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0); 107 0 : assert_se(localtime_r(&ts.tv_sec, &tm)); 108 0 : minutesdelta = tm.tm_gmtoff / 60; 109 : 110 0 : tz.tz_minuteswest = -minutesdelta; 111 0 : tz.tz_dsttime = 0; /* DST_NONE */ 112 : 113 : /* 114 : * If the RTC does not run in UTC but in local time, the very first 115 : * call to settimeofday() will set the kernel's timezone and will warp the 116 : * system clock, so that it runs in UTC instead of the local time we 117 : * have read from the RTC. 118 : */ 119 0 : if (settimeofday(tv_null, &tz) < 0) 120 0 : return negative_errno(); 121 : 122 0 : if (min) 123 0 : *min = minutesdelta; 124 0 : return 0; 125 : } 126 : 127 0 : int clock_reset_timewarp(void) { 128 0 : const struct timeval *tv_null = NULL; 129 : struct timezone tz; 130 : 131 0 : tz.tz_minuteswest = 0; 132 0 : tz.tz_dsttime = 0; /* DST_NONE */ 133 : 134 : /* 135 : * The very first call to settimeofday() does time warp magic. Do a 136 : * dummy call here, so the time warping is sealed and all later calls 137 : * behave as expected. 138 : */ 139 0 : if (settimeofday(tv_null, &tz) < 0) 140 0 : return -errno; 141 : 142 0 : return 0; 143 : } 144 : 145 : #define TIME_EPOCH_USEC ((usec_t) TIME_EPOCH * USEC_PER_SEC) 146 : 147 0 : int clock_apply_epoch(void) { 148 : struct timespec ts; 149 : 150 0 : if (now(CLOCK_REALTIME) >= TIME_EPOCH_USEC) 151 0 : return 0; 152 : 153 0 : if (clock_settime(CLOCK_REALTIME, timespec_store(&ts, TIME_EPOCH_USEC)) < 0) 154 0 : return -errno; 155 : 156 0 : return 1; 157 : }