This commit was manufactured by cvs2svn to create tag 'busybox_1_00'.
[oweals/busybox.git] / busybox / util-linux / hwclock.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini hwclock implementation for busybox
4  *
5  * Copyright (C) 2002 Robert Griebl <griebl@gmx.de>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  *
21 */
22
23
24 #include <sys/ioctl.h>
25 #include <sys/time.h>
26 #include <sys/utsname.h>
27 #include <ctype.h>
28 #include <fcntl.h>
29 #include <getopt.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <syslog.h>
33 #include <time.h>
34 #include <unistd.h>
35 #include "busybox.h"
36
37 /* Copied from linux/rtc.h to eliminate the kernel dependency */
38 struct linux_rtc_time {
39         int tm_sec;
40         int tm_min;
41         int tm_hour;
42         int tm_mday;
43         int tm_mon;
44         int tm_year;
45         int tm_wday;
46         int tm_yday;
47         int tm_isdst;
48 };
49                 
50 #define RTC_SET_TIME   _IOW('p', 0x0a, struct linux_rtc_time) /* Set RTC time    */
51 #define RTC_RD_TIME    _IOR('p', 0x09, struct linux_rtc_time) /* Read RTC time   */
52
53 #ifdef CONFIG_FEATURE_HWCLOCK_LONGOPTIONS
54 # ifndef _GNU_SOURCE
55 #  define _GNU_SOURCE
56 # endif
57 #endif
58
59 static time_t read_rtc(int utc)
60 {
61         int rtc;
62         struct tm tm;
63         char *oldtz = 0;
64         time_t t = 0;
65
66         if (( rtc = open ( "/dev/rtc", O_RDONLY )) < 0 ) {
67                 if (( rtc = open ( "/dev/misc/rtc", O_RDONLY )) < 0 )
68                         bb_perror_msg_and_die ( "Could not access RTC" );
69         }
70         memset ( &tm, 0, sizeof( struct tm ));
71         if ( ioctl ( rtc, RTC_RD_TIME, &tm ) < 0 )
72                 bb_perror_msg_and_die ( "Could not read time from RTC" );
73         tm. tm_isdst = -1; // not known
74
75         close ( rtc );
76
77         if ( utc ) {
78                 oldtz = getenv ( "TZ" );
79                 setenv ( "TZ", "UTC 0", 1 );
80                 tzset ( );
81         }
82
83         t = mktime ( &tm );
84
85         if ( utc ) {
86                 if ( oldtz )
87                         setenv ( "TZ", oldtz, 1 );
88                 else
89                         unsetenv ( "TZ" );
90                 tzset ( );
91         }
92         return t;
93 }
94
95 static void write_rtc(time_t t, int utc)
96 {
97         int rtc;
98         struct tm tm;
99
100         if (( rtc = open ( "/dev/rtc", O_WRONLY )) < 0 ) {
101                 if (( rtc = open ( "/dev/misc/rtc", O_WRONLY )) < 0 )
102                         bb_perror_msg_and_die ( "Could not access RTC" );
103         }
104
105         tm = *( utc ? gmtime ( &t ) : localtime ( &t ));
106         tm. tm_isdst = 0;
107
108         if ( ioctl ( rtc, RTC_SET_TIME, &tm ) < 0 )
109                 bb_perror_msg_and_die ( "Could not set the RTC time" );
110
111         close ( rtc );
112 }
113
114 static int show_clock(int utc)
115 {
116         struct tm *ptm;
117         time_t t;
118         char buffer [64];
119
120         t = read_rtc ( utc );
121         ptm = localtime ( &t );  /* Sets 'tzname[]' */
122
123         safe_strncpy ( buffer, ctime ( &t ), sizeof( buffer ));
124         if ( buffer [0] )
125                 buffer [bb_strlen ( buffer ) - 1] = 0;
126
127         //printf ( "%s  %.6f seconds %s\n", buffer, 0.0, utc ? "" : ( ptm-> tm_isdst ? tzname [1] : tzname [0] ));
128         printf ( "%s  %.6f seconds\n", buffer, 0.0 );
129
130         return 0;
131 }
132
133 static int to_sys_clock(int utc)
134 {
135         struct timeval tv = { 0, 0 };
136         const struct timezone tz = { timezone/60 - 60*daylight, 0 };
137
138         tv. tv_sec = read_rtc ( utc );
139
140         if ( settimeofday ( &tv, &tz ))
141                 bb_perror_msg_and_die ( "settimeofday() failed" );
142
143         return 0;
144 }
145
146 static int from_sys_clock(int utc)
147 {
148         struct timeval tv = { 0, 0 };
149         struct timezone tz = { 0, 0 };
150
151         if ( gettimeofday ( &tv, &tz ))
152                 bb_perror_msg_and_die ( "gettimeofday() failed" );
153
154         write_rtc ( tv. tv_sec, utc );
155         return 0;
156 }
157
158
159 static int check_utc(void)
160 {
161         int utc = 0;
162         FILE *f = fopen ( "/var/lib/hwclock/adjtime", "r" );
163
164         if ( f ) {
165                 char buffer [128];
166
167                 while ( fgets ( buffer, sizeof( buffer ), f )) {
168                         int len = bb_strlen ( buffer );
169
170                         while ( len && isspace ( buffer [len - 1] ))
171                                 len--;
172
173                         buffer [len] = 0;
174
175                         if ( strncmp ( buffer, "UTC", 3 ) == 0 ) {
176                                 utc = 1;
177                                 break;
178                         }
179                 }
180                 fclose ( f );
181         }
182         return utc;
183 }
184
185 #define HWCLOCK_OPT_LOCALTIME   1
186 #define HWCLOCK_OPT_UTC         2
187 #define HWCLOCK_OPT_SHOW        4
188 #define HWCLOCK_OPT_HCTOSYS     8
189 #define HWCLOCK_OPT_SYSTOHC     16
190
191 extern int hwclock_main ( int argc, char **argv )
192 {
193         unsigned long opt;
194         int utc;
195
196 #ifdef CONFIG_FEATURE_HWCLOCK_LONGOPTIONS
197 static const struct option hwclock_long_options[] = {
198                 { "localtime", 0, 0, 'l' },
199                 { "utc",       0, 0, 'u' },
200                 { "show",      0, 0, 'r' },
201                 { "hctosys",   0, 0, 's' },
202                 { "systohc",   0, 0, 'w' },
203                 { 0,           0, 0, 0 }
204         };
205         bb_applet_long_options = hwclock_long_options;
206 #endif
207
208         bb_opt_complementaly = "r~ws:w~rs:s~wr:l~u:u~l";
209         opt = bb_getopt_ulflags(argc, argv, "lursw");
210         /* Check only one mode was given */
211         if(opt & 0x80000000UL) {
212                 bb_show_usage();
213         }
214
215         /* If -u or -l wasn't given check if we are using utc */
216         if (opt & (HWCLOCK_OPT_UTC | HWCLOCK_OPT_LOCALTIME)) 
217                 utc = opt & HWCLOCK_OPT_UTC;
218         else
219                 utc = check_utc();
220         
221         if (opt & HWCLOCK_OPT_HCTOSYS) {
222                 return to_sys_clock ( utc );
223         }
224         else if (opt & HWCLOCK_OPT_SYSTOHC) {
225                 return from_sys_clock ( utc );
226         } else {
227                 /* default HWCLOCK_OPT_SHOW */
228                 return show_clock ( utc );
229         }
230 }