blob: 867721482848de8b931c31b0a8f1fd95f171b84b [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 Vlasenkofb79a2e2007-07-14 22:07:14 +000062 xioctl(rtc, RTC_RD_TIME, &tm);
Bernhard Reutner-Fischer5cf905a2006-03-31 22:36:15 +000063 tm.tm_isdst = -1; /* not known */
Eric Andersenc7bda1c2004-03-15 08:29:22 +000064
Denis Vlasenko92258542006-11-01 10:25:35 +000065 close(rtc);
Robert Griebl1cd04452002-07-21 16:50:49 +000066
Denis Vlasenko92258542006-11-01 10:25:35 +000067 if (utc) {
68 oldtz = getenv("TZ");
Denis Vlasenkofb79a2e2007-07-14 22:07:14 +000069 putenv((char*)"TZ=UTC0");
Denis Vlasenko92258542006-11-01 10:25:35 +000070 tzset();
Robert Griebl1cd04452002-07-21 16:50:49 +000071 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +000072
Denis Vlasenko92258542006-11-01 10:25:35 +000073 t = mktime(&tm);
Eric Andersenc7bda1c2004-03-15 08:29:22 +000074
Denis Vlasenko92258542006-11-01 10:25:35 +000075 if (utc) {
Denis Vlasenkofb79a2e2007-07-14 22:07:14 +000076 unsetenv("TZ");
Denis Vlasenko92258542006-11-01 10:25:35 +000077 if (oldtz)
Denis Vlasenkofb79a2e2007-07-14 22:07:14 +000078 putenv(oldtz - 3);
Denis Vlasenko92258542006-11-01 10:25:35 +000079 tzset();
Robert Griebl1cd04452002-07-21 16:50:49 +000080 }
81 return t;
82}
83
Glenn L McGrath689e4b92004-02-22 09:11:33 +000084static void write_rtc(time_t t, int utc)
Robert Griebl1cd04452002-07-21 16:50:49 +000085{
Robert Griebl1cd04452002-07-21 16:50:49 +000086 struct tm tm;
Denis Vlasenko92258542006-11-01 10:25:35 +000087 int rtc = xopen_rtc(O_WRONLY);
Robert Griebl1cd04452002-07-21 16:50:49 +000088
Denis Vlasenko92258542006-11-01 10:25:35 +000089 tm = *(utc ? gmtime(&t) : localtime(&t));
Bernhard Reutner-Fischer5cf905a2006-03-31 22:36:15 +000090 tm.tm_isdst = 0;
Eric Andersenc7bda1c2004-03-15 08:29:22 +000091
Denis Vlasenkofb79a2e2007-07-14 22:07:14 +000092 xioctl(rtc, RTC_SET_TIME, &tm);
Eric Andersenc7bda1c2004-03-15 08:29:22 +000093
Denis Vlasenko92258542006-11-01 10:25:35 +000094 close(rtc);
Robert Griebl1cd04452002-07-21 16:50:49 +000095}
96
Denis Vlasenko459be352007-06-17 19:09:05 +000097static void show_clock(int utc)
Robert Griebl1cd04452002-07-21 16:50:49 +000098{
Denis Vlasenko459be352007-06-17 19:09:05 +000099 //struct tm *ptm;
Robert Griebl1cd04452002-07-21 16:50:49 +0000100 time_t t;
Denis Vlasenko459be352007-06-17 19:09:05 +0000101 char *cp;
Robert Griebl1cd04452002-07-21 16:50:49 +0000102
Denis Vlasenko92258542006-11-01 10:25:35 +0000103 t = read_rtc(utc);
Denis Vlasenko459be352007-06-17 19:09:05 +0000104 //ptm = localtime(&t); /* Sets 'tzname[]' */
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000105
Denis Vlasenko459be352007-06-17 19:09:05 +0000106 cp = ctime(&t);
107 if (cp[0])
108 cp[strlen(cp) - 1] = '\0';
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000109
Denis Vlasenko459be352007-06-17 19:09:05 +0000110 //printf("%s %.6f seconds %s\n", cp, 0.0, utc ? "" : (ptm->tm_isdst ? tzname[1] : tzname[0]));
111 printf("%s 0.000000 seconds\n", cp);
Robert Griebl1cd04452002-07-21 16:50:49 +0000112}
113
Denis Vlasenko459be352007-06-17 19:09:05 +0000114static void to_sys_clock(int utc)
Robert Griebl1cd04452002-07-21 16:50:49 +0000115{
Denis Vlasenko459be352007-06-17 19:09:05 +0000116 struct timeval tv;
Robert Griebl1cd04452002-07-21 16:50:49 +0000117 const struct timezone tz = { timezone/60 - 60*daylight, 0 };
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000118
Denis Vlasenko92258542006-11-01 10:25:35 +0000119 tv.tv_sec = read_rtc(utc);
Denis Vlasenko459be352007-06-17 19:09:05 +0000120 tv.tv_usec = 0;
Denis Vlasenko92258542006-11-01 10:25:35 +0000121 if (settimeofday(&tv, &tz))
122 bb_perror_msg_and_die("settimeofday() failed");
Robert Griebl1cd04452002-07-21 16:50:49 +0000123}
124
Denis Vlasenko459be352007-06-17 19:09:05 +0000125static void from_sys_clock(int utc)
Robert Griebl1cd04452002-07-21 16:50:49 +0000126{
Denis Vlasenko459be352007-06-17 19:09:05 +0000127 struct timeval tv;
Robert Griebl1cd04452002-07-21 16:50:49 +0000128
Denis Vlasenko459be352007-06-17 19:09:05 +0000129 gettimeofday(&tv, NULL);
130 //if (gettimeofday(&tv, NULL))
131 // bb_perror_msg_and_die("gettimeofday() failed");
Denis Vlasenko92258542006-11-01 10:25:35 +0000132 write_rtc(tv.tv_sec, utc);
Robert Griebl1cd04452002-07-21 16:50:49 +0000133}
134
Mike Frysinger747fc5d2005-09-28 03:21:21 +0000135#ifdef CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS
136# define ADJTIME_PATH "/var/lib/hwclock/adjtime"
137#else
138# define ADJTIME_PATH "/etc/adjtime"
139#endif
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000140static int check_utc(void)
Robert Griebl1cd04452002-07-21 16:50:49 +0000141{
142 int utc = 0;
Denis Vlasenko92258542006-11-01 10:25:35 +0000143 FILE *f = fopen(ADJTIME_PATH, "r");
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000144
Denis Vlasenko92258542006-11-01 10:25:35 +0000145 if (f) {
Bernhard Reutner-Fischer5cf905a2006-03-31 22:36:15 +0000146 RESERVE_CONFIG_BUFFER(buffer, 128);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000147
Denis Vlasenko92258542006-11-01 10:25:35 +0000148 while (fgets(buffer, sizeof(buffer), f)) {
149 int len = strlen(buffer);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000150
Denis Vlasenko92258542006-11-01 10:25:35 +0000151 while (len && isspace(buffer[len - 1]))
Robert Griebl1cd04452002-07-21 16:50:49 +0000152 len--;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000153
Denis Vlasenko92258542006-11-01 10:25:35 +0000154 buffer[len] = 0;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000155
Denis Vlasenko219d14d2007-03-24 15:40:16 +0000156 if (strncmp(buffer, "UTC", 3) == 0) {
Robert Griebl1cd04452002-07-21 16:50:49 +0000157 utc = 1;
158 break;
159 }
160 }
Denis Vlasenko92258542006-11-01 10:25:35 +0000161 fclose(f);
Bernhard Reutner-Fischer5cf905a2006-03-31 22:36:15 +0000162 RELEASE_CONFIG_BUFFER(buffer);
Robert Griebl1cd04452002-07-21 16:50:49 +0000163 }
164 return utc;
165}
166
Denis Vlasenko92258542006-11-01 10:25:35 +0000167#define HWCLOCK_OPT_LOCALTIME 0x01
168#define HWCLOCK_OPT_UTC 0x02
169#define HWCLOCK_OPT_SHOW 0x04
170#define HWCLOCK_OPT_HCTOSYS 0x08
171#define HWCLOCK_OPT_SYSTOHC 0x10
Denis Vlasenko673d4bb2007-03-07 23:02:50 +0000172#define HWCLOCK_OPT_RTCFILE 0x20
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000173
Denis Vlasenko459be352007-06-17 19:09:05 +0000174int hwclock_main(int argc, char **argv);
175int hwclock_main(int argc, char **argv)
Robert Griebl1cd04452002-07-21 16:50:49 +0000176{
Denis Vlasenko67b23e62006-10-03 21:00:06 +0000177 unsigned opt;
Robert Griebl6bb80872004-03-22 21:27:39 +0000178 int utc;
Robert Griebl1cd04452002-07-21 16:50:49 +0000179
Bernhard Reutner-Fischer01d23ad2006-05-26 20:19:22 +0000180#if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000181 static const char hwclock_longopts[] ALIGN1 =
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +0000182 "localtime\0" No_argument "l"
183 "utc\0" No_argument "u"
184 "show\0" No_argument "r"
185 "hctosys\0" No_argument "s"
186 "systohc\0" No_argument "w"
187 "file\0" Required_argument "f"
Denis Vlasenko990d0f62007-07-24 15:54:42 +0000188 ;
Denis Vlasenkobdc88fd2007-07-23 17:14:14 +0000189 applet_long_options = hwclock_longopts;
Robert Griebl1cd04452002-07-21 16:50:49 +0000190#endif
Denis Vlasenko09196572007-07-21 13:27:44 +0000191 opt_complementary = "r--ws:w--rs:s--wr:l--u:u--l";
Denis Vlasenko673d4bb2007-03-07 23:02:50 +0000192 opt = getopt32(argc, argv, "lurswf:", &rtcname);
Robert Griebl1cd04452002-07-21 16:50:49 +0000193
Robert Griebl6bb80872004-03-22 21:27:39 +0000194 /* If -u or -l wasn't given check if we are using utc */
Mike Frysingerb31566e2005-04-16 04:48:48 +0000195 if (opt & (HWCLOCK_OPT_UTC | HWCLOCK_OPT_LOCALTIME))
Robert Griebl6bb80872004-03-22 21:27:39 +0000196 utc = opt & HWCLOCK_OPT_UTC;
197 else
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000198 utc = check_utc();
Mike Frysingerb31566e2005-04-16 04:48:48 +0000199
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000200 if (opt & HWCLOCK_OPT_HCTOSYS) {
Denis Vlasenko459be352007-06-17 19:09:05 +0000201 to_sys_clock(utc);
202 return 0;
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000203 }
Denis Vlasenko459be352007-06-17 19:09:05 +0000204 if (opt & HWCLOCK_OPT_SYSTOHC) {
205 from_sys_clock(utc);
206 return 0;
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000207 }
Denis Vlasenko459be352007-06-17 19:09:05 +0000208 /* default HWCLOCK_OPT_SHOW */
209 show_clock(utc);
210 return 0;
Robert Griebl1cd04452002-07-21 16:50:49 +0000211}