blob: 8fcd8c99c8fba7ad39d388e60c0b61babadc69e4 [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
10
Glenn L McGrath689e4b92004-02-22 09:11:33 +000011#include <sys/ioctl.h>
Robert Griebl1cd04452002-07-21 16:50:49 +000012#include <sys/utsname.h>
Glenn L McGrath689e4b92004-02-22 09:11:33 +000013#include <getopt.h>
Robert Griebl1cd04452002-07-21 16:50:49 +000014#include "busybox.h"
15
Eric Andersenaff114c2004-04-14 17:51:38 +000016/* Copied from linux/rtc.h to eliminate the kernel dependency */
Robert Griebl7ce75f42003-01-02 07:16:53 +000017struct linux_rtc_time {
18 int tm_sec;
19 int tm_min;
20 int tm_hour;
21 int tm_mday;
22 int tm_mon;
23 int tm_year;
24 int tm_wday;
25 int tm_yday;
26 int tm_isdst;
27};
Mike Frysingerb31566e2005-04-16 04:48:48 +000028
Robert Griebl7ce75f42003-01-02 07:16:53 +000029#define RTC_SET_TIME _IOW('p', 0x0a, struct linux_rtc_time) /* Set RTC time */
30#define RTC_RD_TIME _IOR('p', 0x09, struct linux_rtc_time) /* Read RTC time */
Eric Andersen8882ea52002-12-11 03:41:28 +000031
Bernhard Reutner-Fischer01d23ad2006-05-26 20:19:22 +000032#if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS
Robert Griebl1cd04452002-07-21 16:50:49 +000033# ifndef _GNU_SOURCE
34# define _GNU_SOURCE
35# endif
36#endif
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 Vlasenko92258542006-11-01 10:25:35 +000041 rtc = open("/dev/rtc", flags);
42 if (rtc < 0) {
43 rtc = open("/dev/misc/rtc", flags);
44 if (rtc < 0)
45 bb_perror_msg_and_die("cannot access RTC");
46 }
47 return rtc;
48}
49
50static time_t read_rtc(int utc)
51{
Robert Griebl1cd04452002-07-21 16:50:49 +000052 struct tm tm;
53 char *oldtz = 0;
54 time_t t = 0;
Denis Vlasenko92258542006-11-01 10:25:35 +000055 int rtc = xopen_rtc(O_RDONLY);
Robert Griebl1cd04452002-07-21 16:50:49 +000056
Denis Vlasenko92258542006-11-01 10:25:35 +000057 memset(&tm, 0, sizeof(struct tm));
58 if (ioctl(rtc, RTC_RD_TIME, &tm) < 0 )
59 bb_perror_msg_and_die("cannot read time from RTC");
Bernhard Reutner-Fischer5cf905a2006-03-31 22:36:15 +000060 tm.tm_isdst = -1; /* not known */
Eric Andersenc7bda1c2004-03-15 08:29:22 +000061
Denis Vlasenko92258542006-11-01 10:25:35 +000062 close(rtc);
Robert Griebl1cd04452002-07-21 16:50:49 +000063
Denis Vlasenko92258542006-11-01 10:25:35 +000064 if (utc) {
65 oldtz = getenv("TZ");
66 setenv("TZ", "UTC 0", 1);
67 tzset();
Robert Griebl1cd04452002-07-21 16:50:49 +000068 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +000069
Denis Vlasenko92258542006-11-01 10:25:35 +000070 t = mktime(&tm);
Eric Andersenc7bda1c2004-03-15 08:29:22 +000071
Denis Vlasenko92258542006-11-01 10:25:35 +000072 if (utc) {
73 if (oldtz)
74 setenv("TZ", oldtz, 1);
Robert Griebl1cd04452002-07-21 16:50:49 +000075 else
Denis Vlasenko92258542006-11-01 10:25:35 +000076 unsetenv("TZ");
77 tzset();
Robert Griebl1cd04452002-07-21 16:50:49 +000078 }
79 return t;
80}
81
Glenn L McGrath689e4b92004-02-22 09:11:33 +000082static void write_rtc(time_t t, int utc)
Robert Griebl1cd04452002-07-21 16:50:49 +000083{
Robert Griebl1cd04452002-07-21 16:50:49 +000084 struct tm tm;
Denis Vlasenko92258542006-11-01 10:25:35 +000085 int rtc = xopen_rtc(O_WRONLY);
Robert Griebl1cd04452002-07-21 16:50:49 +000086
Denis Vlasenko92258542006-11-01 10:25:35 +000087 tm = *(utc ? gmtime(&t) : localtime(&t));
Bernhard Reutner-Fischer5cf905a2006-03-31 22:36:15 +000088 tm.tm_isdst = 0;
Eric Andersenc7bda1c2004-03-15 08:29:22 +000089
Denis Vlasenko92258542006-11-01 10:25:35 +000090 if (ioctl(rtc, RTC_SET_TIME, &tm) < 0)
91 bb_perror_msg_and_die("cannot set the RTC time");
Eric Andersenc7bda1c2004-03-15 08:29:22 +000092
Denis Vlasenko92258542006-11-01 10:25:35 +000093 close(rtc);
Robert Griebl1cd04452002-07-21 16:50:49 +000094}
95
Glenn L McGrath689e4b92004-02-22 09:11:33 +000096static int show_clock(int utc)
Robert Griebl1cd04452002-07-21 16:50:49 +000097{
98 struct tm *ptm;
99 time_t t;
Bernhard Reutner-Fischer5cf905a2006-03-31 22:36:15 +0000100 RESERVE_CONFIG_BUFFER(buffer, 64);
Robert Griebl1cd04452002-07-21 16:50:49 +0000101
Denis Vlasenko92258542006-11-01 10:25:35 +0000102 t = read_rtc(utc);
103 ptm = localtime(&t); /* Sets 'tzname[]' */
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000104
Denis Vlasenko92258542006-11-01 10:25:35 +0000105 safe_strncpy(buffer, ctime(&t), 64);
106 if (buffer[0])
107 buffer[strlen(buffer) - 1] = 0;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000108
Denis Vlasenkodd539f72006-11-01 20:20:37 +0000109 //printf("%s %.6f seconds %s\n", buffer, 0.0, utc ? "" : (ptm->tm_isdst ? tzname[1] : tzname[0]));
Denis Vlasenko92258542006-11-01 10:25:35 +0000110 printf( "%s %.6f seconds\n", buffer, 0.0);
Bernhard Reutner-Fischer5cf905a2006-03-31 22:36:15 +0000111 RELEASE_CONFIG_BUFFER(buffer);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000112
Robert Griebl1cd04452002-07-21 16:50:49 +0000113 return 0;
114}
115
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000116static int to_sys_clock(int utc)
Robert Griebl1cd04452002-07-21 16:50:49 +0000117{
118 struct timeval tv = { 0, 0 };
119 const struct timezone tz = { timezone/60 - 60*daylight, 0 };
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000120
Denis Vlasenko92258542006-11-01 10:25:35 +0000121 tv.tv_sec = read_rtc(utc);
Robert Griebl1cd04452002-07-21 16:50:49 +0000122
Denis Vlasenko92258542006-11-01 10:25:35 +0000123 if (settimeofday(&tv, &tz))
124 bb_perror_msg_and_die("settimeofday() failed");
Robert Griebl1cd04452002-07-21 16:50:49 +0000125
126 return 0;
127}
128
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000129static int from_sys_clock(int utc)
Robert Griebl1cd04452002-07-21 16:50:49 +0000130{
131 struct timeval tv = { 0, 0 };
132 struct timezone tz = { 0, 0 };
133
Denis Vlasenko92258542006-11-01 10:25:35 +0000134 if (gettimeofday(&tv, &tz))
135 bb_perror_msg_and_die("gettimeofday() failed");
Robert Griebl1cd04452002-07-21 16:50:49 +0000136
Denis Vlasenko92258542006-11-01 10:25:35 +0000137 write_rtc(tv.tv_sec, utc);
Robert Griebl1cd04452002-07-21 16:50:49 +0000138 return 0;
139}
140
Mike Frysinger747fc5d2005-09-28 03:21:21 +0000141#ifdef CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS
142# define ADJTIME_PATH "/var/lib/hwclock/adjtime"
143#else
144# define ADJTIME_PATH "/etc/adjtime"
145#endif
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000146static int check_utc(void)
Robert Griebl1cd04452002-07-21 16:50:49 +0000147{
148 int utc = 0;
Denis Vlasenko92258542006-11-01 10:25:35 +0000149 FILE *f = fopen(ADJTIME_PATH, "r");
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000150
Denis Vlasenko92258542006-11-01 10:25:35 +0000151 if (f) {
Bernhard Reutner-Fischer5cf905a2006-03-31 22:36:15 +0000152 RESERVE_CONFIG_BUFFER(buffer, 128);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000153
Denis Vlasenko92258542006-11-01 10:25:35 +0000154 while (fgets(buffer, sizeof(buffer), f)) {
155 int len = strlen(buffer);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000156
Denis Vlasenko92258542006-11-01 10:25:35 +0000157 while (len && isspace(buffer[len - 1]))
Robert Griebl1cd04452002-07-21 16:50:49 +0000158 len--;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000159
Denis Vlasenko92258542006-11-01 10:25:35 +0000160 buffer[len] = 0;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000161
Denis Vlasenko92258542006-11-01 10:25:35 +0000162 if (strncmp(buffer, "UTC", 3) == 0 ) {
Robert Griebl1cd04452002-07-21 16:50:49 +0000163 utc = 1;
164 break;
165 }
166 }
Denis Vlasenko92258542006-11-01 10:25:35 +0000167 fclose(f);
Bernhard Reutner-Fischer5cf905a2006-03-31 22:36:15 +0000168 RELEASE_CONFIG_BUFFER(buffer);
Robert Griebl1cd04452002-07-21 16:50:49 +0000169 }
170 return utc;
171}
172
Denis Vlasenko92258542006-11-01 10:25:35 +0000173#define HWCLOCK_OPT_LOCALTIME 0x01
174#define HWCLOCK_OPT_UTC 0x02
175#define HWCLOCK_OPT_SHOW 0x04
176#define HWCLOCK_OPT_HCTOSYS 0x08
177#define HWCLOCK_OPT_SYSTOHC 0x10
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000178
Denis Vlasenko92258542006-11-01 10:25:35 +0000179int hwclock_main(int argc, char **argv )
Robert Griebl1cd04452002-07-21 16:50:49 +0000180{
Denis Vlasenko67b23e62006-10-03 21:00:06 +0000181 unsigned opt;
Robert Griebl6bb80872004-03-22 21:27:39 +0000182 int utc;
Robert Griebl1cd04452002-07-21 16:50:49 +0000183
Bernhard Reutner-Fischer01d23ad2006-05-26 20:19:22 +0000184#if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS
Denis Vlasenko92258542006-11-01 10:25:35 +0000185 static const struct option hwclock_long_options[] = {
Robert Griebl1cd04452002-07-21 16:50:49 +0000186 { "localtime", 0, 0, 'l' },
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000187 { "utc", 0, 0, 'u' },
188 { "show", 0, 0, 'r' },
Robert Griebl1cd04452002-07-21 16:50:49 +0000189 { "hctosys", 0, 0, 's' },
190 { "systohc", 0, 0, 'w' },
191 { 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";
196 opt = getopt32(argc, argv, "lursw");
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 Vlasenko92258542006-11-01 10:25:35 +0000205 return to_sys_clock(utc);
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000206 }
207 else if (opt & HWCLOCK_OPT_SYSTOHC) {
Denis Vlasenko92258542006-11-01 10:25:35 +0000208 return from_sys_clock(utc);
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000209 } else {
210 /* default HWCLOCK_OPT_SHOW */
Denis Vlasenko92258542006-11-01 10:25:35 +0000211 return show_clock(utc);
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000212 }
Robert Griebl1cd04452002-07-21 16:50:49 +0000213}