xref: /illumos-gate/usr/src/cmd/rtc/rtc.c (revision 03100a6332bd4edc7a53091fcf7c9a7131bcdaa7)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <sys/types.h>
33 #include <sys/sysi86.h>
34 #include <errno.h>
35 #include <time.h>
36 #include <string.h>
37 #include <sys/stat.h>
38 
39 static char *progname;
40 static char *zonefile = "/etc/rtc_config";
41 static FILE *zonefptr;
42 static char zone_info[256];
43 static char zone_lag[256];
44 static char tz[256] = "TZ=";
45 int debug = 0;
46 int lag;
47 int errors_ok = 0; /* allow "rtc no-args" to be quiet when not configured */
48 static time_t clock_val;
49 static char zone_comment[] =
50 	"#\n"
51 	"#	This file (%s) contains information used to manage the\n"
52 	"#	x86 real time clock hardware.  The hardware is kept in\n"
53 	"#	the machine's local time for compatibility with other x86\n"
54 	"#	operating systems.  This file is read by the kernel at\n"
55 	"#	boot time.  It is set and updated by the /usr/sbin/rtc\n"
56 	"#	command.  The 'zone_info' field designates the local\n"
57 	"#	time zone.  The 'zone_lag' field indicates the number\n"
58 	"#	of seconds between local time and Greenwich Mean Time.\n"
59 	"#\n";
60 
61 /*
62  *	Open the configuration file and extract the
63  *	zone_info and the zone_lag.  Return 0 if successful.
64  */
65 int
66 open_zonefile()
67 {
68 	char b[256], *s;
69 	int lag_hrs;
70 
71 	if ((zonefptr = fopen(zonefile, "r")) == NULL) {
72 		if (errors_ok == 0)
73 			(void) fprintf(stderr,
74 				"%s: cannot open %s: errno = %d\n",
75 				progname, zonefile, errno);
76 		return (1);
77 	}
78 
79 	for (;;) {
80 		if ((s = fgets(b, sizeof (b), zonefptr)) == NULL)
81 			break;
82 		if ((s = strchr(s, 'z')) == NULL)
83 			continue;
84 		if (strncmp(s, "zone_info", 9) == 0) {
85 			s += 9;
86 			while (*s != 0 && *s != '=')
87 				s++;
88 			if (*s == '=') {
89 				s++;
90 				while (*s != 0 && (*s == ' ' || *s == '\t'))
91 					s++;
92 				(void) strncpy(zone_info, s,
93 				    sizeof (zone_info));
94 				s = zone_info;
95 				while (*s != 0 && *s != '\n')
96 					s++;
97 				if (*s == '\n')
98 					*s = 0;
99 			}
100 		} else if (strncmp(s, "zone_lag", 8) == 0) {
101 			s += 8;
102 			while (*s != 0 && *s != '=')
103 				s++;
104 			if (*s == '=') {
105 				s++;
106 				while (*s != 0 && (*s == ' ' || *s == '\t'))
107 					s++;
108 				(void) strncpy(zone_lag, s, sizeof (zone_lag));
109 				s = zone_lag;
110 				while (*s != 0 && *s != '\n')
111 					s++;
112 				if (*s == '\n')
113 					*s = 0;
114 			}
115 		}
116 	}
117 	lag = atoi(zone_lag);
118 	lag_hrs = lag / 3600;
119 	if (zone_info[0] == 0) {
120 		(void) fprintf(stderr, "%s: zone_info field is invalid\n",
121 		    progname);
122 		zone_info[0] = 0;
123 		zone_lag[0] = 0;
124 		return (1);
125 	}
126 	if (zone_lag[0] == 0) {
127 		(void) fprintf(stderr, "%s: zone_lag field is invalid\n",
128 		    progname);
129 		zone_lag[0] = 0;
130 		return (1);
131 	}
132 	if ((lag_hrs < -24) || (lag_hrs > 24)) {
133 		(void) fprintf(stderr, "%s: a GMT lag of %d is out of range\n",
134 		    progname, lag_hrs);
135 		zone_info[0] = 0;
136 		zone_lag[0] = 0;
137 		return (1);
138 	}
139 	if (debug)
140 		(void) fprintf(stderr, "zone_info = %s,   zone_lag = %s\n",
141 		    zone_info, zone_lag);
142 	if (debug)
143 		(void) fprintf(stderr, "lag (decimal) is %d\n", lag);
144 
145 	(void) fclose(zonefptr);
146 	zonefptr = NULL;
147 	return (0);
148 }
149 
150 void
151 display_zone_string(void)
152 {
153 	if (open_zonefile() == 0)
154 		(void) printf("%s\n", zone_info);
155 	else
156 		(void) printf("GMT\n");
157 }
158 
159 long
160 set_zone(char *zone_string)
161 {
162 	struct tm *tm;
163 	long current_lag;
164 
165 	(void) umask(0022);
166 	if ((zonefptr = fopen(zonefile, "w")) == NULL) {
167 		(void) fprintf(stderr, "%s: cannot open %s: errno = %d\n",
168 			progname, zonefile, errno);
169 		return (0);
170 	}
171 
172 	tz[3] = 0;
173 	(void) strncat(tz, zone_string, 253);
174 	if (debug)
175 		(void) fprintf(stderr, "Time Zone string is '%s'\n", tz);
176 
177 	(void) putenv(tz);
178 	if (debug)
179 		(void) system("env | grep TZ");
180 
181 	(void) time(&clock_val);
182 
183 	tm = localtime(&clock_val);
184 	current_lag = tm->tm_isdst ? altzone : timezone;
185 	if (debug)
186 		(void) printf("%s DST.    Lag is %ld.\n", tm->tm_isdst ? "Is" :
187 		    "Is NOT",  tm->tm_isdst ? altzone : timezone);
188 
189 	(void) fprintf(zonefptr, zone_comment, zonefile);
190 	(void) fprintf(zonefptr, "zone_info=%s\n", zone_string);
191 	(void) fprintf(zonefptr, "zone_lag=%ld\n",
192 	    tm->tm_isdst ? altzone : timezone);
193 	(void) fclose(zonefptr);
194 	zonefptr = NULL;
195 	return (current_lag);
196 }
197 
198 void
199 correct_rtc_and_lag()
200 {
201 	struct tm *tm;
202 	long kernels_lag;
203 	long current_lag;
204 
205 	if (open_zonefile())
206 		return;
207 
208 	tz[3] = 0;
209 	(void) strncat(tz, zone_info, 253);
210 	if (debug)
211 		(void) fprintf(stderr, "Time Zone string is '%s'\n", tz);
212 
213 	(void) putenv(tz);
214 	if (debug)
215 		(void) system("env | grep TZ");
216 
217 	(void) time(&clock_val);
218 	tm = localtime(&clock_val);
219 	current_lag = tm->tm_isdst ? altzone : timezone;
220 
221 	if (current_lag != lag) {	/* if file is wrong */
222 		if (debug)
223 			(void) fprintf(stderr, "correcting file");
224 		(void) set_zone(zone_info);	/* then rewrite file */
225 	}
226 
227 	(void) sysi86(GGMTL, &kernels_lag);
228 	if (current_lag != kernels_lag) {
229 		if (debug)
230 			(void) fprintf(stderr, "correcting kernel's lag");
231 		(void) sysi86(SGMTL, current_lag);	/* correct the lag */
232 		(void) sysi86(WTODC);			/* set the rtc to */
233 							/* new local time */
234 	}
235 }
236 
237 void
238 initialize_zone(char *zone_string)
239 {
240 	long current_lag;
241 
242 	/* write the config file */
243 	current_lag = set_zone(zone_string);
244 
245 	/* correct the lag */
246 	(void) sysi86(SGMTL, current_lag);
247 
248 	/*
249 	 * set the unix time from the rtc,
250 	 * assuming the rtc was the correct
251 	 * local time.
252 	 */
253 	(void) sysi86(RTCSYNC);
254 }
255 
256 void
257 usage()
258 {
259 	static char Usage[] = "Usage:\n\
260 rtc [-c] [-z time_zone] [-?]\n";
261 
262 	(void) fprintf(stderr, Usage);
263 }
264 
265 void
266 verbose_usage()
267 {
268 	static char Usage1[] = "\
269 	Options:\n\
270 	    -c\t\tCheck and correct for daylight savings time rollover.\n\
271 	    -z [zone]\tRecord the zone info in the config file.\n";
272 
273 	(void) fprintf(stderr, Usage1);
274 }
275 
276 int
277 main(int argc, char *argv[])
278 {
279 	int c;
280 
281 	progname = argv[0];
282 
283 	if (argc == 1) {
284 		errors_ok = 1;
285 		display_zone_string();
286 	}
287 
288 	while ((c = getopt(argc, argv, "cz:d")) != EOF) {
289 		switch (c) {
290 		case 'c':
291 			correct_rtc_and_lag();
292 			continue;
293 		case 'z':
294 			initialize_zone(optarg);
295 			continue;
296 		case 'd':
297 			debug = 1;
298 			continue;
299 		case '?':
300 			verbose_usage();
301 			return (0);
302 		default:
303 			usage();
304 			return (1);
305 		}
306 	}
307 	return (0);
308 }
309