882a0c55a54c2d2ae830595fc4349c9cedc214fb
[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 #include <sys/utsname.h>
11 #include <getopt.h>
12 #include "libbb.h"
13
14 /* Copied from linux/rtc.h to eliminate the kernel dependency */
15 struct linux_rtc_time {
16         int tm_sec;
17         int tm_min;
18         int tm_hour;
19         int tm_mday;
20         int tm_mon;
21         int tm_year;
22         int tm_wday;
23         int tm_yday;
24         int tm_isdst;
25 };
26
27 #define RTC_SET_TIME   _IOW('p', 0x0a, struct linux_rtc_time) /* Set RTC time    */
28 #define RTC_RD_TIME    _IOR('p', 0x09, struct linux_rtc_time) /* Read RTC time   */
29
30 #if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS
31 # ifndef _GNU_SOURCE
32 #  define _GNU_SOURCE
33 # endif
34 #endif
35
36 static const char *rtcname;
37
38 static int xopen_rtc(int flags)
39 {
40         int rtc;
41
42         if (!rtcname) {
43                 rtc = open("/dev/rtc", flags);
44                 if (rtc >= 0)
45                         return rtc;
46                 rtc = open("/dev/rtc0", flags);
47                 if (rtc >= 0)
48                         return rtc;
49                 rtcname = "/dev/misc/rtc";
50         }
51         return xopen(rtcname, flags);
52 }
53
54 static time_t read_rtc(int utc)
55 {
56         struct tm tm;
57         char *oldtz = 0;
58         time_t t = 0;
59         int rtc = xopen_rtc(O_RDONLY);
60
61         memset(&tm, 0, sizeof(struct tm));
62         xioctl(rtc, RTC_RD_TIME, &tm);
63         tm.tm_isdst = -1; /* not known */
64
65         close(rtc);
66
67         if (utc) {
68                 oldtz = getenv("TZ");
69                 putenv((char*)"TZ=UTC0");
70                 tzset();
71         }
72
73         t = mktime(&tm);
74
75         if (utc) {
76                 unsetenv("TZ");
77                 if (oldtz)
78                         putenv(oldtz - 3);
79                 tzset();
80         }
81         return t;
82 }
83
84 static void write_rtc(time_t t, int utc)
85 {
86         struct tm tm;
87         int rtc = xopen_rtc(O_WRONLY);
88
89         tm = *(utc ? gmtime(&t) : localtime(&t));
90         tm.tm_isdst = 0;
91
92         xioctl(rtc, RTC_SET_TIME, &tm);
93
94         close(rtc);
95 }
96
97 static void show_clock(int utc)
98 {
99         //struct tm *ptm;
100         time_t t;
101         char *cp;
102
103         t = read_rtc(utc);
104         //ptm = localtime(&t);  /* Sets 'tzname[]' */
105
106         cp = ctime(&t);
107         if (cp[0])
108                 cp[strlen(cp) - 1] = '\0';
109
110         //printf("%s  %.6f seconds %s\n", cp, 0.0, utc ? "" : (ptm->tm_isdst ? tzname[1] : tzname[0]));
111         printf("%s  0.000000 seconds\n", cp);
112 }
113
114 static void to_sys_clock(int utc)
115 {
116         struct timeval tv;
117         const struct timezone tz = { timezone/60 - 60*daylight, 0 };
118
119         tv.tv_sec = read_rtc(utc);
120         tv.tv_usec = 0;
121         if (settimeofday(&tv, &tz))
122                 bb_perror_msg_and_die("settimeofday() failed");
123 }
124
125 static void from_sys_clock(int utc)
126 {
127         struct timeval tv;
128
129         gettimeofday(&tv, NULL);
130         //if (gettimeofday(&tv, NULL))
131         //      bb_perror_msg_and_die("gettimeofday() failed");
132         write_rtc(tv.tv_sec, utc);
133 }
134
135 #ifdef CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS
136 # define ADJTIME_PATH "/var/lib/hwclock/adjtime"
137 #else
138 # define ADJTIME_PATH "/etc/adjtime"
139 #endif
140 static int check_utc(void)
141 {
142         int utc = 0;
143         FILE *f = fopen(ADJTIME_PATH, "r");
144
145         if (f) {
146                 RESERVE_CONFIG_BUFFER(buffer, 128);
147
148                 while (fgets(buffer, sizeof(buffer), f)) {
149                         int len = strlen(buffer);
150
151                         while (len && isspace(buffer[len - 1]))
152                                 len--;
153
154                         buffer[len] = 0;
155
156                         if (strncmp(buffer, "UTC", 3) == 0) {
157                                 utc = 1;
158                                 break;
159                         }
160                 }
161                 fclose(f);
162                 RELEASE_CONFIG_BUFFER(buffer);
163         }
164         return utc;
165 }
166
167 #define HWCLOCK_OPT_LOCALTIME   0x01
168 #define HWCLOCK_OPT_UTC         0x02
169 #define HWCLOCK_OPT_SHOW        0x04
170 #define HWCLOCK_OPT_HCTOSYS     0x08
171 #define HWCLOCK_OPT_SYSTOHC     0x10
172 #define HWCLOCK_OPT_RTCFILE     0x20
173
174 int hwclock_main(int argc, char **argv);
175 int hwclock_main(int argc, char **argv)
176 {
177         unsigned opt;
178         int utc;
179
180 #if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS
181         static const struct option hwclock_long_options[] = {
182                 { "localtime", 0, 0, 'l' },
183                 { "utc",       0, 0, 'u' },
184                 { "show",      0, 0, 'r' },
185                 { "hctosys",   0, 0, 's' },
186                 { "systohc",   0, 0, 'w' },
187                 { "file",      1, 0, 'f' },
188                 { 0,           0, 0, 0 }
189         };
190         applet_long_options = hwclock_long_options;
191 #endif
192         opt_complementary = "r--ws:w--rs:s--wr:l--u:u--l";
193         opt = getopt32(argc, argv, "lurswf:", &rtcname);
194
195         /* If -u or -l wasn't given check if we are using utc */
196         if (opt & (HWCLOCK_OPT_UTC | HWCLOCK_OPT_LOCALTIME))
197                 utc = opt & HWCLOCK_OPT_UTC;
198         else
199                 utc = check_utc();
200
201         if (opt & HWCLOCK_OPT_HCTOSYS) {
202                 to_sys_clock(utc);
203                 return 0;
204         }
205         if (opt & HWCLOCK_OPT_SYSTOHC) {
206                 from_sys_clock(utc);
207                 return 0;
208         }
209         /* default HWCLOCK_OPT_SHOW */
210         show_clock(utc);
211         return 0;
212 }