1
0
mirror of https://github.com/systemd/systemd.git synced 2024-11-14 15:21:37 +03:00
systemd/src/shared/hwclock.c
Kay Sievers 2b3c81b02f do not create /dev/rtc symlink, let systemd search for it if needed
The export of the RTCs hctosys flag is uneccesary, the kernel takes care
of the persistemt clock management itself, without any need for:
  CONFIG_RTC_HCTOSYS=y
  CONFIG_RTC_HCTOSYS_DEVICE="rtc0"

"Chaotic hardware platforms" without native kernel persistent clock
support will find the proper RTC with the logic rtc_open() without
the need for a custom symlink.
2013-04-24 19:20:51 +02:00

237 lines
6.0 KiB
C

/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010-2012 Lennart Poettering
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 <assert.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdarg.h>
#include <ctype.h>
#include <sys/prctl.h>
#include <sys/time.h>
#include <linux/rtc.h>
#include "macro.h"
#include "util.h"
#include "log.h"
#include "strv.h"
#include "hwclock.h"
#include "fileio.h"
static int rtc_open(int flags) {
int fd;
DIR *d;
/*
* Some "chaotic platforms" have multiple RTCs and we need to
* find the "system RTC", which is in some setups /dev/rtc1.
*
* First, we try to find the RTC which has hctosys=1 set. If we
* don't find any we just take the first RTC that exists at all,
* then try to open /dev/rtc0.
*/
d = opendir("/sys/class/rtc");
if (!d)
goto fallback;
for (;;) {
char *p, *v;
struct dirent *de;
union dirent_storage buf;
int r;
r = readdir_r(d, &buf.de, &de);
if (r != 0)
goto fallback;
if (!de)
goto fallback;
if (ignore_file(de->d_name))
continue;
p = strjoin("/sys/class/rtc/", de->d_name, "/hctosys", NULL);
if (!p) {
closedir(d);
return -ENOMEM;
}
r = read_one_line_file(p, &v);
free(p);
if (r < 0)
continue;
r = parse_boolean(v);
free(v);
if (r <= 0)
continue;
p = strappend("/dev/", de->d_name);
if (!p) {
closedir(d);
return -ENOMEM;
}
fd = open(p, flags);
free(p);
if (fd >= 0) {
closedir(d);
return fd;
}
}
fallback:
if (d)
closedir(d);
fd = open("/dev/rtc0", flags);
if (fd < 0)
return -errno;
return fd;
}
int hwclock_get_time(struct tm *tm) {
int fd;
int err = 0;
assert(tm);
fd = rtc_open(O_RDONLY|O_CLOEXEC);
if (fd < 0)
return -errno;
/* This leaves the timezone fields of struct tm
* uninitialized! */
if (ioctl(fd, RTC_RD_TIME, tm) < 0)
err = -errno;
/* We don't know daylight saving, so we reset this in order not
* to confused mktime(). */
tm->tm_isdst = -1;
close_nointr_nofail(fd);
return err;
}
int hwclock_set_time(const struct tm *tm) {
int fd;
int err = 0;
assert(tm);
fd = rtc_open(O_RDONLY|O_CLOEXEC);
if (fd < 0)
return -errno;
if (ioctl(fd, RTC_SET_TIME, tm) < 0)
err = -errno;
close_nointr_nofail(fd);
return err;
}
int hwclock_is_localtime(void) {
_cleanup_fclose_ FILE *f;
/*
* The third line of adjtime is "UTC" or "LOCAL" or nothing.
* # /etc/adjtime
* 0.0 0 0
* 0
* UTC
*/
f = fopen("/etc/adjtime", "re");
if (f) {
char line[LINE_MAX];
bool b;
b = fgets(line, sizeof(line), f) &&
fgets(line, sizeof(line), f) &&
fgets(line, sizeof(line), f);
if (!b)
return -EIO;
truncate_nl(line);
return streq(line, "LOCAL");
} else if (errno != ENOENT)
return -errno;
return 0;
}
int hwclock_set_timezone(int *min) {
const struct timeval *tv_null = NULL;
struct timespec ts;
struct tm *tm;
int minutesdelta;
struct timezone tz;
assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
assert_se(tm = localtime(&ts.tv_sec));
minutesdelta = tm->tm_gmtoff / 60;
tz.tz_minuteswest = -minutesdelta;
tz.tz_dsttime = 0; /* DST_NONE*/
/*
* If the hardware clock does not run in UTC, but in local time:
* The very first time we set the kernel's timezone, it will warp
* the clock so that it runs in UTC instead of local time.
*/
if (settimeofday(tv_null, &tz) < 0)
return -errno;
if (min)
*min = minutesdelta;
return 0;
}
int hwclock_reset_timezone(void) {
const struct timeval *tv_null = NULL;
struct timezone tz;
tz.tz_minuteswest = 0;
tz.tz_dsttime = 0; /* DST_NONE*/
/*
* The very first time we set the kernel's timezone, it will warp
* the clock. Do a dummy call here, so the time warping is sealed
* and we set only the time zone with next call.
*/
if (settimeofday(tv_null, &tz) < 0)
return -errno;
return 0;
}