#include <dlfcn.h>
#include <sys/wait.h>
#include <sys/capability.h>
+#include <sys/time.h>
+#include <linux/rtc.h>
#include "macro.h"
#include "util.h"
int chvt(int vt) {
int fd, r = 0;
- if ((fd = open("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC)) < 0)
+ if ((fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC)) < 0)
return -errno;
if (vt < 0) {
int vt_disallocate(const char *name) {
int fd, r;
unsigned u;
- int temporary_vt, temporary_fd;
- char tpath[64];
- struct vt_stat vt_stat;
/* Deallocate the VT if possible. If not possible
* (i.e. because it is the active one), at least clear it
* entirely (including the scrollback buffer) */
- if (!tty_is_vc(name))
- return -EIO;
+ if (!startswith(name, "/dev/"))
+ return -EINVAL;
+
+ if (!tty_is_vc(name)) {
+ /* So this is not a VT. I guess we cannot deallocate
+ * it then. But let's at least clear the screen */
+
+ fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ loop_write(fd, "\033[H\033[2J", 7, false); /* clear screen */
+ close_nointr_nofail(fd);
+
+ return 0;
+ }
if (!startswith(name, "/dev/tty"))
return -EINVAL;
return r;
if (u <= 0)
- return -EIO;
+ return -EINVAL;
+ /* Try to deallocate */
fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
if (fd < 0)
return fd;
r = ioctl(fd, VT_DISALLOCATE, u);
- if (r >= 0) {
- close_nointr_nofail(fd);
- return 0;
- }
-
- if (errno != EBUSY) {
- close_nointr_nofail(fd);
- return -errno;
- }
-
- if (ioctl(fd, VT_GETSTATE, &vt_stat) < 0) {
- close_nointr_nofail(fd);
- return -errno;
- }
+ close_nointr_nofail(fd);
- if (u != vt_stat.v_active) {
- close_nointr_nofail(fd);
- return -EBUSY;
- }
+ if (r >= 0)
+ return 0;
- if (ioctl(fd, VT_OPENQRY, &temporary_vt) < 0) {
- close_nointr_nofail(fd);
+ if (errno != EBUSY)
return -errno;
- }
- if (temporary_vt <= 0) {
- close_nointr_nofail(fd);
- return -EIO;
- }
-
- /* Switch to temporary VT */
- snprintf(tpath, sizeof(tpath), "/dev/tty%i", temporary_vt);
- char_array_0(tpath);
- temporary_fd = open_terminal(tpath, O_RDWR|O_NOCTTY|O_CLOEXEC);
- ioctl(fd, VT_ACTIVATE, temporary_vt);
- if (temporary_fd >= 0)
- close_nointr_nofail(temporary_fd);
-
- /* Reopen /dev/tty0 */
- close_nointr_nofail(fd);
- fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
+ /* Couldn't deallocate, so let's clear it fully with
+ * scrollback */
+ fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
if (fd < 0)
- r = -errno;
- else {
- /* Disallocate the real VT */
- if (ioctl(fd, VT_DISALLOCATE, u) < 0)
- r = -errno;
- else
- r = 0;
- }
-
- /* Recreate original VT */
- temporary_fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
-
- if (temporary_fd >= 0) {
- loop_write(temporary_fd, "\033[H\033[2J", 7, false); /* clear screen explicitly */
- close_nointr_nofail(temporary_fd);
- }
+ return fd;
- /* Switch back to original VT */
- if (fd >= 0) {
- ioctl(fd, VT_ACTIVATE, vt_stat.v_active);
- close_nointr_nofail(fd);
- }
+ /* Requires Linux 2.6.40 */
+ loop_write(fd, "\033[H\033[3J", 7, false); /* clear screen including scrollback */
+ close_nointr_nofail(fd);
- return r;
+ return 0;
}
static const char *const ioprio_class_table[] = {
*strv = files;
return r;
}
+
+bool hwclock_is_localtime(void) {
+ FILE *f;
+ char line[LINE_MAX];
+ bool local = false;
+
+ /*
+ * The third line of adjtime is "UTC" or "LOCAL" or nothing.
+ * # /etc/adjtime
+ * 0.0 0 0.0
+ * 0
+ * UTC
+ */
+ f = fopen("/etc/adjtime", "re");
+ if (f) {
+ if (fgets(line, sizeof(line), f) &&
+ fgets(line, sizeof(line), f) &&
+ fgets(line, sizeof(line), f) ) {
+ if (!strcmp(line, "LOCAL\n"))
+ local = true;
+ }
+ fclose(f);
+ }
+ return local;
+}
+
+int hwclock_apply_localtime_delta(void) {
+ const struct timeval *tv_null = NULL;
+ struct timeval tv;
+ struct tm *tm;
+ int minuteswest;
+ struct timezone tz;
+
+ gettimeofday(&tv, NULL);
+ tm = localtime(&tv.tv_sec);
+ minuteswest = tm->tm_gmtoff / 60;
+
+ tz.tz_minuteswest = -minuteswest;
+ 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;
+ else
+ return minuteswest;
+}
+
+int hwclock_get_time(struct tm *tm) {
+ int fd;
+ int err = 0;
+
+ fd = open("/dev/rtc0", O_RDONLY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+ if (ioctl(fd, RTC_RD_TIME, tm) < 0)
+ err = -errno;
+ close(fd);
+
+ return err;
+}
+
+int hwclock_set_time(const struct tm *tm) {
+ int fd;
+ int err = 0;
+
+ fd = open("/dev/rtc0", O_RDONLY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+ if (ioctl(fd, RTC_SET_TIME, tm) < 0)
+ err = -errno;
+ close(fd);
+
+ return err;
+}