hwclock: size optimizations
[platform/upstream/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         if (ioctl(rtc, RTC_RD_TIME, &tm) < 0)
63                 bb_perror_msg_and_die("cannot read time from RTC");
64         tm.tm_isdst = -1; /* not known */
65
66         close(rtc);
67
68         if (utc) {
69                 oldtz = getenv("TZ");
70                 setenv("TZ", "UTC 0", 1);
71                 tzset();
72         }
73
74         t = mktime(&tm);
75
76         if (utc) {
77                 if (oldtz)
78                         setenv("TZ", oldtz, 1);
79                 else
80                         unsetenv("TZ");
81                 tzset();
82         }
83         return t;
84 }
85
86 static void write_rtc(time_t t, int utc)
87 {
88         struct tm tm;
89         int rtc = xopen_rtc(O_WRONLY);
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 void show_clock(int utc)
101 {
102         //struct tm *ptm;
103         time_t t;
104         char *cp;
105
106         t = read_rtc(utc);
107         //ptm = localtime(&t);  /* Sets 'tzname[]' */
108
109         cp = ctime(&t);
110         if (cp[0])
111                 cp[strlen(cp) - 1] = '\0';
112
113         //printf("%s  %.6f seconds %s\n", cp, 0.0, utc ? "" : (ptm->tm_isdst ? tzname[1] : tzname[0]));
114         printf("%s  0.000000 seconds\n", cp);
115 }
116
117 static void to_sys_clock(int utc)
118 {
119         struct timeval tv;
120         const struct timezone tz = { timezone/60 - 60*daylight, 0 };
121
122         tv.tv_sec = read_rtc(utc);
123         tv.tv_usec = 0;
124         if (settimeofday(&tv, &tz))
125                 bb_perror_msg_and_die("settimeofday() failed");
126 }
127
128 static void from_sys_clock(int utc)
129 {
130         struct timeval tv;
131
132         gettimeofday(&tv, NULL);
133         //if (gettimeofday(&tv, NULL))
134         //      bb_perror_msg_and_die("gettimeofday() failed");
135         write_rtc(tv.tv_sec, utc);
136 }
137
138 #ifdef CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS
139 # define ADJTIME_PATH "/var/lib/hwclock/adjtime"
140 #else
141 # define ADJTIME_PATH "/etc/adjtime"
142 #endif
143 static int check_utc(void)
144 {
145         int utc = 0;
146         FILE *f = fopen(ADJTIME_PATH, "r");
147
148         if (f) {
149                 RESERVE_CONFIG_BUFFER(buffer, 128);
150
151                 while (fgets(buffer, sizeof(buffer), f)) {
152                         int len = strlen(buffer);
153
154                         while (len && isspace(buffer[len - 1]))
155                                 len--;
156
157                         buffer[len] = 0;
158
159                         if (strncmp(buffer, "UTC", 3) == 0) {
160                                 utc = 1;
161                                 break;
162                         }
163                 }
164                 fclose(f);
165                 RELEASE_CONFIG_BUFFER(buffer);
166         }
167         return utc;
168 }
169
170 #define HWCLOCK_OPT_LOCALTIME   0x01
171 #define HWCLOCK_OPT_UTC         0x02
172 #define HWCLOCK_OPT_SHOW        0x04
173 #define HWCLOCK_OPT_HCTOSYS     0x08
174 #define HWCLOCK_OPT_SYSTOHC     0x10
175 #define HWCLOCK_OPT_RTCFILE     0x20
176
177 int hwclock_main(int argc, char **argv);
178 int hwclock_main(int argc, char **argv)
179 {
180         unsigned opt;
181         int utc;
182
183 #if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS
184         static const struct option hwclock_long_options[] = {
185                 { "localtime", 0, 0, 'l' },
186                 { "utc",       0, 0, 'u' },
187                 { "show",      0, 0, 'r' },
188                 { "hctosys",   0, 0, 's' },
189                 { "systohc",   0, 0, 'w' },
190                 { "file",      1, 0, 'f' },
191                 { 0,           0, 0, 0 }
192         };
193         applet_long_options = hwclock_long_options;
194 #endif
195         opt_complementary = "?:r--ws:w--rs:s--wr:l--u:u--l";
196         opt = getopt32(argc, argv, "lurswf:", &rtcname);
197
198         /* If -u or -l wasn't given check if we are using utc */
199         if (opt & (HWCLOCK_OPT_UTC | HWCLOCK_OPT_LOCALTIME))
200                 utc = opt & HWCLOCK_OPT_UTC;
201         else
202                 utc = check_utc();
203
204         if (opt & HWCLOCK_OPT_HCTOSYS) {
205                 to_sys_clock(utc);
206                 return 0;
207         }
208         if (opt & HWCLOCK_OPT_SYSTOHC) {
209                 from_sys_clock(utc);
210                 return 0;
211         }
212         /* default HWCLOCK_OPT_SHOW */
213         show_clock(utc);
214         return 0;
215 }