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 }