chiark / gitweb /
timedatectl: get time values from the service instead of the client
[elogind.git] / src / shared / hwclock.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010-2012 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <assert.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #include <stdlib.h>
27 #include <signal.h>
28 #include <stdio.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32 #include <sys/ioctl.h>
33 #include <stdarg.h>
34 #include <ctype.h>
35 #include <sys/prctl.h>
36 #include <sys/time.h>
37 #include <linux/rtc.h>
38
39 #include "macro.h"
40 #include "util.h"
41 #include "log.h"
42 #include "strv.h"
43 #include "hwclock.h"
44 #include "fileio.h"
45
46 int hwclock_get_time(struct tm *tm) {
47         int fd;
48         int err = 0;
49
50         assert(tm);
51
52         fd = open("/dev/rtc", O_RDONLY|O_CLOEXEC);
53         if (fd < 0)
54                 return -errno;
55
56         /* This leaves the timezone fields of struct tm
57          * uninitialized! */
58         if (ioctl(fd, RTC_RD_TIME, tm) < 0)
59                 err = -errno;
60
61         /* We don't know daylight saving, so we reset this in order not
62          * to confuse mktime(). */
63         tm->tm_isdst = -1;
64
65         close_nointr_nofail(fd);
66
67         return err;
68 }
69
70 int hwclock_set_time(const struct tm *tm) {
71         int fd;
72         int err = 0;
73
74         assert(tm);
75
76         fd = open("/dev/rtc", O_RDONLY|O_CLOEXEC);
77         if (fd < 0)
78                 return -errno;
79
80         if (ioctl(fd, RTC_SET_TIME, tm) < 0)
81                 err = -errno;
82
83         close_nointr_nofail(fd);
84
85         return err;
86 }
87
88 int hwclock_is_localtime(void) {
89         _cleanup_fclose_ FILE *f;
90
91         /*
92          * The third line of adjtime is "UTC" or "LOCAL" or nothing.
93          *   # /etc/adjtime
94          *   0.0 0 0
95          *   0
96          *   UTC
97          */
98         f = fopen("/etc/adjtime", "re");
99         if (f) {
100                 char line[LINE_MAX];
101                 bool b;
102
103                 b = fgets(line, sizeof(line), f) &&
104                         fgets(line, sizeof(line), f) &&
105                         fgets(line, sizeof(line), f);
106                 if (!b)
107                         return -EIO;
108
109                 truncate_nl(line);
110                 return streq(line, "LOCAL");
111
112         } else if (errno != ENOENT)
113                 return -errno;
114
115         return 0;
116 }
117
118 int hwclock_set_timezone(int *min) {
119         const struct timeval *tv_null = NULL;
120         struct timespec ts;
121         struct tm *tm;
122         int minutesdelta;
123         struct timezone tz;
124
125         assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
126         assert_se(tm = localtime(&ts.tv_sec));
127         minutesdelta = tm->tm_gmtoff / 60;
128
129         tz.tz_minuteswest = -minutesdelta;
130         tz.tz_dsttime = 0; /* DST_NONE*/
131
132         /*
133          * If the hardware clock does not run in UTC, but in local time:
134          * The very first time we set the kernel's timezone, it will warp
135          * the clock so that it runs in UTC instead of local time.
136          */
137         if (settimeofday(tv_null, &tz) < 0)
138                 return -errno;
139         if (min)
140                 *min = minutesdelta;
141         return 0;
142 }
143
144 int hwclock_reset_timezone(void) {
145         const struct timeval *tv_null = NULL;
146         struct timezone tz;
147
148         tz.tz_minuteswest = 0;
149         tz.tz_dsttime = 0; /* DST_NONE*/
150
151         /*
152          * The very first time we set the kernel's timezone, it will warp
153          * the clock. Do a dummy call here, so the time warping is sealed
154          * and we set only the timezone with next call.
155          */
156         if (settimeofday(tv_null, &tz) < 0)
157                 return -errno;
158
159         return 0;
160 }