Robert P. Day removed 8 gazillion occurrences of "extern" on function
[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/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 #ifdef CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS
159 # define ADJTIME_PATH "/var/lib/hwclock/adjtime"
160 #else
161 # define ADJTIME_PATH "/etc/adjtime"
162 #endif
163 static int check_utc(void)
164 {
165         int utc = 0;
166         FILE *f = fopen ( ADJTIME_PATH, "r" );
167
168         if ( f ) {
169                 char buffer [128];
170
171                 while ( fgets ( buffer, sizeof( buffer ), f )) {
172                         int len = bb_strlen ( buffer );
173
174                         while ( len && isspace ( buffer [len - 1] ))
175                                 len--;
176
177                         buffer [len] = 0;
178
179                         if ( strncmp ( buffer, "UTC", 3 ) == 0 ) {
180                                 utc = 1;
181                                 break;
182                         }
183                 }
184                 fclose ( f );
185         }
186         return utc;
187 }
188
189 #define HWCLOCK_OPT_LOCALTIME   0x01
190 #define HWCLOCK_OPT_UTC                 0x02
191 #define HWCLOCK_OPT_SHOW                0x04
192 #define HWCLOCK_OPT_HCTOSYS             0x08
193 #define HWCLOCK_OPT_SYSTOHC             0x10
194
195 int hwclock_main ( int argc, char **argv )
196 {
197         unsigned long opt;
198         int utc;
199
200 #ifdef CONFIG_FEATURE_HWCLOCK_LONGOPTIONS
201 static const struct option hwclock_long_options[] = {
202                 { "localtime", 0, 0, 'l' },
203                 { "utc",       0, 0, 'u' },
204                 { "show",      0, 0, 'r' },
205                 { "hctosys",   0, 0, 's' },
206                 { "systohc",   0, 0, 'w' },
207                 { 0,           0, 0, 0 }
208         };
209         bb_applet_long_options = hwclock_long_options;
210 #endif
211
212         bb_opt_complementally = "?:r--ws:w--rs:s--wr:l--u:u--l";
213         opt = bb_getopt_ulflags(argc, argv, "lursw");
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 }