blob: 5992d84208a9b35a930a6173715c02dc4aa00a82 [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>
12#include <sys/time.h>
Robert Griebl1cd04452002-07-21 16:50:49 +000013#include <sys/utsname.h>
Robert Griebl1cd04452002-07-21 16:50:49 +000014#include <ctype.h>
15#include <fcntl.h>
Glenn L McGrath689e4b92004-02-22 09:11:33 +000016#include <getopt.h>
17#include <stdlib.h>
18#include <string.h>
19#include <syslog.h>
Robert Griebl1cd04452002-07-21 16:50:49 +000020#include <time.h>
Glenn L McGrath689e4b92004-02-22 09:11:33 +000021#include <unistd.h>
Robert Griebl1cd04452002-07-21 16:50:49 +000022#include "busybox.h"
23
Eric Andersenaff114c2004-04-14 17:51:38 +000024/* Copied from linux/rtc.h to eliminate the kernel dependency */
Robert Griebl7ce75f42003-01-02 07:16:53 +000025struct linux_rtc_time {
26 int tm_sec;
27 int tm_min;
28 int tm_hour;
29 int tm_mday;
30 int tm_mon;
31 int tm_year;
32 int tm_wday;
33 int tm_yday;
34 int tm_isdst;
35};
Mike Frysingerb31566e2005-04-16 04:48:48 +000036
Robert Griebl7ce75f42003-01-02 07:16:53 +000037#define RTC_SET_TIME _IOW('p', 0x0a, struct linux_rtc_time) /* Set RTC time */
38#define RTC_RD_TIME _IOR('p', 0x09, struct linux_rtc_time) /* Read RTC time */
Eric Andersen8882ea52002-12-11 03:41:28 +000039
Robert Griebl1cd04452002-07-21 16:50:49 +000040#ifdef CONFIG_FEATURE_HWCLOCK_LONGOPTIONS
41# ifndef _GNU_SOURCE
42# define _GNU_SOURCE
43# endif
44#endif
45
Glenn L McGrath689e4b92004-02-22 09:11:33 +000046static time_t read_rtc(int utc)
Robert Griebl1cd04452002-07-21 16:50:49 +000047{
48 int rtc;
49 struct tm tm;
50 char *oldtz = 0;
51 time_t t = 0;
52
53 if (( rtc = open ( "/dev/rtc", O_RDONLY )) < 0 ) {
54 if (( rtc = open ( "/dev/misc/rtc", O_RDONLY )) < 0 )
Manuel Novoa III cad53642003-03-19 09:13:01 +000055 bb_perror_msg_and_die ( "Could not access RTC" );
Robert Griebl1cd04452002-07-21 16:50:49 +000056 }
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000057 memset ( &tm, 0, sizeof( struct tm ));
Robert Griebl1cd04452002-07-21 16:50:49 +000058 if ( ioctl ( rtc, RTC_RD_TIME, &tm ) < 0 )
Manuel Novoa III cad53642003-03-19 09:13:01 +000059 bb_perror_msg_and_die ( "Could not 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
Robert Griebl1cd04452002-07-21 16:50:49 +000062 close ( rtc );
63
Eric Andersenc7bda1c2004-03-15 08:29:22 +000064 if ( utc ) {
Robert Griebl1cd04452002-07-21 16:50:49 +000065 oldtz = getenv ( "TZ" );
66 setenv ( "TZ", "UTC 0", 1 );
67 tzset ( );
68 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +000069
Robert Griebl1cd04452002-07-21 16:50:49 +000070 t = mktime ( &tm );
Eric Andersenc7bda1c2004-03-15 08:29:22 +000071
Robert Griebl1cd04452002-07-21 16:50:49 +000072 if ( utc ) {
73 if ( oldtz )
74 setenv ( "TZ", oldtz, 1 );
75 else
76 unsetenv ( "TZ" );
77 tzset ( );
78 }
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{
84 int rtc;
85 struct tm tm;
86
87 if (( rtc = open ( "/dev/rtc", O_WRONLY )) < 0 ) {
88 if (( rtc = open ( "/dev/misc/rtc", O_WRONLY )) < 0 )
Manuel Novoa III cad53642003-03-19 09:13:01 +000089 bb_perror_msg_and_die ( "Could not access RTC" );
Robert Griebl1cd04452002-07-21 16:50:49 +000090 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +000091
Robert Griebl1cd04452002-07-21 16:50:49 +000092 tm = *( utc ? gmtime ( &t ) : localtime ( &t ));
Bernhard Reutner-Fischer5cf905a2006-03-31 22:36:15 +000093 tm.tm_isdst = 0;
Eric Andersenc7bda1c2004-03-15 08:29:22 +000094
Robert Griebl1cd04452002-07-21 16:50:49 +000095 if ( ioctl ( rtc, RTC_SET_TIME, &tm ) < 0 )
Manuel Novoa III cad53642003-03-19 09:13:01 +000096 bb_perror_msg_and_die ( "Could not set the RTC time" );
Eric Andersenc7bda1c2004-03-15 08:29:22 +000097
Robert Griebl1cd04452002-07-21 16:50:49 +000098 close ( rtc );
99}
100
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000101static int show_clock(int utc)
Robert Griebl1cd04452002-07-21 16:50:49 +0000102{
103 struct tm *ptm;
104 time_t t;
Bernhard Reutner-Fischer5cf905a2006-03-31 22:36:15 +0000105 RESERVE_CONFIG_BUFFER(buffer, 64);
Robert Griebl1cd04452002-07-21 16:50:49 +0000106
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000107 t = read_rtc ( utc );
Robert Griebl1cd04452002-07-21 16:50:49 +0000108 ptm = localtime ( &t ); /* Sets 'tzname[]' */
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000109
Bernhard Reutner-Fischer5cf905a2006-03-31 22:36:15 +0000110 safe_strncpy ( buffer, ctime ( &t ), 64);
Robert Griebl1cd04452002-07-21 16:50:49 +0000111 if ( buffer [0] )
Manuel Novoa III cad53642003-03-19 09:13:01 +0000112 buffer [bb_strlen ( buffer ) - 1] = 0;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000113
Robert Griebl1cd04452002-07-21 16:50:49 +0000114 //printf ( "%s %.6f seconds %s\n", buffer, 0.0, utc ? "" : ( ptm-> tm_isdst ? tzname [1] : tzname [0] ));
115 printf ( "%s %.6f seconds\n", buffer, 0.0 );
Bernhard Reutner-Fischer5cf905a2006-03-31 22:36:15 +0000116 RELEASE_CONFIG_BUFFER(buffer);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000117
Robert Griebl1cd04452002-07-21 16:50:49 +0000118 return 0;
119}
120
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000121static int to_sys_clock(int utc)
Robert Griebl1cd04452002-07-21 16:50:49 +0000122{
123 struct timeval tv = { 0, 0 };
124 const struct timezone tz = { timezone/60 - 60*daylight, 0 };
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000125
Bernhard Reutner-Fischer5cf905a2006-03-31 22:36:15 +0000126 tv.tv_sec = read_rtc ( utc );
Robert Griebl1cd04452002-07-21 16:50:49 +0000127
128 if ( settimeofday ( &tv, &tz ))
Manuel Novoa III cad53642003-03-19 09:13:01 +0000129 bb_perror_msg_and_die ( "settimeofday() failed" );
Robert Griebl1cd04452002-07-21 16:50:49 +0000130
131 return 0;
132}
133
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000134static int from_sys_clock(int utc)
Robert Griebl1cd04452002-07-21 16:50:49 +0000135{
136 struct timeval tv = { 0, 0 };
137 struct timezone tz = { 0, 0 };
138
139 if ( gettimeofday ( &tv, &tz ))
Manuel Novoa III cad53642003-03-19 09:13:01 +0000140 bb_perror_msg_and_die ( "gettimeofday() failed" );
Robert Griebl1cd04452002-07-21 16:50:49 +0000141
Bernhard Reutner-Fischer5cf905a2006-03-31 22:36:15 +0000142 write_rtc ( tv.tv_sec, utc );
Robert Griebl1cd04452002-07-21 16:50:49 +0000143 return 0;
144}
145
Mike Frysinger747fc5d2005-09-28 03:21:21 +0000146#ifdef CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS
147# define ADJTIME_PATH "/var/lib/hwclock/adjtime"
148#else
149# define ADJTIME_PATH "/etc/adjtime"
150#endif
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000151static int check_utc(void)
Robert Griebl1cd04452002-07-21 16:50:49 +0000152{
153 int utc = 0;
Mike Frysinger747fc5d2005-09-28 03:21:21 +0000154 FILE *f = fopen ( ADJTIME_PATH, "r" );
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000155
Robert Griebl1cd04452002-07-21 16:50:49 +0000156 if ( f ) {
Bernhard Reutner-Fischer5cf905a2006-03-31 22:36:15 +0000157 RESERVE_CONFIG_BUFFER(buffer, 128);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000158
Robert Griebl1cd04452002-07-21 16:50:49 +0000159 while ( fgets ( buffer, sizeof( buffer ), f )) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000160 int len = bb_strlen ( buffer );
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000161
Robert Griebl1cd04452002-07-21 16:50:49 +0000162 while ( len && isspace ( buffer [len - 1] ))
163 len--;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000164
Robert Griebl1cd04452002-07-21 16:50:49 +0000165 buffer [len] = 0;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000166
Robert Griebl1cd04452002-07-21 16:50:49 +0000167 if ( strncmp ( buffer, "UTC", 3 ) == 0 ) {
168 utc = 1;
169 break;
170 }
171 }
172 fclose ( f );
Bernhard Reutner-Fischer5cf905a2006-03-31 22:36:15 +0000173 RELEASE_CONFIG_BUFFER(buffer);
Robert Griebl1cd04452002-07-21 16:50:49 +0000174 }
175 return utc;
176}
177
Mike Frysingerb31566e2005-04-16 04:48:48 +0000178#define HWCLOCK_OPT_LOCALTIME 0x01
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000179#define HWCLOCK_OPT_UTC 0x02
180#define HWCLOCK_OPT_SHOW 0x04
181#define HWCLOCK_OPT_HCTOSYS 0x08
182#define HWCLOCK_OPT_SYSTOHC 0x10
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000183
Rob Landleydfba7412006-03-06 20:47:33 +0000184int hwclock_main ( int argc, char **argv )
Robert Griebl1cd04452002-07-21 16:50:49 +0000185{
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000186 unsigned long opt;
Robert Griebl6bb80872004-03-22 21:27:39 +0000187 int utc;
Robert Griebl1cd04452002-07-21 16:50:49 +0000188
189#ifdef CONFIG_FEATURE_HWCLOCK_LONGOPTIONS
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000190static const struct option hwclock_long_options[] = {
Robert Griebl1cd04452002-07-21 16:50:49 +0000191 { "localtime", 0, 0, 'l' },
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000192 { "utc", 0, 0, 'u' },
193 { "show", 0, 0, 'r' },
Robert Griebl1cd04452002-07-21 16:50:49 +0000194 { "hctosys", 0, 0, 's' },
195 { "systohc", 0, 0, 'w' },
196 { 0, 0, 0, 0 }
197 };
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000198 bb_applet_long_options = hwclock_long_options;
Robert Griebl1cd04452002-07-21 16:50:49 +0000199#endif
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000200
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000201 bb_opt_complementally = "?:r--ws:w--rs:s--wr:l--u:u--l";
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000202 opt = bb_getopt_ulflags(argc, argv, "lursw");
Robert Griebl1cd04452002-07-21 16:50:49 +0000203
Robert Griebl6bb80872004-03-22 21:27:39 +0000204 /* If -u or -l wasn't given check if we are using utc */
Mike Frysingerb31566e2005-04-16 04:48:48 +0000205 if (opt & (HWCLOCK_OPT_UTC | HWCLOCK_OPT_LOCALTIME))
Robert Griebl6bb80872004-03-22 21:27:39 +0000206 utc = opt & HWCLOCK_OPT_UTC;
207 else
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000208 utc = check_utc();
Mike Frysingerb31566e2005-04-16 04:48:48 +0000209
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000210 if (opt & HWCLOCK_OPT_HCTOSYS) {
Robert Griebl1cd04452002-07-21 16:50:49 +0000211 return to_sys_clock ( utc );
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000212 }
213 else if (opt & HWCLOCK_OPT_SYSTOHC) {
214 return from_sys_clock ( utc );
215 } else {
216 /* default HWCLOCK_OPT_SHOW */
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000217 return show_clock ( utc );
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000218 }
Robert Griebl1cd04452002-07-21 16:50:49 +0000219}