add open_read_close() and similar stuff
[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  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
8 */
9
10
11 #include <sys/ioctl.h>
12 #include <sys/utsname.h>
13 #include <ctype.h>
14 #include <fcntl.h>
15 #include <getopt.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <syslog.h>
19 #include <time.h>
20 #include <unistd.h>
21 #include "busybox.h"
22
23 /* Copied from linux/rtc.h to eliminate the kernel dependency */
24 struct 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 };
35
36 #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   */
38
39 #if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS
40 # ifndef _GNU_SOURCE
41 #  define _GNU_SOURCE
42 # endif
43 #endif
44
45 static time_t read_rtc(int utc)
46 {
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 )
54                         bb_perror_msg_and_die ( "cannot access RTC" );
55         }
56         memset ( &tm, 0, sizeof( struct tm ));
57         if ( ioctl ( rtc, RTC_RD_TIME, &tm ) < 0 )
58                 bb_perror_msg_and_die ( "cannot read time from RTC" );
59         tm.tm_isdst = -1; /* not known */
60
61         close ( rtc );
62
63         if ( utc ) {
64                 oldtz = getenv ( "TZ" );
65                 setenv ( "TZ", "UTC 0", 1 );
66                 tzset ( );
67         }
68
69         t = mktime ( &tm );
70
71         if ( utc ) {
72                 if ( oldtz )
73                         setenv ( "TZ", oldtz, 1 );
74                 else
75                         unsetenv ( "TZ" );
76                 tzset ( );
77         }
78         return t;
79 }
80
81 static void write_rtc(time_t t, int utc)
82 {
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 )
88                         bb_perror_msg_and_die ( "cannot access RTC" );
89         }
90
91         tm = *( utc ? gmtime ( &t ) : localtime ( &t ));
92         tm.tm_isdst = 0;
93
94         if ( ioctl ( rtc, RTC_SET_TIME, &tm ) < 0 )
95                 bb_perror_msg_and_die ( "cannot set the RTC time" );
96
97         close ( rtc );
98 }
99
100 static int show_clock(int utc)
101 {
102         struct tm *ptm;
103         time_t t;
104         RESERVE_CONFIG_BUFFER(buffer, 64);
105
106         t = read_rtc ( utc );
107         ptm = localtime ( &t );  /* Sets 'tzname[]' */
108
109         safe_strncpy ( buffer, ctime ( &t ), 64);
110         if ( buffer [0] )
111                 buffer [strlen ( buffer ) - 1] = 0;
112
113         //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 );
115         RELEASE_CONFIG_BUFFER(buffer);
116
117         return 0;
118 }
119
120 static int to_sys_clock(int utc)
121 {
122         struct timeval tv = { 0, 0 };
123         const struct timezone tz = { timezone/60 - 60*daylight, 0 };
124
125         tv.tv_sec = read_rtc ( utc );
126
127         if ( settimeofday ( &tv, &tz ))
128                 bb_perror_msg_and_die ( "settimeofday() failed" );
129
130         return 0;
131 }
132
133 static int from_sys_clock(int utc)
134 {
135         struct timeval tv = { 0, 0 };
136         struct timezone tz = { 0, 0 };
137
138         if ( gettimeofday ( &tv, &tz ))
139                 bb_perror_msg_and_die ( "gettimeofday() failed" );
140
141         write_rtc ( tv.tv_sec, utc );
142         return 0;
143 }
144
145 #ifdef CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS
146 # define ADJTIME_PATH "/var/lib/hwclock/adjtime"
147 #else
148 # define ADJTIME_PATH "/etc/adjtime"
149 #endif
150 static int check_utc(void)
151 {
152         int utc = 0;
153         FILE *f = fopen ( ADJTIME_PATH, "r" );
154
155         if ( f ) {
156                 RESERVE_CONFIG_BUFFER(buffer, 128);
157
158                 while ( fgets ( buffer, sizeof( buffer ), f )) {
159                         int len = strlen ( buffer );
160
161                         while ( len && isspace ( buffer [len - 1] ))
162                                 len--;
163
164                         buffer [len] = 0;
165
166                         if ( strncmp ( buffer, "UTC", 3 ) == 0 ) {
167                                 utc = 1;
168                                 break;
169                         }
170                 }
171                 fclose ( f );
172                 RELEASE_CONFIG_BUFFER(buffer);
173         }
174         return utc;
175 }
176
177 #define HWCLOCK_OPT_LOCALTIME   0x01
178 #define HWCLOCK_OPT_UTC                 0x02
179 #define HWCLOCK_OPT_SHOW                0x04
180 #define HWCLOCK_OPT_HCTOSYS             0x08
181 #define HWCLOCK_OPT_SYSTOHC             0x10
182
183 int hwclock_main ( int argc, char **argv )
184 {
185         unsigned opt;
186         int utc;
187
188 #if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS
189 static const struct option hwclock_long_options[] = {
190                 { "localtime", 0, 0, 'l' },
191                 { "utc",       0, 0, 'u' },
192                 { "show",      0, 0, 'r' },
193                 { "hctosys",   0, 0, 's' },
194                 { "systohc",   0, 0, 'w' },
195                 { 0,           0, 0, 0 }
196         };
197         applet_long_options = hwclock_long_options;
198 #endif
199
200         opt_complementary = "?:r--ws:w--rs:s--wr:l--u:u--l";
201         opt = getopt32(argc, argv, "lursw");
202
203         /* If -u or -l wasn't given check if we are using utc */
204         if (opt & (HWCLOCK_OPT_UTC | HWCLOCK_OPT_LOCALTIME))
205                 utc = opt & HWCLOCK_OPT_UTC;
206         else
207                 utc = check_utc();
208
209         if (opt & HWCLOCK_OPT_HCTOSYS) {
210                 return to_sys_clock ( utc );
211         }
212         else if (opt & HWCLOCK_OPT_SYSTOHC) {
213                 return from_sys_clock ( utc );
214         } else {
215                 /* default HWCLOCK_OPT_SHOW */
216                 return show_clock ( utc );
217         }
218 }