Fix tar -j support
[oweals/busybox.git] / 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/utsname.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <syslog.h>
28 #include <string.h>
29 #include <ctype.h>
30 #include <fcntl.h>
31 #include <sys/time.h>
32 #include <time.h>
33 #include <sys/ioctl.h>
34 #include "busybox.h"
35
36
37 /* Copied from linux/rtc.h to eliminate the kernel dependancy */
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
51 #define RTC_SET_TIME   _IOW('p', 0x0a, struct linux_rtc_time) /* Set RTC time    */
52 #define RTC_RD_TIME    _IOR('p', 0x09, struct linux_rtc_time) /* Read RTC time   */
53
54
55 #ifdef CONFIG_FEATURE_HWCLOCK_LONGOPTIONS
56 # ifndef _GNU_SOURCE
57 #  define _GNU_SOURCE
58 # endif
59 #endif
60
61 #include <getopt.h>
62
63
64 enum OpMode {
65         SHOW,
66         SYSTOHC,
67         HCTOSYS
68 };
69
70
71 time_t read_rtc ( int utc )
72 {
73         int rtc;
74         struct tm tm;
75         char *oldtz = 0;
76         time_t t = 0;
77
78         if (( rtc = open ( "/dev/rtc", O_RDONLY )) < 0 ) {
79                 if (( rtc = open ( "/dev/misc/rtc", O_RDONLY )) < 0 )
80                         bb_perror_msg_and_die ( "Could not access RTC" );
81         }
82         memset ( &tm, 0, sizeof( struct tm ));
83         if ( ioctl ( rtc, RTC_RD_TIME, &tm ) < 0 )
84                 bb_perror_msg_and_die ( "Could not read time from RTC" );
85         tm. tm_isdst = -1; // not known
86         
87         close ( rtc );
88
89         if ( utc ) {    
90                 oldtz = getenv ( "TZ" );
91                 setenv ( "TZ", "UTC 0", 1 );
92                 tzset ( );
93         }
94         
95         t = mktime ( &tm );
96         
97         if ( utc ) {
98                 if ( oldtz )
99                         setenv ( "TZ", oldtz, 1 );
100                 else
101                         unsetenv ( "TZ" );
102                 tzset ( );
103         }
104         return t;
105 }
106
107 void write_rtc ( time_t t, int utc )
108 {
109         int rtc;
110         struct tm tm;
111
112         if (( rtc = open ( "/dev/rtc", O_WRONLY )) < 0 ) {
113                 if (( rtc = open ( "/dev/misc/rtc", O_WRONLY )) < 0 )
114                         bb_perror_msg_and_die ( "Could not access RTC" );
115         }
116         
117         tm = *( utc ? gmtime ( &t ) : localtime ( &t ));
118         tm. tm_isdst = 0;
119         
120         if ( ioctl ( rtc, RTC_SET_TIME, &tm ) < 0 )
121                 bb_perror_msg_and_die ( "Could not set the RTC time" );
122         
123         close ( rtc );
124 }
125
126 int show_clock ( int utc )
127 {
128         struct tm *ptm;
129         time_t t;
130         char buffer [64];
131
132         t = read_rtc ( utc );           
133         ptm = localtime ( &t );  /* Sets 'tzname[]' */
134         
135         safe_strncpy ( buffer, ctime ( &t ), sizeof( buffer ));
136         if ( buffer [0] )
137                 buffer [bb_strlen ( buffer ) - 1] = 0;
138         
139         //printf ( "%s  %.6f seconds %s\n", buffer, 0.0, utc ? "" : ( ptm-> tm_isdst ? tzname [1] : tzname [0] ));
140         printf ( "%s  %.6f seconds\n", buffer, 0.0 );
141         
142         return 0;
143 }
144
145 int to_sys_clock ( int utc )
146 {
147         struct timeval tv = { 0, 0 };
148         const struct timezone tz = { timezone/60 - 60*daylight, 0 };
149         
150         tv. tv_sec = read_rtc ( utc );
151
152         if ( settimeofday ( &tv, &tz ))
153                 bb_perror_msg_and_die ( "settimeofday() failed" );
154
155         return 0;
156 }
157
158 int from_sys_clock ( int utc )
159 {
160         struct timeval tv = { 0, 0 };
161         struct timezone tz = { 0, 0 };
162
163         if ( gettimeofday ( &tv, &tz ))
164                 bb_perror_msg_and_die ( "gettimeofday() failed" );
165
166         write_rtc ( tv. tv_sec, utc );
167         return 0;
168 }
169
170
171 int check_utc ( void )
172 {
173         int utc = 0;
174         FILE *f = fopen ( "/etc/adjtime", "r" );
175         
176         if ( f ) {
177                 char buffer [128];
178         
179                 while ( fgets ( buffer, sizeof( buffer ), f )) {
180                         int len = bb_strlen ( buffer );
181                         
182                         while ( len && isspace ( buffer [len - 1] ))
183                                 len--;
184                                 
185                         buffer [len] = 0;
186                 
187                         if ( strncmp ( buffer, "UTC", 3 ) == 0 ) {
188                                 utc = 1;
189                                 break;
190                         }
191                 }
192                 fclose ( f );
193         }
194         return utc;
195 }
196
197 extern int hwclock_main ( int argc, char **argv )
198 {
199         int     opt;
200         enum OpMode mode = SHOW;
201         int utc = 0;
202         int utc_arg = 0;
203
204 #ifdef CONFIG_FEATURE_HWCLOCK_LONGOPTIONS
205         struct option long_options[] = {
206                 { "show",      0, 0, 'r' },
207                 { "utc",       0, 0, 'u' },
208                 { "localtime", 0, 0, 'l' },
209                 { "hctosys",   0, 0, 's' },
210                 { "systohc",   0, 0, 'w' },
211                 { 0,           0, 0, 0 }
212         };
213         
214         while (( opt = getopt_long ( argc, argv, "rwsul", long_options, 0 )) != EOF ) {
215 #else
216         while (( opt = getopt ( argc, argv, "rwsul" )) != EOF ) {
217 #endif
218                 switch ( opt ) {
219                 case 'r': 
220                         mode = SHOW; 
221                         break;
222                 case 'w': 
223                         mode = SYSTOHC;
224                         break;
225                 case 's': 
226                         mode = HCTOSYS;
227                         break;
228                 case 'u': 
229                         utc = 1;
230                         utc_arg = 1;
231                         break;
232                 case 'l': // -l is not supported by the normal hwclock (only --localtime)
233                         utc = 0;
234                         utc_arg = 1;
235                         break;
236                 default:
237                         bb_show_usage();
238                         break;
239                 }
240         }
241
242         if ( !utc_arg )
243                 utc = check_utc ( );
244         
245         switch ( mode ) {
246         case SYSTOHC:
247                 return from_sys_clock ( utc );
248
249         case HCTOSYS:
250                 return to_sys_clock ( utc );
251
252         case SHOW:
253         default:
254                 return show_clock ( utc );      
255         }       
256 }
257
258