hwclock.c revision a866073d35dea05e6f3e56328d3eb6436943e7e6
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering/***
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering This file is part of systemd.
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering Copyright 2010-2012 Lennart Poettering
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering systemd is free software; you can redistribute it and/or modify it
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering under the terms of the GNU Lesser General Public License as published by
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering (at your option) any later version.
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering systemd is distributed in the hope that it will be useful, but
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering Lesser General Public License for more details.
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering You should have received a copy of the GNU Lesser General Public License
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering***/
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering
96cde13ace6406582688028f3df5668a172ba628Zbigniew Jędrzejewski-Szmek#include <assert.h>
96cde13ace6406582688028f3df5668a172ba628Zbigniew Jędrzejewski-Szmek#include <string.h>
96cde13ace6406582688028f3df5668a172ba628Zbigniew Jędrzejewski-Szmek#include <unistd.h>
143bfdaf0b890fa7acadf02d1eafacaef1b696bdHolger Hans Peter Freyther#include <errno.h>
6482f6269c87d2249e52e889a63adbdd50f2d691Ronny Chevalier#include <stdlib.h>
0b452006de98294d1690f045f6ea2f7f6630ec3bRonny Chevalier#include <signal.h>
96cde13ace6406582688028f3df5668a172ba628Zbigniew Jędrzejewski-Szmek#include <stdio.h>
7027ff61a34a12487712b382a061c654acc3a679Lennart Poettering#include <sys/types.h>
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering#include <sys/stat.h>
d4fffc4b8beb86e77fd710c1f43913a490ed083aZbigniew Jędrzejewski-Szmek#include <fcntl.h>
96cde13ace6406582688028f3df5668a172ba628Zbigniew Jędrzejewski-Szmek#include <sys/ioctl.h>
d4fffc4b8beb86e77fd710c1f43913a490ed083aZbigniew Jędrzejewski-Szmek#include <stdarg.h>
d4fffc4b8beb86e77fd710c1f43913a490ed083aZbigniew Jędrzejewski-Szmek#include <ctype.h>
d4fffc4b8beb86e77fd710c1f43913a490ed083aZbigniew Jędrzejewski-Szmek#include <sys/prctl.h>
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering#include <sys/time.h>
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering#include <linux/rtc.h>
96cde13ace6406582688028f3df5668a172ba628Zbigniew Jędrzejewski-Szmek
7027ff61a34a12487712b382a061c654acc3a679Lennart Poettering#include "macro.h"
d7bd3de0654669e65b9642c248c5fa6d1d9a9f61Lennart Poettering#include "util.h"
d7bd3de0654669e65b9642c248c5fa6d1d9a9f61Lennart Poettering#include "log.h"
d7bd3de0654669e65b9642c248c5fa6d1d9a9f61Lennart Poettering#include "strv.h"
cfeaa44a09756a93a881f786678973d9b1e382dbLennart Poettering#include "hwclock.h"
cfeaa44a09756a93a881f786678973d9b1e382dbLennart Poettering
7027ff61a34a12487712b382a061c654acc3a679Lennart Poetteringstatic int rtc_open(int flags) {
cfeaa44a09756a93a881f786678973d9b1e382dbLennart Poettering int fd;
cfeaa44a09756a93a881f786678973d9b1e382dbLennart Poettering DIR *d;
d7bd3de0654669e65b9642c248c5fa6d1d9a9f61Lennart Poettering
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering /* First, we try to make use of the /dev/rtc symlink. If that
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering * doesn't exist, we open the first RTC which has hctosys=1
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering * set. If we don't find any we just take the first RTC that
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering * exists at all. */
d4fffc4b8beb86e77fd710c1f43913a490ed083aZbigniew Jędrzejewski-Szmek
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering fd = open("/dev/rtc", flags);
d4fffc4b8beb86e77fd710c1f43913a490ed083aZbigniew Jędrzejewski-Szmek if (fd >= 0)
d4fffc4b8beb86e77fd710c1f43913a490ed083aZbigniew Jędrzejewski-Szmek return fd;
d4fffc4b8beb86e77fd710c1f43913a490ed083aZbigniew Jędrzejewski-Szmek
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering d = opendir("/sys/class/rtc");
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering if (!d)
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering goto fallback;
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering for (;;) {
d7bd3de0654669e65b9642c248c5fa6d1d9a9f61Lennart Poettering char *p, *v;
d7bd3de0654669e65b9642c248c5fa6d1d9a9f61Lennart Poettering struct dirent buf, *de;
d7bd3de0654669e65b9642c248c5fa6d1d9a9f61Lennart Poettering int r;
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering
cfeaa44a09756a93a881f786678973d9b1e382dbLennart Poettering r = readdir_r(d, &buf, &de);
cfeaa44a09756a93a881f786678973d9b1e382dbLennart Poettering if (r != 0)
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering goto fallback;
d7bd3de0654669e65b9642c248c5fa6d1d9a9f61Lennart Poettering
d4fffc4b8beb86e77fd710c1f43913a490ed083aZbigniew Jędrzejewski-Szmek if (!de)
cfeaa44a09756a93a881f786678973d9b1e382dbLennart Poettering goto fallback;
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering if (ignore_file(de->d_name))
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering continue;
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering
d4fffc4b8beb86e77fd710c1f43913a490ed083aZbigniew Jędrzejewski-Szmek p = strjoin("/sys/class/rtc/", de->d_name, "/hctosys", NULL);
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering if (!p) {
d4fffc4b8beb86e77fd710c1f43913a490ed083aZbigniew Jędrzejewski-Szmek closedir(d);
d4fffc4b8beb86e77fd710c1f43913a490ed083aZbigniew Jędrzejewski-Szmek return -ENOMEM;
d4fffc4b8beb86e77fd710c1f43913a490ed083aZbigniew Jędrzejewski-Szmek }
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering r = read_one_line_file(p, &v);
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering free(p);
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering
374ec6abf31ada6ca554cc8ea99b282373fac010Lennart Poettering if (r < 0)
374ec6abf31ada6ca554cc8ea99b282373fac010Lennart Poettering continue;
374ec6abf31ada6ca554cc8ea99b282373fac010Lennart Poettering
374ec6abf31ada6ca554cc8ea99b282373fac010Lennart Poettering r = parse_boolean(v);
cfeaa44a09756a93a881f786678973d9b1e382dbLennart Poettering free(v);
d7bd3de0654669e65b9642c248c5fa6d1d9a9f61Lennart Poettering
d7bd3de0654669e65b9642c248c5fa6d1d9a9f61Lennart Poettering if (r <= 0)
d7bd3de0654669e65b9642c248c5fa6d1d9a9f61Lennart Poettering continue;
cfeaa44a09756a93a881f786678973d9b1e382dbLennart Poettering
d7bd3de0654669e65b9642c248c5fa6d1d9a9f61Lennart Poettering p = strappend("/dev/", de->d_name);
d4fffc4b8beb86e77fd710c1f43913a490ed083aZbigniew Jędrzejewski-Szmek fd = open(p, flags);
320814811417146cfa1e416f69f1101eed630c36Luke Shumaker free(p);
cfeaa44a09756a93a881f786678973d9b1e382dbLennart Poettering
7027ff61a34a12487712b382a061c654acc3a679Lennart Poettering if (fd >= 0) {
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering closedir(d);
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering return fd;
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering }
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering }
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poetteringfallback:
96cde13ace6406582688028f3df5668a172ba628Zbigniew Jędrzejewski-Szmek if (d)
96cde13ace6406582688028f3df5668a172ba628Zbigniew Jędrzejewski-Szmek closedir(d);
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering
374ec6abf31ada6ca554cc8ea99b282373fac010Lennart Poettering fd = open("/dev/rtc0", flags);
374ec6abf31ada6ca554cc8ea99b282373fac010Lennart Poettering if (fd < 0)
cfeaa44a09756a93a881f786678973d9b1e382dbLennart Poettering return -errno;
cfeaa44a09756a93a881f786678973d9b1e382dbLennart Poettering
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering return fd;
7027ff61a34a12487712b382a061c654acc3a679Lennart Poettering}
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poetteringint hwclock_get_time(struct tm *tm) {
7027ff61a34a12487712b382a061c654acc3a679Lennart Poettering int fd;
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering int err = 0;
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering assert(tm);
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering fd = rtc_open(O_RDONLY|O_CLOEXEC);
374ec6abf31ada6ca554cc8ea99b282373fac010Lennart Poettering if (fd < 0)
374ec6abf31ada6ca554cc8ea99b282373fac010Lennart Poettering return -errno;
cfeaa44a09756a93a881f786678973d9b1e382dbLennart Poettering
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering /* This leaves the timezone fields of struct tm
7027ff61a34a12487712b382a061c654acc3a679Lennart Poettering * uninitialized! */
8b0849e9710d721c5d0b775aaf0fd662eefa1449Lennart Poettering if (ioctl(fd, RTC_RD_TIME, tm) < 0)
8b0849e9710d721c5d0b775aaf0fd662eefa1449Lennart Poettering err = -errno;
8b0849e9710d721c5d0b775aaf0fd662eefa1449Lennart Poettering
8b0849e9710d721c5d0b775aaf0fd662eefa1449Lennart Poettering /* We don't know daylight saving, so we reset this in order not
8b0849e9710d721c5d0b775aaf0fd662eefa1449Lennart Poettering * to confused mktime(). */
8b0849e9710d721c5d0b775aaf0fd662eefa1449Lennart Poettering tm->tm_isdst = -1;
8b0849e9710d721c5d0b775aaf0fd662eefa1449Lennart Poettering
8b0849e9710d721c5d0b775aaf0fd662eefa1449Lennart Poettering close_nointr_nofail(fd);
8b0849e9710d721c5d0b775aaf0fd662eefa1449Lennart Poettering
8b0849e9710d721c5d0b775aaf0fd662eefa1449Lennart Poettering return err;
8b0849e9710d721c5d0b775aaf0fd662eefa1449Lennart Poettering}
8b0849e9710d721c5d0b775aaf0fd662eefa1449Lennart Poettering
8b0849e9710d721c5d0b775aaf0fd662eefa1449Lennart Poetteringint hwclock_set_time(const struct tm *tm) {
8b0849e9710d721c5d0b775aaf0fd662eefa1449Lennart Poettering int fd;
8b0849e9710d721c5d0b775aaf0fd662eefa1449Lennart Poettering int err = 0;
8b0849e9710d721c5d0b775aaf0fd662eefa1449Lennart Poettering
8b0849e9710d721c5d0b775aaf0fd662eefa1449Lennart Poettering assert(tm);
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering fd = rtc_open(O_RDONLY|O_CLOEXEC);
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering if (fd < 0)
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering return -errno;
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering
7027ff61a34a12487712b382a061c654acc3a679Lennart Poettering if (ioctl(fd, RTC_SET_TIME, tm) < 0)
7027ff61a34a12487712b382a061c654acc3a679Lennart Poettering err = -errno;
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering close_nointr_nofail(fd);
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering return err;
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering}
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poetteringint hwclock_is_localtime(void) {
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering FILE *f;
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering bool local = false;
e9174f29c7e3ee45137537b126458718913a3ec5Lennart Poettering
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering /*
fed1e721fd0c81e60c77120539f34e16c2585634Lennart Poettering * The third line of adjtime is "UTC" or "LOCAL" or nothing.
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering * # /etc/adjtime
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering * 0.0 0 0
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering * 0
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering * UTC
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering */
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering f = fopen("/etc/adjtime", "re");
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering if (f) {
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering char line[LINE_MAX];
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering bool b;
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering b = fgets(line, sizeof(line), f) &&
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering fgets(line, sizeof(line), f) &&
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering fgets(line, sizeof(line), f);
e9174f29c7e3ee45137537b126458718913a3ec5Lennart Poettering
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering fclose(f);
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering if (!b)
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering return -EIO;
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering
1021b21bc6f8dd522b46116e8598b17f9f93f1b7Lennart Poettering truncate_nl(line);
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering local = streq(line, "LOCAL");
de0671ee7fe465e108f62dcbbbe9366f81dd9e9aZbigniew Jędrzejewski-Szmek
de0671ee7fe465e108f62dcbbbe9366f81dd9e9aZbigniew Jędrzejewski-Szmek } else if (errno != -ENOENT)
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering return -errno;
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering
de0671ee7fe465e108f62dcbbbe9366f81dd9e9aZbigniew Jędrzejewski-Szmek return local;
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering}
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poetteringint hwclock_set_timezone(int *min) {
1021b21bc6f8dd522b46116e8598b17f9f93f1b7Lennart Poettering const struct timeval *tv_null = NULL;
1021b21bc6f8dd522b46116e8598b17f9f93f1b7Lennart Poettering struct timespec ts;
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering struct tm *tm;
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering int minuteswest;
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering struct timezone tz;
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering assert_se(tm = localtime(&ts.tv_sec));
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering minuteswest = tm->tm_gmtoff / 60;
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering tz.tz_minuteswest = -minuteswest;
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering tz.tz_dsttime = 0; /* DST_NONE*/
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering /*
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering * If the hardware clock does not run in UTC, but in local time:
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering * The very first time we set the kernel's timezone, it will warp
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering * the clock so that it runs in UTC instead of local time.
a0ab566574303be1ca12cdb334f284cfd407caa5Lennart Poettering */
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering if (settimeofday(tv_null, &tz) < 0)
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering return -errno;
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering if (min)
e13bb5d2b133f9ae51c0a2d20aa51071c780e9aeKay Sievers *min = minuteswest;
e13bb5d2b133f9ae51c0a2d20aa51071c780e9aeKay Sievers return 0;
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering}
a0ab566574303be1ca12cdb334f284cfd407caa5Lennart Poettering
a0ab566574303be1ca12cdb334f284cfd407caa5Lennart Poetteringint hwclock_reset_timezone(void) {
a0ab566574303be1ca12cdb334f284cfd407caa5Lennart Poettering const struct timeval *tv_null = NULL;
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering struct timezone tz;
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering
78edb35ab4f4227485cb9ec816b43c37e0d5e62aLennart Poettering tz.tz_minuteswest = 0;
78edb35ab4f4227485cb9ec816b43c37e0d5e62aLennart Poettering tz.tz_dsttime = 0; /* DST_NONE*/
78edb35ab4f4227485cb9ec816b43c37e0d5e62aLennart Poettering
78edb35ab4f4227485cb9ec816b43c37e0d5e62aLennart Poettering /*
78edb35ab4f4227485cb9ec816b43c37e0d5e62aLennart Poettering * The very first time we set the kernel's timezone, it will warp
78edb35ab4f4227485cb9ec816b43c37e0d5e62aLennart Poettering * the clock. Do a dummy call here, so the time warping is sealed
78edb35ab4f4227485cb9ec816b43c37e0d5e62aLennart Poettering * and we set only the time zone with next call.
78edb35ab4f4227485cb9ec816b43c37e0d5e62aLennart Poettering */
78edb35ab4f4227485cb9ec816b43c37e0d5e62aLennart Poettering if (settimeofday(tv_null, &tz) < 0)
78edb35ab4f4227485cb9ec816b43c37e0d5e62aLennart Poettering return -errno;
78edb35ab4f4227485cb9ec816b43c37e0d5e62aLennart Poettering
78edb35ab4f4227485cb9ec816b43c37e0d5e62aLennart Poettering return 0;
78edb35ab4f4227485cb9ec816b43c37e0d5e62aLennart Poettering}
a016b9228f338cb9b380ce7e00826ef462767d98Lennart Poettering