blob: 204a103c3084a9de1c3ac1302e4f87d03f7a7c97 [file] [log] [blame]
Robert Griebl1cd04452002-07-21 16:50:49 +00001/* vi: set sw=4 ts=4: */
2/*
Robert Griebl6859d762002-08-05 02:57:12 +00003 * Mini hwclock implementation for busybox
4 *
Robert Griebl1cd04452002-07-21 16:50:49 +00005 * Copyright (C) 2002 Robert Griebl <griebl@gmx.de>
6 *
Bernhard Reutner-Fischer5cf905a2006-03-31 22:36:15 +00007 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
Robert Griebl6859d762002-08-05 02:57:12 +00008*/
Robert Griebl1cd04452002-07-21 16:50:49 +00009
Robert Griebl1cd04452002-07-21 16:50:49 +000010#include <sys/utsname.h>
Glenn L McGrath689e4b92004-02-22 09:11:33 +000011#include <getopt.h>
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000012#include "libbb.h"
Robert Griebl1cd04452002-07-21 16:50:49 +000013
Eric Andersenaff114c2004-04-14 17:51:38 +000014/* Copied from linux/rtc.h to eliminate the kernel dependency */
Robert Griebl7ce75f42003-01-02 07:16:53 +000015struct linux_rtc_time {
16 int tm_sec;
17 int tm_min;
18 int tm_hour;
19 int tm_mday;
20 int tm_mon;
21 int tm_year;
22 int tm_wday;
23 int tm_yday;
24 int tm_isdst;
25};
Mike Frysingerb31566e2005-04-16 04:48:48 +000026
Robert Griebl7ce75f42003-01-02 07:16:53 +000027#define RTC_SET_TIME _IOW('p', 0x0a, struct linux_rtc_time) /* Set RTC time */
28#define RTC_RD_TIME _IOR('p', 0x09, struct linux_rtc_time) /* Read RTC time */
Eric Andersen8882ea52002-12-11 03:41:28 +000029
Bernhard Reutner-Fischer01d23ad2006-05-26 20:19:22 +000030#if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS
Robert Griebl1cd04452002-07-21 16:50:49 +000031# ifndef _GNU_SOURCE
32# define _GNU_SOURCE
33# endif
34#endif
35
Denis Vlasenko673d4bb2007-03-07 23:02:50 +000036static const char *rtcname;
37
Denis Vlasenko92258542006-11-01 10:25:35 +000038static int xopen_rtc(int flags)
Robert Griebl1cd04452002-07-21 16:50:49 +000039{
40 int rtc;
Denis Vlasenko673d4bb2007-03-07 23:02:50 +000041
42 if (!rtcname) {
43 rtc = open("/dev/rtc", flags);
44 if (rtc >= 0)
45 return rtc;
46 rtc = open("/dev/rtc0", flags);
47 if (rtc >= 0)
48 return rtc;
49 rtcname = "/dev/misc/rtc";
Denis Vlasenko92258542006-11-01 10:25:35 +000050 }
Denis Vlasenko673d4bb2007-03-07 23:02:50 +000051 return xopen(rtcname, flags);
Denis Vlasenko92258542006-11-01 10:25:35 +000052}
53
54static time_t read_rtc(int utc)
55{
Robert Griebl1cd04452002-07-21 16:50:49 +000056 struct tm tm;
57 char *oldtz = 0;
58 time_t t = 0;
Denis Vlasenko92258542006-11-01 10:25:35 +000059 int rtc = xopen_rtc(O_RDONLY);
Robert Griebl1cd04452002-07-21 16:50:49 +000060
Denis Vlasenko92258542006-11-01 10:25:35 +000061 memset(&tm, 0, sizeof(struct tm));
Denis Vlasenko219d14d2007-03-24 15:40:16 +000062 if (ioctl(rtc, RTC_RD_TIME, &tm) < 0)
Denis Vlasenko92258542006-11-01 10:25:35 +000063 bb_perror_msg_and_die("cannot read time from RTC");
Bernhard Reutner-Fischer5cf905a2006-03-31 22:36:15 +000064 tm.tm_isdst = -1; /* not known */
Eric Andersenc7bda1c2004-03-15 08:29:22 +000065
Denis Vlasenko92258542006-11-01 10:25:35 +000066 close(rtc);
Robert Griebl1cd04452002-07-21 16:50:49 +000067
Denis Vlasenko92258542006-11-01 10:25:35 +000068 if (utc) {
69 oldtz = getenv("TZ");
70 setenv("TZ", "UTC 0", 1);
71 tzset();
Robert Griebl1cd04452002-07-21 16:50:49 +000072 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +000073
Denis Vlasenko92258542006-11-01 10:25:35 +000074 t = mktime(&tm);
Eric Andersenc7bda1c2004-03-15 08:29:22 +000075
Denis Vlasenko92258542006-11-01 10:25:35 +000076 if (utc) {
77 if (oldtz)
78 setenv("TZ", oldtz, 1);
Robert Griebl1cd04452002-07-21 16:50:49 +000079 else
Denis Vlasenko92258542006-11-01 10:25:35 +000080 unsetenv("TZ");
81 tzset();
Robert Griebl1cd04452002-07-21 16:50:49 +000082 }
83 return t;
84}
85
Glenn L McGrath689e4b92004-02-22 09:11:33 +000086static void write_rtc(time_t t, int utc)
Robert Griebl1cd04452002-07-21 16:50:49 +000087{
Robert Griebl1cd04452002-07-21 16:50:49 +000088 struct tm tm;
Denis Vlasenko92258542006-11-01 10:25:35 +000089 int rtc = xopen_rtc(O_WRONLY);
Robert Griebl1cd04452002-07-21 16:50:49 +000090
Denis Vlasenko92258542006-11-01 10:25:35 +000091 tm = *(utc ? gmtime(&t) : localtime(&t));
Bernhard Reutner-Fischer5cf905a2006-03-31 22:36:15 +000092 tm.tm_isdst = 0;
Eric Andersenc7bda1c2004-03-15 08:29:22 +000093
Denis Vlasenko92258542006-11-01 10:25:35 +000094 if (ioctl(rtc, RTC_SET_TIME, &tm) < 0)
95 bb_perror_msg_and_die("cannot set the RTC time");
Eric Andersenc7bda1c2004-03-15 08:29:22 +000096
Denis Vlasenko92258542006-11-01 10:25:35 +000097 close(rtc);
Robert Griebl1cd04452002-07-21 16:50:49 +000098}
99
Denis Vlasenko459be352007-06-17 19:09:05 +0000100static void show_clock(int utc)
Robert Griebl1cd04452002-07-21 16:50:49 +0000101{
Denis Vlasenko459be352007-06-17 19:09:05 +0000102 //struct tm *ptm;
Robert Griebl1cd04452002-07-21 16:50:49 +0000103 time_t t;
Denis Vlasenko459be352007-06-17 19:09:05 +0000104 char *cp;
Robert Griebl1cd04452002-07-21 16:50:49 +0000105
Denis Vlasenko92258542006-11-01 10:25:35 +0000106 t = read_rtc(utc);
Denis Vlasenko459be352007-06-17 19:09:05 +0000107 //ptm = localtime(&t); /* Sets 'tzname[]' */
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000108
Denis Vlasenko459be352007-06-17 19:09:05 +0000109 cp = ctime(&t);
110 if (cp[0])
111 cp[strlen(cp) - 1] = '\0';
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000112
Denis Vlasenko459be352007-06-17 19:09:05 +0000113 //printf("%s %.6f seconds %s\n", cp, 0.0, utc ? "" : (ptm->tm_isdst ? tzname[1] : tzname[0]));
114 printf("%s 0.000000 seconds\n", cp);
Robert Griebl1cd04452002-07-21 16:50:49 +0000115}
116
Denis Vlasenko459be352007-06-17 19:09:05 +0000117static void to_sys_clock(int utc)
Robert Griebl1cd04452002-07-21 16:50:49 +0000118{
Denis Vlasenko459be352007-06-17 19:09:05 +0000119 struct timeval tv;
Robert Griebl1cd04452002-07-21 16:50:49 +0000120 const struct timezone tz = { timezone/60 - 60*daylight, 0 };
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000121
Denis Vlasenko92258542006-11-01 10:25:35 +0000122 tv.tv_sec = read_rtc(utc);
Denis Vlasenko459be352007-06-17 19:09:05 +0000123 tv.tv_usec = 0;
Denis Vlasenko92258542006-11-01 10:25:35 +0000124 if (settimeofday(&tv, &tz))
125 bb_perror_msg_and_die("settimeofday() failed");
Robert Griebl1cd04452002-07-21 16:50:49 +0000126}
127
Denis Vlasenko459be352007-06-17 19:09:05 +0000128static void from_sys_clock(int utc)
Robert Griebl1cd04452002-07-21 16:50:49 +0000129{
Denis Vlasenko459be352007-06-17 19:09:05 +0000130 struct timeval tv;
Robert Griebl1cd04452002-07-21 16:50:49 +0000131
Denis Vlasenko459be352007-06-17 19:09:05 +0000132 gettimeofday(&tv, NULL);
133 //if (gettimeofday(&tv, NULL))
134 // bb_perror_msg_and_die("gettimeofday() failed");
Denis Vlasenko92258542006-11-01 10:25:35 +0000135 write_rtc(tv.tv_sec, utc);
Robert Griebl1cd04452002-07-21 16:50:49 +0000136}
137
Mike Frysinger747fc5d2005-09-28 03:21:21 +0000138#ifdef CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS
139# define ADJTIME_PATH "/var/lib/hwclock/adjtime"
140#else
141# define ADJTIME_PATH "/etc/adjtime"
142#endif
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000143static int check_utc(void)
Robert Griebl1cd04452002-07-21 16:50:49 +0000144{
145 int utc = 0;
Denis Vlasenko92258542006-11-01 10:25:35 +0000146 FILE *f = fopen(ADJTIME_PATH, "r");
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000147
Denis Vlasenko92258542006-11-01 10:25:35 +0000148 if (f) {
Bernhard Reutner-Fischer5cf905a2006-03-31 22:36:15 +0000149 RESERVE_CONFIG_BUFFER(buffer, 128);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000150
Denis Vlasenko92258542006-11-01 10:25:35 +0000151 while (fgets(buffer, sizeof(buffer), f)) {
152 int len = strlen(buffer);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000153
Denis Vlasenko92258542006-11-01 10:25:35 +0000154 while (len && isspace(buffer[len - 1]))
Robert Griebl1cd04452002-07-21 16:50:49 +0000155 len--;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000156
Denis Vlasenko92258542006-11-01 10:25:35 +0000157 buffer[len] = 0;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000158
Denis Vlasenko219d14d2007-03-24 15:40:16 +0000159 if (strncmp(buffer, "UTC", 3) == 0) {
Robert Griebl1cd04452002-07-21 16:50:49 +0000160 utc = 1;
161 break;
162 }
163 }
Denis Vlasenko92258542006-11-01 10:25:35 +0000164 fclose(f);
Bernhard Reutner-Fischer5cf905a2006-03-31 22:36:15 +0000165 RELEASE_CONFIG_BUFFER(buffer);
Robert Griebl1cd04452002-07-21 16:50:49 +0000166 }
167 return utc;
168}
169
Denis Vlasenko92258542006-11-01 10:25:35 +0000170#define HWCLOCK_OPT_LOCALTIME 0x01
171#define HWCLOCK_OPT_UTC 0x02
172#define HWCLOCK_OPT_SHOW 0x04
173#define HWCLOCK_OPT_HCTOSYS 0x08
174#define HWCLOCK_OPT_SYSTOHC 0x10
Denis Vlasenko673d4bb2007-03-07 23:02:50 +0000175#define HWCLOCK_OPT_RTCFILE 0x20
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000176
Denis Vlasenko459be352007-06-17 19:09:05 +0000177int hwclock_main(int argc, char **argv);
178int hwclock_main(int argc, char **argv)
Robert Griebl1cd04452002-07-21 16:50:49 +0000179{
Denis Vlasenko67b23e62006-10-03 21:00:06 +0000180 unsigned opt;
Robert Griebl6bb80872004-03-22 21:27:39 +0000181 int utc;
Robert Griebl1cd04452002-07-21 16:50:49 +0000182
Bernhard Reutner-Fischer01d23ad2006-05-26 20:19:22 +0000183#if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS
Denis Vlasenko92258542006-11-01 10:25:35 +0000184 static const struct option hwclock_long_options[] = {
Robert Griebl1cd04452002-07-21 16:50:49 +0000185 { "localtime", 0, 0, 'l' },
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000186 { "utc", 0, 0, 'u' },
187 { "show", 0, 0, 'r' },
Robert Griebl1cd04452002-07-21 16:50:49 +0000188 { "hctosys", 0, 0, 's' },
189 { "systohc", 0, 0, 'w' },
Denis Vlasenko673d4bb2007-03-07 23:02:50 +0000190 { "file", 1, 0, 'f' },
Robert Griebl1cd04452002-07-21 16:50:49 +0000191 { 0, 0, 0, 0 }
192 };
Denis Vlasenko67b23e62006-10-03 21:00:06 +0000193 applet_long_options = hwclock_long_options;
Robert Griebl1cd04452002-07-21 16:50:49 +0000194#endif
Denis Vlasenko67b23e62006-10-03 21:00:06 +0000195 opt_complementary = "?:r--ws:w--rs:s--wr:l--u:u--l";
Denis Vlasenko673d4bb2007-03-07 23:02:50 +0000196 opt = getopt32(argc, argv, "lurswf:", &rtcname);
Robert Griebl1cd04452002-07-21 16:50:49 +0000197
Robert Griebl6bb80872004-03-22 21:27:39 +0000198 /* If -u or -l wasn't given check if we are using utc */
Mike Frysingerb31566e2005-04-16 04:48:48 +0000199 if (opt & (HWCLOCK_OPT_UTC | HWCLOCK_OPT_LOCALTIME))
Robert Griebl6bb80872004-03-22 21:27:39 +0000200 utc = opt & HWCLOCK_OPT_UTC;
201 else
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000202 utc = check_utc();
Mike Frysingerb31566e2005-04-16 04:48:48 +0000203
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000204 if (opt & HWCLOCK_OPT_HCTOSYS) {
Denis Vlasenko459be352007-06-17 19:09:05 +0000205 to_sys_clock(utc);
206 return 0;
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000207 }
Denis Vlasenko459be352007-06-17 19:09:05 +0000208 if (opt & HWCLOCK_OPT_SYSTOHC) {
209 from_sys_clock(utc);
210 return 0;
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000211 }
Denis Vlasenko459be352007-06-17 19:09:05 +0000212 /* default HWCLOCK_OPT_SHOW */
213 show_clock(utc);
214 return 0;
Robert Griebl1cd04452002-07-21 16:50:49 +0000215}