blob: e1a67f290d15ec6361e6414cef9c8c74eae663dd [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>
Robert Griebl1cd04452002-07-21 16:50:49 +000013#include <ctype.h>
14#include <fcntl.h>
Glenn L McGrath689e4b92004-02-22 09:11:33 +000015#include <getopt.h>
16#include <stdlib.h>
17#include <string.h>
18#include <syslog.h>
Robert Griebl1cd04452002-07-21 16:50:49 +000019#include <time.h>
Glenn L McGrath689e4b92004-02-22 09:11:33 +000020#include <unistd.h>
Robert Griebl1cd04452002-07-21 16:50:49 +000021#include "busybox.h"
22
Eric Andersenaff114c2004-04-14 17:51:38 +000023/* Copied from linux/rtc.h to eliminate the kernel dependency */
Robert Griebl7ce75f42003-01-02 07:16:53 +000024struct linux_rtc_time {
25 int tm_sec;
26 int tm_min;
27 int tm_hour;
28 int tm_mday;
29 int tm_mon;
30 int tm_year;
31 int tm_wday;
32 int tm_yday;
33 int tm_isdst;
34};
Mike Frysingerb31566e2005-04-16 04:48:48 +000035
Robert Griebl7ce75f42003-01-02 07:16:53 +000036#define RTC_SET_TIME _IOW('p', 0x0a, struct linux_rtc_time) /* Set RTC time */
37#define RTC_RD_TIME _IOR('p', 0x09, struct linux_rtc_time) /* Read RTC time */
Eric Andersen8882ea52002-12-11 03:41:28 +000038
Bernhard Reutner-Fischer01d23ad2006-05-26 20:19:22 +000039#if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS
Robert Griebl1cd04452002-07-21 16:50:49 +000040# ifndef _GNU_SOURCE
41# define _GNU_SOURCE
42# endif
43#endif
44
Glenn L McGrath689e4b92004-02-22 09:11:33 +000045static time_t read_rtc(int utc)
Robert Griebl1cd04452002-07-21 16:50:49 +000046{
47 int rtc;
48 struct tm tm;
49 char *oldtz = 0;
50 time_t t = 0;
51
52 if (( rtc = open ( "/dev/rtc", O_RDONLY )) < 0 ) {
53 if (( rtc = open ( "/dev/misc/rtc", O_RDONLY )) < 0 )
Manuel Novoa III cad53642003-03-19 09:13:01 +000054 bb_perror_msg_and_die ( "Could not access RTC" );
Robert Griebl1cd04452002-07-21 16:50:49 +000055 }
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000056 memset ( &tm, 0, sizeof( struct tm ));
Robert Griebl1cd04452002-07-21 16:50:49 +000057 if ( ioctl ( rtc, RTC_RD_TIME, &tm ) < 0 )
Manuel Novoa III cad53642003-03-19 09:13:01 +000058 bb_perror_msg_and_die ( "Could not read time from RTC" );
Bernhard Reutner-Fischer5cf905a2006-03-31 22:36:15 +000059 tm.tm_isdst = -1; /* not known */
Eric Andersenc7bda1c2004-03-15 08:29:22 +000060
Robert Griebl1cd04452002-07-21 16:50:49 +000061 close ( rtc );
62
Eric Andersenc7bda1c2004-03-15 08:29:22 +000063 if ( utc ) {
Robert Griebl1cd04452002-07-21 16:50:49 +000064 oldtz = getenv ( "TZ" );
65 setenv ( "TZ", "UTC 0", 1 );
66 tzset ( );
67 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +000068
Robert Griebl1cd04452002-07-21 16:50:49 +000069 t = mktime ( &tm );
Eric Andersenc7bda1c2004-03-15 08:29:22 +000070
Robert Griebl1cd04452002-07-21 16:50:49 +000071 if ( utc ) {
72 if ( oldtz )
73 setenv ( "TZ", oldtz, 1 );
74 else
75 unsetenv ( "TZ" );
76 tzset ( );
77 }
78 return t;
79}
80
Glenn L McGrath689e4b92004-02-22 09:11:33 +000081static void write_rtc(time_t t, int utc)
Robert Griebl1cd04452002-07-21 16:50:49 +000082{
83 int rtc;
84 struct tm tm;
85
86 if (( rtc = open ( "/dev/rtc", O_WRONLY )) < 0 ) {
87 if (( rtc = open ( "/dev/misc/rtc", O_WRONLY )) < 0 )
Manuel Novoa III cad53642003-03-19 09:13:01 +000088 bb_perror_msg_and_die ( "Could not access RTC" );
Robert Griebl1cd04452002-07-21 16:50:49 +000089 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +000090
Robert Griebl1cd04452002-07-21 16:50:49 +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
Robert Griebl1cd04452002-07-21 16:50:49 +000094 if ( ioctl ( rtc, RTC_SET_TIME, &tm ) < 0 )
Manuel Novoa III cad53642003-03-19 09:13:01 +000095 bb_perror_msg_and_die ( "Could not set the RTC time" );
Eric Andersenc7bda1c2004-03-15 08:29:22 +000096
Robert Griebl1cd04452002-07-21 16:50:49 +000097 close ( rtc );
98}
99
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000100static int show_clock(int utc)
Robert Griebl1cd04452002-07-21 16:50:49 +0000101{
102 struct tm *ptm;
103 time_t t;
Bernhard Reutner-Fischer5cf905a2006-03-31 22:36:15 +0000104 RESERVE_CONFIG_BUFFER(buffer, 64);
Robert Griebl1cd04452002-07-21 16:50:49 +0000105
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000106 t = read_rtc ( utc );
Robert Griebl1cd04452002-07-21 16:50:49 +0000107 ptm = localtime ( &t ); /* Sets 'tzname[]' */
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000108
Bernhard Reutner-Fischer5cf905a2006-03-31 22:36:15 +0000109 safe_strncpy ( buffer, ctime ( &t ), 64);
Robert Griebl1cd04452002-07-21 16:50:49 +0000110 if ( buffer [0] )
Rob Landleya3896512006-05-07 20:20:34 +0000111 buffer [strlen ( buffer ) - 1] = 0;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000112
Robert Griebl1cd04452002-07-21 16:50:49 +0000113 //printf ( "%s %.6f seconds %s\n", buffer, 0.0, utc ? "" : ( ptm-> tm_isdst ? tzname [1] : tzname [0] ));
114 printf ( "%s %.6f seconds\n", buffer, 0.0 );
Bernhard Reutner-Fischer5cf905a2006-03-31 22:36:15 +0000115 RELEASE_CONFIG_BUFFER(buffer);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000116
Robert Griebl1cd04452002-07-21 16:50:49 +0000117 return 0;
118}
119
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000120static int to_sys_clock(int utc)
Robert Griebl1cd04452002-07-21 16:50:49 +0000121{
122 struct timeval tv = { 0, 0 };
123 const struct timezone tz = { timezone/60 - 60*daylight, 0 };
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000124
Bernhard Reutner-Fischer5cf905a2006-03-31 22:36:15 +0000125 tv.tv_sec = read_rtc ( utc );
Robert Griebl1cd04452002-07-21 16:50:49 +0000126
127 if ( settimeofday ( &tv, &tz ))
Manuel Novoa III cad53642003-03-19 09:13:01 +0000128 bb_perror_msg_and_die ( "settimeofday() failed" );
Robert Griebl1cd04452002-07-21 16:50:49 +0000129
130 return 0;
131}
132
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000133static int from_sys_clock(int utc)
Robert Griebl1cd04452002-07-21 16:50:49 +0000134{
135 struct timeval tv = { 0, 0 };
136 struct timezone tz = { 0, 0 };
137
138 if ( gettimeofday ( &tv, &tz ))
Manuel Novoa III cad53642003-03-19 09:13:01 +0000139 bb_perror_msg_and_die ( "gettimeofday() failed" );
Robert Griebl1cd04452002-07-21 16:50:49 +0000140
Bernhard Reutner-Fischer5cf905a2006-03-31 22:36:15 +0000141 write_rtc ( tv.tv_sec, utc );
Robert Griebl1cd04452002-07-21 16:50:49 +0000142 return 0;
143}
144
Mike Frysinger747fc5d2005-09-28 03:21:21 +0000145#ifdef CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS
146# define ADJTIME_PATH "/var/lib/hwclock/adjtime"
147#else
148# define ADJTIME_PATH "/etc/adjtime"
149#endif
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000150static int check_utc(void)
Robert Griebl1cd04452002-07-21 16:50:49 +0000151{
152 int utc = 0;
Mike Frysinger747fc5d2005-09-28 03:21:21 +0000153 FILE *f = fopen ( ADJTIME_PATH, "r" );
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000154
Robert Griebl1cd04452002-07-21 16:50:49 +0000155 if ( f ) {
Bernhard Reutner-Fischer5cf905a2006-03-31 22:36:15 +0000156 RESERVE_CONFIG_BUFFER(buffer, 128);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000157
Robert Griebl1cd04452002-07-21 16:50:49 +0000158 while ( fgets ( buffer, sizeof( buffer ), f )) {
Rob Landleya3896512006-05-07 20:20:34 +0000159 int len = strlen ( buffer );
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000160
Robert Griebl1cd04452002-07-21 16:50:49 +0000161 while ( len && isspace ( buffer [len - 1] ))
162 len--;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000163
Robert Griebl1cd04452002-07-21 16:50:49 +0000164 buffer [len] = 0;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000165
Robert Griebl1cd04452002-07-21 16:50:49 +0000166 if ( strncmp ( buffer, "UTC", 3 ) == 0 ) {
167 utc = 1;
168 break;
169 }
170 }
171 fclose ( f );
Bernhard Reutner-Fischer5cf905a2006-03-31 22:36:15 +0000172 RELEASE_CONFIG_BUFFER(buffer);
Robert Griebl1cd04452002-07-21 16:50:49 +0000173 }
174 return utc;
175}
176
Mike Frysingerb31566e2005-04-16 04:48:48 +0000177#define HWCLOCK_OPT_LOCALTIME 0x01
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000178#define HWCLOCK_OPT_UTC 0x02
179#define HWCLOCK_OPT_SHOW 0x04
180#define HWCLOCK_OPT_HCTOSYS 0x08
181#define HWCLOCK_OPT_SYSTOHC 0x10
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000182
Rob Landleydfba7412006-03-06 20:47:33 +0000183int hwclock_main ( int argc, char **argv )
Robert Griebl1cd04452002-07-21 16:50:49 +0000184{
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000185 unsigned long opt;
Robert Griebl6bb80872004-03-22 21:27:39 +0000186 int utc;
Robert Griebl1cd04452002-07-21 16:50:49 +0000187
Bernhard Reutner-Fischer01d23ad2006-05-26 20:19:22 +0000188#if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000189static const struct option hwclock_long_options[] = {
Robert Griebl1cd04452002-07-21 16:50:49 +0000190 { "localtime", 0, 0, 'l' },
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000191 { "utc", 0, 0, 'u' },
192 { "show", 0, 0, 'r' },
Robert Griebl1cd04452002-07-21 16:50:49 +0000193 { "hctosys", 0, 0, 's' },
194 { "systohc", 0, 0, 'w' },
195 { 0, 0, 0, 0 }
196 };
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000197 bb_applet_long_options = hwclock_long_options;
Robert Griebl1cd04452002-07-21 16:50:49 +0000198#endif
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000199
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000200 bb_opt_complementally = "?:r--ws:w--rs:s--wr:l--u:u--l";
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000201 opt = bb_getopt_ulflags(argc, argv, "lursw");
Robert Griebl1cd04452002-07-21 16:50:49 +0000202
Robert Griebl6bb80872004-03-22 21:27:39 +0000203 /* If -u or -l wasn't given check if we are using utc */
Mike Frysingerb31566e2005-04-16 04:48:48 +0000204 if (opt & (HWCLOCK_OPT_UTC | HWCLOCK_OPT_LOCALTIME))
Robert Griebl6bb80872004-03-22 21:27:39 +0000205 utc = opt & HWCLOCK_OPT_UTC;
206 else
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000207 utc = check_utc();
Mike Frysingerb31566e2005-04-16 04:48:48 +0000208
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000209 if (opt & HWCLOCK_OPT_HCTOSYS) {
Robert Griebl1cd04452002-07-21 16:50:49 +0000210 return to_sys_clock ( utc );
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000211 }
212 else if (opt & HWCLOCK_OPT_SYSTOHC) {
213 return from_sys_clock ( utc );
214 } else {
215 /* default HWCLOCK_OPT_SHOW */
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000216 return show_clock ( utc );
Glenn L McGrath689e4b92004-02-22 09:11:33 +0000217 }
Robert Griebl1cd04452002-07-21 16:50:49 +0000218}