1 /*------------------------------------------------------------------------------
2 // emTimeZonesProc.c
3 //
4 // Copyright (C) 2008-2009,2017-2018 Oliver Hamann.
5 //
6 // Homepage: http://eaglemode.sourceforge.net/
7 //
8 // This program is free software: you can redistribute it and/or modify it under
9 // the terms of the GNU General Public License version 3 as published by the
10 // Free Software Foundation.
11 //
12 // This program is distributed in the hope that it will be useful, but WITHOUT
13 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 // FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
15 // more details.
16 //
17 // You should have received a copy of the GNU General Public License version 3
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 //----------------------------------------------------------------------------*/
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <errno.h>
25 #if defined(_WIN32)
26 # include <fcntl.h>
27 # include <io.h>
28 # include <windows.h>
29 #else
30 # include <time.h>
31 # include <unistd.h>
32 # include <sys/stat.h>
33 #endif
34
35
36 /* This is a child process because setenv("TZ",name) is not thread-safe. */
37
38
39 #if defined(_WIN32)
40 struct TzMapEntry {
41 char * tz;
42 char * winTz;
43 };
44 static struct TzMapEntry * tzMap=NULL;
45 static int tzMapElems=0;
46 #endif
47
48
49 #if defined(_WIN32)
compareTzMapEntries(const void * a,const void * b)50 static int compareTzMapEntries(const void * a, const void * b)
51 {
52 return strcmp(
53 ((const struct TzMapEntry*)a)->tz,
54 ((const struct TzMapEntry*)b)->tz
55 );
56 }
57 #endif
58
59
60 #if defined(_WIN32)
loadZone2WinTab(const char * zoneInfoDir,char * errBuf,int errBufSize)61 static int loadZone2WinTab(
62 const char * zoneInfoDir, char * errBuf, int errBufSize
63 )
64 {
65 char * path;
66 char * p, * tz, * winTz;
67 char buf[512];
68 FILE * f;
69 int tzMapSize;
70
71 path=malloc(strlen(zoneInfoDir)+256);
72 sprintf(path,"%s\\zone2win.tab",zoneInfoDir);
73 f=fopen(path,"r");
74 if (!f) {
75 snprintf(errBuf,errBufSize,"Failed to open %s: %s",path,strerror(errno));
76 errBuf[errBufSize-1]=0;
77 free(path);
78 return 0;
79 }
80 free(path);
81
82 tzMapSize=256;
83 tzMapElems=0;
84 tzMap=malloc(sizeof(struct TzMapEntry)*tzMapSize);
85
86 while (fgets(buf,sizeof(buf),f)) {
87 tz=NULL;
88 winTz=NULL;
89
90 p=strchr(buf,'#');
91 if (!p) p=buf+strlen(buf);
92 while (p>buf && (unsigned char)p[-1]<=32) p--;
93 *p=0;
94 p=buf;
95 while (*p && (unsigned char)*p<=32) p++;
96 if (*p) {
97 tz=p;
98 do { p++; } while ((unsigned char)*p>32);
99 if (*p) {
100 *p=0;
101 do { p++; } while (*p && (unsigned char)*p<=32);
102 if (*p) winTz=p;
103 }
104 }
105
106 if (tz && winTz) {
107 if (tzMapElems>=tzMapSize) {
108 tzMapSize+=256;
109 tzMap=realloc(tzMap,sizeof(struct TzMapEntry)*tzMapSize);
110 }
111 tzMap[tzMapElems].tz=strdup(tz);
112 tzMap[tzMapElems].winTz=strdup(winTz);
113 tzMapElems++;
114 }
115 }
116
117 fclose(f);
118
119 qsort(tzMap,tzMapElems,sizeof(struct TzMapEntry),compareTzMapEntries);
120
121 return 1;
122 }
123 #endif
124
125
126 #if defined(_WIN32)
getWinTz(const char * zoneInfoDir,const char * tz,char * errBuf,int errBufSize)127 static const char * getWinTz(
128 const char * zoneInfoDir, const char * tz, char * errBuf, int errBufSize
129 )
130 {
131 int i,j,k,l;
132
133 if (!tzMap) {
134 if (!loadZone2WinTab(zoneInfoDir,errBuf,errBufSize)) {
135 return 0;
136 }
137 }
138
139 i=0;
140 j=tzMapElems;
141 while (i<j) {
142 k=(i+j)/2;
143 l=strcmp(tzMap[k].tz,tz);
144 if (l<0) i=k+1;
145 else if (l>0) j=k;
146 else return tzMap[k].winTz;
147 }
148
149 snprintf(errBuf,errBufSize,"Windows time zone not configured");
150 errBuf[errBufSize-1]=0;
151 return NULL;
152 }
153 #endif
154
155
156 #if defined(_WIN32)
getWinTzInfo(const char * winTz,TIME_ZONE_INFORMATION * info,char * errBuf,int errBufSize)157 static int getWinTzInfo(
158 const char * winTz, TIME_ZONE_INFORMATION * info,
159 char * errBuf, int errBufSize
160 )
161 {
162 struct Tzi {
163 LONG Bias;
164 LONG StandardBias;
165 LONG DaylightBias;
166 SYSTEMTIME StandardDate;
167 SYSTEMTIME DaylightDate;
168 };
169
170 char * key;
171 HKEY hk;
172 struct Tzi tzi;
173 DWORD d,tziSize;
174
175 key=malloc(strlen(winTz)+256);
176 sprintf(key,"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\%s",winTz);
177
178 d=RegOpenKeyEx(HKEY_LOCAL_MACHINE,key,0,KEY_QUERY_VALUE,&hk);
179 if (d != ERROR_SUCCESS) {
180 snprintf(errBuf,errBufSize,"RegOpenKeyEx failed for \"%s\": 0x%lX",winTz,(long)d);
181 errBuf[errBufSize-1]=0;
182 free(key);
183 return 0;
184 }
185
186 tziSize=sizeof(tzi);
187 d=RegQueryValueEx(hk,"TZI",NULL,NULL,(BYTE*)&tzi,&tziSize);
188 if (d!=ERROR_SUCCESS) {
189 snprintf(errBuf,errBufSize,"RegQueryValueEx failed for TZI in \"%s\": 0x%lX",winTz,(long)d);
190 errBuf[errBufSize-1]=0;
191 RegCloseKey(hk);
192 free(key);
193 return 0;
194 }
195 if (tziSize!=sizeof(tzi)) {
196 snprintf(errBuf,errBufSize,"Reg TZI value too short for %s",winTz);
197 errBuf[errBufSize-1]=0;
198 RegCloseKey(hk);
199 free(key);
200 return 0;
201 }
202
203 memset(info,0,sizeof(TIME_ZONE_INFORMATION));
204 info->Bias=tzi.Bias;
205 info->DaylightBias=tzi.DaylightBias;
206 info->DaylightDate=tzi.DaylightDate;
207 info->StandardBias=tzi.StandardBias;
208 info->StandardDate=tzi.StandardDate;
209
210 RegCloseKey(hk);
211 free(key);
212
213 return 1;
214 }
215 #endif
216
217
218 #if !defined(_WIN32)
checkTzFile(const char * zoneInfoDir,const char * tz)219 static int checkTzFile(const char * zoneInfoDir, const char * tz)
220 {
221 char * path;
222 struct stat st;
223 int sr;
224
225 path=malloc(strlen(zoneInfoDir)+strlen(tz)+2);
226 strcpy(path,zoneInfoDir);
227 strcat(path,"/");
228 strcat(path,tz);
229 sr=stat(path,&st);
230 free(path);
231 return sr==0 && (st.st_mode&S_IFMT)==S_IFREG;
232 }
233 #endif
234
235
236 #if !defined(_WIN32)
checkTzFileCached(const char * zoneInfoDir,const char * tz)237 static int checkTzFileCached(const char * zoneInfoDir, const char * tz)
238 {
239 struct cacheEntry {
240 char * tz;
241 int result;
242 };
243 static struct cacheEntry * * cache = NULL;
244 static int cacheElems = 0;
245 static int cacheSize = 0;
246 int i,j,k,l;
247
248 i=0;
249 j=cacheElems;
250 while (i<j) {
251 k=(i+j)/2;
252 l=strcmp(cache[k]->tz,tz);
253 if (l<0) i=k+1;
254 else if (l>0) j=k;
255 else return cache[k]->result;
256 }
257 if (cacheElems>=cacheSize) {
258 cacheSize+=256;
259 if (cache) cache=realloc(cache,sizeof(struct cacheEntry*)*cacheSize);
260 else cache=malloc(sizeof(struct cacheEntry*)*cacheSize);
261 }
262 if (i<cacheElems) {
263 memmove(cache+i+1,cache+i,sizeof(struct cacheEntry*)*(cacheElems-i));
264 }
265 cacheElems++;
266 cache[i]=malloc(sizeof(struct cacheEntry));
267 cache[i]->tz=strdup(tz);
268 cache[i]->result=checkTzFile(zoneInfoDir,tz);
269 return cache[i]->result;
270 }
271 #endif
272
273
274 #if defined(_WIN32)
275 static SYSTEMTIME tzBaseTime;
276 #else
277 static time_t tzBaseTime;
278 #endif
279
280
setTzBaseTimeNow()281 void setTzBaseTimeNow()
282 {
283 #if defined(_WIN32)
284 GetSystemTime(&tzBaseTime);
285 #else
286 tzBaseTime=time(NULL);
287 #endif
288 }
289
290
setTzBaseTime(int year,int month,int day,int hour,int minute,int second)291 void setTzBaseTime(int year, int month, int day, int hour, int minute, int second)
292 {
293 #if defined(_WIN32)
294 memset(&tzBaseTime,0,sizeof(tzBaseTime));
295 tzBaseTime.wYear=(WORD)year;
296 tzBaseTime.wMonth=(WORD)month;
297 tzBaseTime.wDay=(WORD)day;
298 tzBaseTime.wHour=(WORD)hour;
299 tzBaseTime.wMinute=(WORD)minute;
300 tzBaseTime.wSecond=(WORD)second;
301 #else
302 struct tm tm;
303
304 memset(&tm,0,sizeof(struct tm));
305 tm.tm_year=year-1900;
306 tm.tm_mon=month-1;
307 tm.tm_mday=day;
308 tm.tm_hour=hour;
309 tm.tm_min=minute;
310 tm.tm_sec=second;
311
312 if (setenv("TZ","",1)!=0) {
313 fprintf(stderr,"setenv failed: %s\n",strerror(errno));
314 exit(255);
315 }
316
317 tzBaseTime=mktime(&tm);
318 #endif
319 }
320
321
getTzTime(const char * zoneInfoDir,const char * tz,int * pYear,int * pMonth,int * pDay,int * pWDay,int * pHour,int * pMinute,int * pSecond,char * errBuf,int errBufSize)322 int getTzTime(
323 const char * zoneInfoDir, const char * tz,
324 int * pYear, int * pMonth, int * pDay, int * pWDay,
325 int * pHour, int * pMinute, int * pSecond,
326 char * errBuf, int errBufSize
327 )
328 {
329 #if defined(_WIN32)
330 TIME_ZONE_INFORMATION tzInfo;
331 const char * winTz;
332 SYSTEMTIME st;
333
334 winTz=getWinTz(zoneInfoDir,tz,errBuf,errBufSize);
335 if (!winTz) {
336 return 0;
337 }
338
339 if (!getWinTzInfo(winTz,&tzInfo,errBuf,errBufSize)) {
340 return 0;
341 }
342
343 if (!SystemTimeToTzSpecificLocalTime(&tzInfo,&tzBaseTime,&st)) {
344 snprintf(errBuf,errBufSize,"Time conversion failure (0x%lX)",(long)GetLastError());
345 errBuf[errBufSize-1]=0;
346 return 0;
347 }
348
349 *pYear =st.wYear;
350 *pMonth =st.wMonth;
351 *pDay =st.wDay;
352 *pWDay =st.wDayOfWeek;
353 *pHour =st.wHour;
354 *pMinute=st.wMinute;
355 *pSecond=st.wSecond;
356 return 1;
357 #else
358 struct tm * ptm;
359
360 if (!checkTzFileCached(zoneInfoDir,tz)) {
361 snprintf(errBuf,errBufSize,"Could not find %s/%s",zoneInfoDir,tz);
362 errBuf[errBufSize-1]=0;
363 return 0;
364 }
365 if (setenv("TZ",tz,1)!=0) {
366 snprintf(errBuf,errBufSize,"setenv failed: %s",strerror(errno));
367 errBuf[errBufSize-1]=0;
368 return 0;
369 }
370 ptm=localtime(&tzBaseTime);
371 if (!ptm) {
372 snprintf(errBuf,errBufSize,"Time conversion failure");
373 errBuf[errBufSize-1]=0;
374 return 0;
375 }
376 *pYear =ptm->tm_year+1900;
377 *pMonth =ptm->tm_mon+1;
378 *pDay =ptm->tm_mday;
379 *pWDay =ptm->tm_wday;
380 *pHour =ptm->tm_hour;
381 *pMinute=ptm->tm_min;
382 *pSecond=ptm->tm_sec;
383 return 1;
384 #endif
385 }
386
387
handleRequest(const char * zoneInfoDir,const char * request,char * replyBuf,int replyBufSize)388 void handleRequest(
389 const char * zoneInfoDir, const char * request,
390 char * replyBuf, int replyBufSize
391 )
392 {
393 int year, month, day, wDay, hour, minute, second, n;
394 const char * p;
395 char errBuf[256];
396
397 if (strncmp(request,"test#",5)==0 && (p=strchr(request+5,'#'))!=NULL) {
398 n=sscanf(request+5,"%d-%d-%d %d:%d:%d",&year,&month,&day,&hour,&minute,&second);
399 if (n!=6) {
400 snprintf(replyBuf,replyBufSize,"ERROR: Failed to parse test command\n");
401 replyBuf[replyBufSize-1]=0;
402 return;
403 }
404 setTzBaseTime(year,month,day,hour,minute,second);
405 n=snprintf(replyBuf,replyBufSize,"%s#",request+5);
406 if (n>replyBufSize) n=replyBufSize;
407 replyBuf+=n;
408 replyBufSize-=n;
409 request=p+1;
410 }
411 else {
412 setTzBaseTimeNow();
413 }
414
415 if (
416 getTzTime(
417 zoneInfoDir,request,
418 &year,&month,&day,&wDay,&hour,&minute,&second,
419 errBuf,sizeof(errBuf)
420 )
421 ) {
422 snprintf(
423 replyBuf,replyBufSize,
424 "%d-%d-%d %d %d:%d:%d\n",
425 year,month,day,wDay,hour,minute,second
426 );
427 replyBuf[replyBufSize-1]=0;
428 }
429 else {
430 snprintf(replyBuf,replyBufSize,"ERROR: %s\n",errBuf);
431 replyBuf[replyBufSize-1]=0;
432 }
433 }
434
435
tzServe(int argc,char * argv[])436 int tzServe(int argc, char * argv[])
437 {
438 static const int maxReplySize=256;
439 const char * zoneInfoDir;
440 char * rBuf, * wBuf, * request;
441 int fdIn, fdOut, rBufSize, wBufSize, rBufFill, wBufFill, i, j;
442
443 if (argc!=2) {
444 fprintf(stderr,"%s: Illegal arguments\n",argv[0]);
445 return 1;
446 }
447 zoneInfoDir=argv[1];
448
449 fdIn=fileno(stdin);
450 fdOut=fileno(stdout);
451 rBufSize=65536;
452 wBufSize=65536;
453 rBuf=(char*)malloc(rBufSize);
454 wBuf=(char*)malloc(wBufSize);
455 rBufFill=0;
456 wBufFill=0;
457 for (;;) {
458 if (rBufFill<rBufSize) {
459 i=(int)read(fdIn,rBuf+rBufFill,rBufSize-rBufFill);
460 if (i<=0) break;
461 rBufFill+=i;
462 }
463 for (i=0, j=0; i<rBufFill; i++) {
464 if (rBuf[i]!=0x0a && rBuf[i]!=0x0d) continue;
465 if (i==j) { j++; continue; }
466 rBuf[i]=0;
467 request=rBuf+j;
468 j=i+1;
469 while (wBufSize-wBufFill<maxReplySize) {
470 wBufSize*=2;
471 wBuf=(char*)realloc(wBuf,wBufSize);
472 }
473 handleRequest(zoneInfoDir,request,wBuf+wBufFill,maxReplySize);
474 wBufFill+=strlen(wBuf+wBufFill);
475 }
476 if (j==0 && rBufFill>=rBufSize) {
477 rBufSize*=2;
478 rBuf=(char*)realloc(rBuf,rBufSize);
479 continue;
480 }
481 rBufFill-=j;
482 if (rBufFill>0) memmove(rBuf,rBuf+j,rBufFill);
483 if (wBufFill>0) {
484 i=(int)write(fdOut,wBuf,wBufFill);
485 if (i<=0) break;
486 wBufFill-=i;
487 if (wBufFill>0) memmove(wBuf,wBuf+i,wBufFill);
488 }
489 }
490 free(rBuf);
491 free(wBuf);
492 return 0;
493 }
494
495
496 #ifdef _WIN32
tzServeThreadProc(LPVOID data)497 static DWORD WINAPI tzServeThreadProc(LPVOID data)
498 {
499 return tzServe(__argc,__argv);
500 }
501 #endif
502
503
main(int argc,char * argv[])504 int main(int argc, char * argv[])
505 {
506 #ifdef _WIN32
507 HANDLE hdl;
508 DWORD d;
509 MSG msg;
510
511 setmode(fileno(stdout),O_BINARY);
512 setmode(fileno(stdin),O_BINARY);
513 setbuf(stderr,NULL);
514
515 hdl=CreateThread(NULL,0,tzServeThreadProc,NULL,0,&d);
516 do {
517 while (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) {
518 if (msg.message==WM_QUIT) ExitProcess(143);
519 }
520 d=MsgWaitForMultipleObjects(1,&hdl,FALSE,INFINITE,QS_ALLPOSTMESSAGE);
521 } while(d==WAIT_OBJECT_0+1);
522 WaitForSingleObject(hdl,INFINITE);
523 GetExitCodeThread(hdl,&d);
524 return d;
525 #else
526 return tzServe(argc,argv);
527 #endif
528 }
529