]>
Commit | Line | Data |
---|---|---|
9c8434f6 | 1 | /* |
9c8434f6 | 2 | * Windows support |
3 | * AUTHOR: Guido Serassio <serassio@squid-cache.org> | |
4 | * inspired by previous work by Romeo Anghelache & Eric Stern. | |
5 | * | |
6 | * SQUID Web Proxy Cache http://www.squid-cache.org/ | |
7 | * ---------------------------------------------------------- | |
8 | * | |
9 | * Squid is the result of efforts by numerous individuals from | |
10 | * the Internet community; see the CONTRIBUTORS file for full | |
11 | * details. Many organizations have provided support for Squid's | |
12 | * development; see the SPONSORS file for full details. Squid is | |
13 | * Copyrighted (C) 2001 by the Regents of the University of | |
14 | * California; see the COPYRIGHT file for full details. Squid | |
15 | * incorporates software developed and/or copyrighted by other | |
16 | * sources; see the CREDITS file for full details. | |
17 | * | |
18 | * This program is free software; you can redistribute it and/or modify | |
19 | * it under the terms of the GNU General Public License as published by | |
20 | * the Free Software Foundation; either version 2 of the License, or | |
21 | * (at your option) any later version. | |
26ac0430 | 22 | * |
9c8434f6 | 23 | * This program is distributed in the hope that it will be useful, |
24 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
25 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
26 | * GNU General Public License for more details. | |
26ac0430 | 27 | * |
9c8434f6 | 28 | * You should have received a copy of the GNU General Public License |
29 | * along with this program; if not, write to the Free Software | |
30 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. | |
31 | * | |
32 | */ | |
33 | ||
582c2af2 | 34 | #include "squid.h" |
91d63ce7 AJ |
35 | #include "Debug.h" |
36 | #include "globals.h" | |
cf3edd6f | 37 | #include "protos.h" |
91d63ce7 | 38 | #include "SquidConfig.h" |
9c8434f6 | 39 | |
7aa9bb3e | 40 | #if _SQUID_WINDOWS_ |
9c8434f6 | 41 | #ifndef _MSWSOCK_ |
42 | #include <mswsock.h> | |
43 | #endif | |
44 | #include <process.h> | |
45 | #if defined(_MSC_VER) /* Microsoft C Compiler ONLY */ | |
46 | #include <crtdbg.h> | |
47 | #endif | |
48 | #endif | |
49 | ||
cf3edd6f FC |
50 | /* forward declarations */ |
51 | static void WIN32_Exit(void); | |
9c8434f6 | 52 | static unsigned int GetOSVersion(); |
53 | void WIN32_svcstatusupdate(DWORD, DWORD); | |
54 | void WINAPI WIN32_svcHandler(DWORD); | |
91d63ce7 AJ |
55 | extern "C" void WINAPI SquidWinSvcMain(DWORD, char **); |
56 | ||
9c8434f6 | 57 | #if USE_WIN32_SERVICE |
91d63ce7 | 58 | static void WIN32_Abort(int); |
9c8434f6 | 59 | static int WIN32_StoreKey(const char *, DWORD, unsigned char *, int); |
60 | static int WIN32_create_key(void); | |
61 | static void WIN32_build_argv (char *); | |
62 | #endif | |
9c8434f6 | 63 | |
9c8434f6 | 64 | #if defined(_MSC_VER) /* Microsoft C Compiler ONLY */ |
65 | void Squid_Win32InvalidParameterHandler(const wchar_t*, const wchar_t*, const wchar_t*, unsigned int, uintptr_t); | |
66 | #endif | |
67 | static int Win32SockInit(void); | |
68 | static void Win32SockCleanup(void); | |
69 | SQUIDCEXTERN LPCRITICAL_SECTION dbg_mutex; | |
70 | void WIN32_ExceptionHandlerCleanup(void); | |
71 | static int s_iInitCount = 0; | |
353942c5 | 72 | static HANDLE NotifyAddrChange_thread = INVALID_HANDLE_VALUE; |
9c8434f6 | 73 | |
babd1fd5 | 74 | #undef NotifyAddrChange |
75 | typedef DWORD(WINAPI * PFNotifyAddrChange) (OUT PHANDLE, IN LPOVERLAPPED); | |
76 | #define NOTIFYADDRCHANGE "NotifyAddrChange" | |
9c8434f6 | 77 | |
78 | #if USE_WIN32_SERVICE | |
79 | static SERVICE_STATUS svcStatus; | |
80 | static SERVICE_STATUS_HANDLE svcHandle; | |
81 | static int WIN32_argc; | |
82 | static char ** WIN32_argv; | |
83 | static char * WIN32_module_name; | |
84 | ||
d2b4d181 | 85 | #define VENDOR "squid-cache.org" |
9c8434f6 | 86 | static char VENDORString[] = VENDOR; |
d2b4d181 | 87 | #define SOFTWARENAME PACKAGE_NAME |
9c8434f6 | 88 | static char SOFTWARENAMEString[] = SOFTWARENAME; |
9c8434f6 | 89 | #define SOFTWARE "SOFTWARE" |
90 | static char SOFTWAREString[] = SOFTWARE; | |
91 | #define COMMANDLINE "CommandLine" | |
92 | #define CONFIGFILE "ConfigFile" | |
93 | #undef ChangeServiceConfig2 | |
94 | typedef BOOL (WINAPI * PFChangeServiceConfig2) (SC_HANDLE, DWORD, LPVOID); | |
95 | #ifdef UNICODE | |
96 | #define CHANGESERVICECONFIG2 "ChangeServiceConfig2W" | |
97 | #else | |
98 | #define CHANGESERVICECONFIG2 "ChangeServiceConfig2A" | |
99 | #endif | |
100 | static SC_ACTION Squid_SCAction[] = { { SC_ACTION_RESTART, 60000 } }; | |
101 | static char Squid_ServiceDescriptionString[] = SOFTWARENAME " " VERSION " WWW Proxy Server"; | |
102 | static SERVICE_DESCRIPTION Squid_ServiceDescription = { Squid_ServiceDescriptionString }; | |
103 | static SERVICE_FAILURE_ACTIONS Squid_ServiceFailureActions = { INFINITE, NULL, NULL, 1, Squid_SCAction }; | |
d2b4d181 | 104 | static char REGKEY[256]=SOFTWARE"\\"VENDOR"\\"SOFTWARENAME"\\"; |
9c8434f6 | 105 | static char *keys[] = { |
26ac0430 AJ |
106 | SOFTWAREString, /* key[0] */ |
107 | VENDORString, /* key[1] */ | |
108 | SOFTWARENAMEString, /* key[2] */ | |
d2b4d181 GS |
109 | NULL, /* key[3] */ |
110 | NULL /* key[4] */ | |
26ac0430 | 111 | }; |
91d63ce7 AJ |
112 | |
113 | static int Squid_Aborting = 0; | |
9c8434f6 | 114 | #endif |
115 | ||
116 | /* ====================================================================== */ | |
117 | /* LOCAL FUNCTIONS */ | |
118 | /* ====================================================================== */ | |
119 | ||
120 | #if USE_WIN32_SERVICE | |
121 | static int | |
122 | WIN32_create_key(void) | |
123 | { | |
124 | int index; | |
125 | HKEY hKey; | |
126 | HKEY hKeyNext; | |
127 | int retval; | |
128 | LONG rv; | |
129 | ||
130 | hKey = HKEY_LOCAL_MACHINE; | |
131 | index = 0; | |
132 | retval = 0; | |
133 | ||
134 | /* Walk the tree, creating at each stage if necessary */ | |
135 | ||
136 | while (keys[index]) { | |
137 | unsigned long result; | |
138 | rv = RegCreateKeyEx(hKey, keys[index], /* subkey */ | |
139 | 0, /* reserved */ | |
140 | NULL, /* class */ | |
141 | REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKeyNext, &result); | |
142 | ||
143 | if (rv != ERROR_SUCCESS) { | |
144 | fprintf(stderr, "RegCreateKeyEx(%s),%d\n", keys[index], (int) rv); | |
145 | retval = -4; | |
146 | } | |
147 | ||
148 | /* Close the old key */ | |
149 | rv = RegCloseKey(hKey); | |
150 | ||
151 | if (rv != ERROR_SUCCESS) { | |
152 | fprintf(stderr, "RegCloseKey %d\n", (int) rv); | |
153 | ||
154 | if (retval == 0) { | |
155 | /* Keep error status from RegCreateKeyEx, if any */ | |
156 | retval = -4; | |
157 | } | |
158 | } | |
159 | ||
160 | if (retval) { | |
161 | break; | |
162 | } | |
163 | ||
164 | hKey = hKeyNext; | |
14942edd | 165 | ++index; |
9c8434f6 | 166 | } |
167 | ||
168 | if (keys[index] == NULL) { | |
169 | /* Close the final key we opened, if we walked the entire | |
170 | * tree | |
171 | */ | |
172 | rv = RegCloseKey(hKey); | |
173 | ||
174 | if (rv != ERROR_SUCCESS) { | |
175 | fprintf(stderr, "RegCloseKey %d\n", (int) rv); | |
176 | ||
177 | if (retval == 0) { | |
178 | /* Keep error status from RegCreateKeyEx, if any */ | |
179 | retval = -4; | |
180 | } | |
181 | } | |
182 | } | |
183 | ||
184 | return retval; | |
185 | } | |
186 | ||
187 | static int | |
188 | WIN32_StoreKey(const char *key, DWORD type, unsigned char *value, | |
189 | int value_size) | |
190 | { | |
191 | LONG rv; | |
192 | HKEY hKey; | |
193 | int retval; | |
194 | ||
195 | rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGKEY, 0, KEY_WRITE, &hKey); | |
196 | ||
197 | if (rv == ERROR_FILE_NOT_FOUND) { | |
198 | /* Key could not be opened -- try to create it | |
199 | */ | |
200 | ||
201 | if (WIN32_create_key() < 0) { | |
202 | /* Creation failed (error already reported) */ | |
203 | return -4; | |
204 | } | |
205 | ||
206 | /* Now it has been created we should be able to open it | |
207 | */ | |
208 | rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGKEY, 0, KEY_WRITE, &hKey); | |
209 | ||
210 | if (rv == ERROR_FILE_NOT_FOUND) { | |
211 | fprintf(stderr, "Registry does not contain key %s after creation\n", | |
212 | REGKEY); | |
213 | return -1; | |
214 | } | |
215 | } | |
216 | ||
217 | if (rv != ERROR_SUCCESS) { | |
218 | fprintf(stderr, "RegOpenKeyEx HKLM\\%s, %d\n", REGKEY, (int) rv); | |
219 | return -4; | |
220 | } | |
221 | ||
222 | /* Now set the value and data */ | |
223 | rv = RegSetValueEx(hKey, key, /* value key name */ | |
224 | 0, /* reserved */ | |
225 | type, /* type */ | |
226 | value, /* value data */ | |
227 | (DWORD) value_size); /* for size of "value" */ | |
228 | ||
229 | retval = 0; /* Return value */ | |
230 | ||
231 | if (rv != ERROR_SUCCESS) { | |
232 | fprintf(stderr, "RegQueryValueEx(key %s),%d\n", key, (int) rv); | |
233 | retval = -4; | |
234 | } else { | |
235 | fprintf(stderr, "Registry stored HKLM\\%s\\%s value %s\n", | |
236 | REGKEY, | |
237 | key, | |
238 | type == REG_SZ ? value : (unsigned char *) "(not displayable)"); | |
239 | } | |
240 | ||
241 | /* Make sure we close the key even if there was an error storing | |
242 | * the data | |
243 | */ | |
244 | rv = RegCloseKey(hKey); | |
245 | ||
246 | if (rv != ERROR_SUCCESS) { | |
247 | fprintf(stderr, "RegCloseKey HKLM\\%s, %d\n", REGKEY, (int) rv); | |
248 | ||
249 | if (retval == 0) { | |
250 | /* Keep error status from RegQueryValueEx, if any */ | |
251 | retval = -4; | |
252 | } | |
253 | } | |
254 | ||
255 | return retval; | |
256 | } | |
257 | ||
258 | /* Build argv, argc from string passed from Windows. */ | |
259 | static void WIN32_build_argv(char *cmd) | |
260 | { | |
261 | int argvlen = 0; | |
262 | char *word; | |
263 | ||
264 | WIN32_argc = 1; | |
265 | WIN32_argv = (char **) xmalloc ((WIN32_argc+1) * sizeof (char *)); | |
266 | WIN32_argv[0]=xstrdup(WIN32_module_name); | |
267 | /* Scan command line until there is nothing left. */ | |
268 | ||
269 | while (*cmd) { | |
270 | /* Ignore spaces */ | |
271 | ||
272 | if (xisspace(*cmd)) { | |
14942edd | 273 | ++cmd; |
9c8434f6 | 274 | continue; |
275 | } | |
276 | ||
277 | /* Found the beginning of an argument. */ | |
278 | word = cmd; | |
279 | ||
280 | while (*cmd) { | |
14942edd | 281 | ++cmd; /* Skip over this character */ |
9c8434f6 | 282 | |
283 | if (xisspace(*cmd)) /* End of argument if space */ | |
284 | break; | |
285 | } | |
286 | ||
287 | if (*cmd) | |
288 | *cmd++ = '\0'; /* Terminate `word' */ | |
289 | ||
290 | /* See if we need to allocate more space for argv */ | |
291 | if (WIN32_argc >= argvlen) { | |
292 | argvlen = WIN32_argc + 1; | |
293 | WIN32_argv = (char **) xrealloc (WIN32_argv, (1 + argvlen) * sizeof (char *)); | |
294 | } | |
295 | ||
296 | /* Add word to argv file. */ | |
297 | WIN32_argv[WIN32_argc++] = word; | |
298 | } | |
299 | ||
300 | WIN32_argv[WIN32_argc] = NULL; | |
301 | } | |
302 | ||
303 | #endif /* USE_WIN32_SERVICE */ | |
304 | ||
305 | static unsigned int | |
306 | GetOSVersion() | |
307 | { | |
cda5938d | 308 | OSVERSIONINFOEX osvi; |
309 | BOOL bOsVersionInfoEx; | |
9c8434f6 | 310 | |
311 | safe_free(WIN32_OS_string); | |
cda5938d | 312 | memset(&osvi, '\0', sizeof(OSVERSIONINFOEX)); |
313 | /* Try calling GetVersionEx using the OSVERSIONINFOEX structure. | |
314 | * If that fails, try using the OSVERSIONINFO structure. | |
315 | */ | |
9c8434f6 | 316 | |
cda5938d | 317 | osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); |
9c8434f6 | 318 | |
cda5938d | 319 | if (!(bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO *) & osvi))) { |
26ac0430 AJ |
320 | osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); |
321 | if (!GetVersionEx((OSVERSIONINFO *) & osvi)) | |
322 | goto GetVerError; | |
cda5938d | 323 | } |
324 | switch (osvi.dwPlatformId) { | |
9c8434f6 | 325 | case VER_PLATFORM_WIN32_NT: |
26ac0430 AJ |
326 | if (osvi.dwMajorVersion <= 4) { |
327 | WIN32_OS_string = xstrdup("Windows NT"); | |
328 | return _WIN_OS_WINNT; | |
329 | } | |
330 | if ((osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion == 0)) { | |
331 | WIN32_OS_string = xstrdup("Windows 2000"); | |
332 | return _WIN_OS_WIN2K; | |
333 | } | |
334 | if ((osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion == 1)) { | |
335 | WIN32_OS_string = xstrdup("Windows XP"); | |
336 | return _WIN_OS_WINXP; | |
337 | } | |
338 | if ((osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion == 2)) { | |
339 | WIN32_OS_string = xstrdup("Windows Server 2003"); | |
340 | return _WIN_OS_WINNET; | |
341 | } | |
342 | if ((osvi.dwMajorVersion == 6) && (osvi.dwMinorVersion == 0)) { | |
343 | if (osvi.wProductType == VER_NT_WORKSTATION) | |
344 | WIN32_OS_string = xstrdup("Windows Vista"); | |
345 | else | |
346 | WIN32_OS_string = xstrdup("Windows Server 2008"); | |
347 | return _WIN_OS_WINLON; | |
348 | } | |
af6a12ee AJ |
349 | if ((osvi.dwMajorVersion == 6) && (osvi.dwMinorVersion == 1)) { |
350 | if (osvi.wProductType == VER_NT_WORKSTATION) | |
351 | WIN32_OS_string = xstrdup("Windows 7"); | |
352 | else | |
353 | WIN32_OS_string = xstrdup("Windows Server 2008 R2"); | |
354 | return _WIN_OS_WIN7; | |
355 | } | |
356 | if (((osvi.dwMajorVersion > 6)) || ((osvi.dwMajorVersion == 6) && (osvi.dwMinorVersion > 1))) { | |
357 | if (osvi.wProductType == VER_NT_WORKSTATION) | |
358 | WIN32_OS_string = xstrdup("Unknown Windows version, assuming Windows 7 capabilities"); | |
359 | else | |
360 | WIN32_OS_string = xstrdup("Unknown Windows version, assuming Windows Server 2008 R2 capabilities"); | |
361 | return _WIN_OS_WIN7; | |
362 | } | |
26ac0430 | 363 | break; |
9c8434f6 | 364 | case VER_PLATFORM_WIN32_WINDOWS: |
26ac0430 AJ |
365 | if ((osvi.dwMajorVersion == 4) && (osvi.dwMinorVersion == 0)) { |
366 | WIN32_OS_string = xstrdup("Windows 95"); | |
367 | return _WIN_OS_WIN95; | |
368 | } | |
369 | if ((osvi.dwMajorVersion == 4) && (osvi.dwMinorVersion == 10)) { | |
370 | WIN32_OS_string = xstrdup("Windows 98"); | |
371 | return _WIN_OS_WIN98; | |
372 | } | |
373 | if ((osvi.dwMajorVersion == 4) && (osvi.dwMinorVersion == 90)) { | |
374 | WIN32_OS_string = xstrdup("Windows Me"); | |
375 | return _WIN_OS_WINME; | |
376 | } | |
377 | break; | |
9c8434f6 | 378 | case VER_PLATFORM_WIN32s: |
26ac0430 AJ |
379 | WIN32_OS_string = xstrdup("Windows 3.1 with WIN32S"); |
380 | return _WIN_OS_WIN32S; | |
381 | break; | |
9c8434f6 | 382 | default: |
26ac0430 | 383 | break; |
9c8434f6 | 384 | } |
26ac0430 | 385 | GetVerError: |
9c8434f6 | 386 | WIN32_OS_string = xstrdup("Unknown Windows system"); |
387 | return _WIN_OS_UNKNOWN; | |
388 | } | |
389 | ||
390 | /* ====================================================================== */ | |
391 | /* PUBLIC FUNCTIONS */ | |
392 | /* ====================================================================== */ | |
393 | ||
91d63ce7 | 394 | #if USE_WIN32_SERVICE |
9c8434f6 | 395 | void |
396 | WIN32_Abort(int sig) | |
397 | { | |
9c8434f6 | 398 | svcStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; |
399 | svcStatus.dwServiceSpecificExitCode = 1; | |
9c8434f6 | 400 | Squid_Aborting = 1; |
401 | WIN32_Exit(); | |
402 | } | |
91d63ce7 | 403 | #endif |
9c8434f6 | 404 | |
babd1fd5 | 405 | void |
406 | WIN32_IpAddrChangeMonitorExit() | |
407 | { | |
408 | DWORD status = ERROR_SUCCESS; | |
409 | ||
b6696974 | 410 | if (NotifyAddrChange_thread != INVALID_HANDLE_VALUE) { |
26ac0430 AJ |
411 | TerminateThread(NotifyAddrChange_thread, status); |
412 | CloseHandle(NotifyAddrChange_thread); | |
babd1fd5 | 413 | } |
414 | } | |
415 | ||
9c8434f6 | 416 | void |
417 | WIN32_Exit() | |
418 | { | |
9c8434f6 | 419 | Win32SockCleanup(); |
9c8434f6 | 420 | #if USE_WIN32_SERVICE |
421 | ||
422 | if (WIN32_run_mode == _WIN_SQUID_RUN_MODE_SERVICE) { | |
423 | if (!Squid_Aborting) { | |
424 | svcStatus.dwCurrentState = SERVICE_STOPPED; | |
425 | SetServiceStatus(svcHandle, &svcStatus); | |
426 | } | |
427 | } | |
428 | ||
429 | #endif | |
9c8434f6 | 430 | if (dbg_mutex) |
431 | DeleteCriticalSection(dbg_mutex); | |
432 | ||
433 | WIN32_ExceptionHandlerCleanup(); | |
babd1fd5 | 434 | WIN32_IpAddrChangeMonitorExit(); |
9c8434f6 | 435 | _exit(0); |
436 | } | |
437 | ||
babd1fd5 | 438 | static DWORD WINAPI |
439 | WIN32_IpAddrChangeMonitor(LPVOID lpParam) | |
440 | { | |
441 | DWORD Result; | |
442 | HMODULE IPHLPAPIHandle; | |
443 | PFNotifyAddrChange NotifyAddrChange; | |
444 | ||
445 | if ((IPHLPAPIHandle = GetModuleHandle("IPHLPAPI")) == NULL) | |
26ac0430 | 446 | IPHLPAPIHandle = LoadLibrary("IPHLPAPI"); |
babd1fd5 | 447 | NotifyAddrChange = (PFNotifyAddrChange) GetProcAddress(IPHLPAPIHandle, NOTIFYADDRCHANGE); |
448 | ||
449 | while (1) { | |
26ac0430 AJ |
450 | Result = NotifyAddrChange(NULL, NULL); |
451 | if (Result != NO_ERROR) { | |
e0236918 | 452 | debugs(1, DBG_IMPORTANT, "NotifyAddrChange error " << Result); |
26ac0430 AJ |
453 | return 1; |
454 | } | |
e0236918 | 455 | debugs(1, DBG_IMPORTANT, "Notification of IP address change received, requesting Squid reconfiguration ..."); |
26ac0430 | 456 | reconfigure(SIGHUP); |
babd1fd5 | 457 | } |
458 | return 0; | |
459 | } | |
460 | ||
461 | DWORD | |
462 | WIN32_IpAddrChangeMonitorInit() | |
463 | { | |
464 | DWORD status = ERROR_SUCCESS; | |
465 | DWORD threadID = 0, ThrdParam = 0; | |
466 | ||
b6696974 | 467 | if ((WIN32_run_mode == _WIN_SQUID_RUN_MODE_SERVICE) && (Config.onoff.WIN32_IpAddrChangeMonitor)) { |
26ac0430 AJ |
468 | NotifyAddrChange_thread = CreateThread(NULL, 0, WIN32_IpAddrChangeMonitor, |
469 | &ThrdParam, 0, &threadID); | |
470 | if (NotifyAddrChange_thread == NULL) { | |
471 | status = GetLastError(); | |
472 | NotifyAddrChange_thread = INVALID_HANDLE_VALUE; | |
e0236918 | 473 | debugs(1, DBG_IMPORTANT, "Failed to start IP monitor thread."); |
26ac0430 AJ |
474 | } else |
475 | debugs(1, 2, "Starting IP monitor thread [" << threadID << "] ..."); | |
babd1fd5 | 476 | } |
477 | return status; | |
478 | } | |
babd1fd5 | 479 | |
9c8434f6 | 480 | int WIN32_Subsystem_Init(int * argc, char *** argv) |
481 | { | |
482 | #if defined(_MSC_VER) /* Microsoft C Compiler ONLY */ | |
483 | _invalid_parameter_handler oldHandler, newHandler; | |
484 | #endif | |
485 | ||
486 | WIN32_OS_version = GetOSVersion(); | |
487 | ||
488 | if ((WIN32_OS_version == _WIN_OS_UNKNOWN) || (WIN32_OS_version == _WIN_OS_WIN32S)) | |
489 | return 1; | |
490 | ||
491 | if (atexit(WIN32_Exit) != 0) | |
492 | return 1; | |
493 | ||
494 | #if defined(_MSC_VER) /* Microsoft C Compiler ONLY */ | |
495 | ||
496 | newHandler = Squid_Win32InvalidParameterHandler; | |
497 | ||
498 | oldHandler = _set_invalid_parameter_handler(newHandler); | |
499 | ||
500 | _CrtSetReportMode(_CRT_ASSERT, 0); | |
501 | ||
502 | #endif | |
503 | #if USE_WIN32_SERVICE | |
504 | ||
505 | if (WIN32_run_mode == _WIN_SQUID_RUN_MODE_SERVICE) { | |
506 | char path[512]; | |
507 | HKEY hndKey; | |
508 | ||
509 | if (signal(SIGABRT, WIN32_Abort) == SIG_ERR) | |
510 | return 1; | |
511 | ||
512 | /* Register the service Handler function */ | |
cf2155f2 | 513 | svcHandle = RegisterServiceCtrlHandler(service_name, WIN32_svcHandler); |
9c8434f6 | 514 | |
515 | if (svcHandle == 0) | |
516 | return 1; | |
517 | ||
518 | /* Set Process work dir to directory cointaining squid.exe */ | |
519 | GetModuleFileName(NULL, path, 512); | |
520 | ||
521 | WIN32_module_name=xstrdup(path); | |
522 | ||
523 | path[strlen(path) - 10] = '\0'; | |
524 | ||
525 | if (SetCurrentDirectory(path) == 0) | |
526 | return 1; | |
527 | ||
528 | safe_free(ConfigFile); | |
529 | ||
530 | /* get config file from Windows Registry */ | |
04f7fd38 | 531 | if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGKEY, 0, KEY_QUERY_VALUE, &hndKey) == ERROR_SUCCESS) { |
9c8434f6 | 532 | DWORD Type = 0; |
533 | DWORD Size = 0; | |
534 | LONG Result; | |
0dde2160 | 535 | Result = RegQueryValueEx(hndKey, CONFIGFILE, NULL, &Type, NULL, &Size); |
9c8434f6 | 536 | |
537 | if (Result == ERROR_SUCCESS && Size) { | |
538 | ConfigFile = static_cast<char *>(xmalloc(Size)); | |
0dde2160 | 539 | RegQueryValueEx(hndKey, CONFIGFILE, NULL, &Type, (unsigned char *)ConfigFile, &Size); |
9c8434f6 | 540 | } else |
541 | ConfigFile = xstrdup(DefaultConfigFile); | |
542 | ||
543 | Size = 0; | |
544 | ||
545 | Type = 0; | |
546 | ||
0dde2160 | 547 | Result = RegQueryValueEx(hndKey, COMMANDLINE, NULL, &Type, NULL, &Size); |
9c8434f6 | 548 | |
549 | if (Result == ERROR_SUCCESS && Size) { | |
550 | WIN32_Service_Command_Line = static_cast<char *>(xmalloc(Size)); | |
0dde2160 | 551 | RegQueryValueEx(hndKey, COMMANDLINE, NULL, &Type, (unsigned char *)WIN32_Service_Command_Line, &Size); |
9c8434f6 | 552 | } else |
553 | WIN32_Service_Command_Line = xstrdup(""); | |
554 | ||
555 | RegCloseKey(hndKey); | |
556 | } else { | |
557 | ConfigFile = xstrdup(DefaultConfigFile); | |
558 | WIN32_Service_Command_Line = xstrdup(""); | |
559 | } | |
560 | ||
561 | WIN32_build_argv(WIN32_Service_Command_Line); | |
562 | *argc = WIN32_argc; | |
563 | *argv = WIN32_argv; | |
564 | /* Set Service Status to SERVICE_START_PENDING */ | |
565 | svcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; | |
566 | svcStatus.dwCurrentState = SERVICE_START_PENDING; | |
567 | svcStatus.dwControlsAccepted = | |
568 | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; | |
569 | svcStatus.dwWin32ExitCode = 0; | |
570 | svcStatus.dwServiceSpecificExitCode = 0; | |
571 | svcStatus.dwCheckPoint = 0; | |
572 | svcStatus.dwWaitHint = 10000; | |
573 | SetServiceStatus(svcHandle, &svcStatus); | |
9c8434f6 | 574 | |
575 | _setmaxstdio(Squid_MaxFD); | |
9c8434f6 | 576 | |
577 | } | |
578 | ||
579 | #endif /* USE_WIN32_SERVICE */ | |
9c8434f6 | 580 | if (Win32SockInit() < 0) |
581 | return 1; | |
582 | ||
9c8434f6 | 583 | return 0; |
584 | } | |
585 | ||
586 | #if USE_WIN32_SERVICE | |
587 | void | |
588 | WIN32_svcstatusupdate(DWORD svcstate, DWORD WaitHint) | |
589 | { | |
590 | if (WIN32_run_mode == _WIN_SQUID_RUN_MODE_SERVICE) { | |
14942edd | 591 | ++svcStatus.dwCheckPoint; |
9c8434f6 | 592 | svcStatus.dwWaitHint = WaitHint; |
593 | svcStatus.dwCurrentState = svcstate; | |
594 | SetServiceStatus(svcHandle, &svcStatus); | |
595 | } | |
596 | } | |
597 | ||
598 | VOID WINAPI | |
599 | WIN32_svcHandler(DWORD Opcode) | |
600 | { | |
601 | DWORD status; | |
602 | ||
603 | switch (Opcode) { | |
604 | ||
605 | case _WIN_SQUID_SERVICE_CONTROL_STOP: | |
606 | ||
607 | case _WIN_SQUID_SERVICE_CONTROL_SHUTDOWN: | |
608 | /* Do whatever it takes to stop here. */ | |
609 | svcStatus.dwWin32ExitCode = 0; | |
610 | svcStatus.dwCurrentState = SERVICE_STOP_PENDING; | |
611 | svcStatus.dwCheckPoint = 0; | |
612 | svcStatus.dwWaitHint = 10000; | |
613 | shut_down(SIGTERM); | |
614 | ||
615 | if (!SetServiceStatus(svcHandle, &svcStatus)) { | |
616 | status = GetLastError(); | |
e0236918 | 617 | debugs(1, DBG_IMPORTANT, "SetServiceStatus error " << status); |
9c8434f6 | 618 | } |
619 | ||
e0236918 | 620 | debugs(1, DBG_IMPORTANT, "Leaving Squid service"); |
9c8434f6 | 621 | return; |
622 | ||
623 | case _WIN_SQUID_SERVICE_CONTROL_INTERROGATE: | |
624 | /* Fall through to send current status. */ | |
625 | ||
626 | if (!SetServiceStatus(svcHandle, &svcStatus)) { | |
627 | status = GetLastError(); | |
e0236918 | 628 | debugs(1, DBG_IMPORTANT, "SetServiceStatus error " << status); |
9c8434f6 | 629 | } |
630 | ||
631 | break; | |
632 | ||
633 | case _WIN_SQUID_SERVICE_CONTROL_ROTATE: | |
634 | rotate_logs(SIGUSR1); | |
635 | break; | |
636 | ||
637 | case _WIN_SQUID_SERVICE_CONTROL_RECONFIGURE: | |
638 | reconfigure(SIGHUP); | |
639 | break; | |
640 | ||
641 | case _WIN_SQUID_SERVICE_CONTROL_DEBUG: | |
642 | sigusr2_handle(SIGUSR2); | |
643 | break; | |
644 | ||
645 | case _WIN_SQUID_SERVICE_CONTROL_INTERRUPT: | |
646 | /* Do whatever it takes to stop here. */ | |
647 | svcStatus.dwWin32ExitCode = 0; | |
648 | svcStatus.dwCurrentState = SERVICE_STOP_PENDING; | |
649 | svcStatus.dwCheckPoint = 0; | |
650 | svcStatus.dwWaitHint = 10000; | |
651 | shut_down(SIGINT); | |
652 | ||
653 | if (!SetServiceStatus(svcHandle, &svcStatus)) { | |
654 | status = GetLastError(); | |
e0236918 | 655 | debugs(1, DBG_IMPORTANT, "SetServiceStatus error " << status); |
9c8434f6 | 656 | } |
657 | ||
e0236918 | 658 | debugs(1, DBG_IMPORTANT, "Leaving Squid service"); |
9c8434f6 | 659 | break; |
660 | ||
661 | default: | |
e0236918 | 662 | debugs(1, DBG_IMPORTANT, "Unrecognized opcode " << Opcode); |
9c8434f6 | 663 | } |
664 | ||
665 | return; | |
666 | } | |
667 | ||
668 | void | |
669 | WIN32_RemoveService() | |
670 | { | |
671 | SC_HANDLE schService; | |
672 | SC_HANDLE schSCManager; | |
673 | ||
cf2155f2 AJ |
674 | if (!service_name) |
675 | service_name = xstrdup(APP_SHORTNAME); | |
9c8434f6 | 676 | |
cf2155f2 | 677 | strcat(REGKEY, service_name); |
9c8434f6 | 678 | |
cf2155f2 | 679 | keys[4] = service_name; |
9c8434f6 | 680 | |
681 | schSCManager = OpenSCManager(NULL, /* machine (NULL == local) */ | |
682 | NULL, /* database (NULL == default) */ | |
683 | SC_MANAGER_ALL_ACCESS /* access required */ | |
684 | ); | |
685 | ||
686 | if (!schSCManager) | |
687 | fprintf(stderr, "OpenSCManager failed\n"); | |
688 | else { | |
cf2155f2 | 689 | schService = OpenService(schSCManager, service_name, SERVICE_ALL_ACCESS); |
9c8434f6 | 690 | |
691 | if (schService == NULL) | |
692 | fprintf(stderr, "OpenService failed\n"); | |
693 | ||
694 | /* Could not open the service */ | |
695 | else { | |
696 | /* try to stop the service */ | |
697 | ||
698 | if (ControlService(schService, _WIN_SQUID_SERVICE_CONTROL_STOP, | |
699 | &svcStatus)) { | |
700 | sleep(1); | |
701 | ||
702 | while (QueryServiceStatus(schService, &svcStatus)) { | |
703 | if (svcStatus.dwCurrentState == SERVICE_STOP_PENDING) | |
704 | sleep(1); | |
705 | else | |
706 | break; | |
707 | } | |
708 | } | |
709 | ||
710 | /* now remove the service */ | |
711 | if (DeleteService(schService) == 0) | |
712 | fprintf(stderr, "DeleteService failed.\n"); | |
713 | else | |
cf2155f2 | 714 | printf("Service %s deleted successfully.\n", service_name); |
9c8434f6 | 715 | |
716 | CloseServiceHandle(schService); | |
717 | } | |
718 | ||
719 | CloseServiceHandle(schSCManager); | |
720 | } | |
721 | } | |
722 | ||
723 | void | |
724 | WIN32_SetServiceCommandLine() | |
725 | { | |
cf2155f2 AJ |
726 | if (!service_name) |
727 | service_name = xstrdup(APP_SHORTNAME); | |
9c8434f6 | 728 | |
cf2155f2 | 729 | strcat(REGKEY, service_name); |
9c8434f6 | 730 | |
cf2155f2 | 731 | keys[4] = service_name; |
9c8434f6 | 732 | |
733 | /* Now store the Service Command Line in the registry */ | |
734 | WIN32_StoreKey(COMMANDLINE, REG_SZ, (unsigned char *) WIN32_Command_Line, strlen(WIN32_Command_Line) + 1); | |
735 | } | |
736 | ||
737 | void | |
738 | WIN32_InstallService() | |
739 | { | |
740 | SC_HANDLE schService; | |
741 | SC_HANDLE schSCManager; | |
742 | char ServicePath[512]; | |
743 | char szPath[512]; | |
744 | int lenpath; | |
745 | ||
cf2155f2 AJ |
746 | if (!service_name) |
747 | service_name = xstrdup(APP_SHORTNAME); | |
9c8434f6 | 748 | |
cf2155f2 | 749 | strcat(REGKEY, service_name); |
9c8434f6 | 750 | |
cf2155f2 | 751 | keys[4] = service_name; |
9c8434f6 | 752 | |
753 | if ((lenpath = GetModuleFileName(NULL, ServicePath, 512)) == 0) { | |
754 | fprintf(stderr, "Can't get executable path\n"); | |
755 | exit(1); | |
756 | } | |
757 | ||
cf2155f2 | 758 | snprintf(szPath, sizeof(szPath), "%s %s:%s", ServicePath, _WIN_SQUID_SERVICE_OPTION, service_name); |
9c8434f6 | 759 | schSCManager = OpenSCManager(NULL, /* machine (NULL == local) */ |
760 | NULL, /* database (NULL == default) */ | |
761 | SC_MANAGER_ALL_ACCESS /* access required */ | |
762 | ); | |
763 | ||
764 | if (!schSCManager) { | |
765 | fprintf(stderr, "OpenSCManager failed\n"); | |
766 | exit(1); | |
767 | } else { | |
768 | schService = CreateService(schSCManager, /* SCManager database */ | |
cf2155f2 AJ |
769 | service_name, /* name of service */ |
770 | service_name, /* name to display */ | |
9c8434f6 | 771 | SERVICE_ALL_ACCESS, /* desired access */ |
772 | SERVICE_WIN32_OWN_PROCESS, /* service type */ | |
773 | SERVICE_AUTO_START, /* start type */ | |
774 | SERVICE_ERROR_NORMAL, /* error control type */ | |
775 | (const char *) szPath, /* service's binary */ | |
776 | NULL, /* no load ordering group */ | |
777 | NULL, /* no tag identifier */ | |
778 | "Tcpip\0AFD\0", /* dependencies */ | |
779 | NULL, /* LocalSystem account */ | |
780 | NULL); /* no password */ | |
781 | ||
782 | if (schService) { | |
783 | if (WIN32_OS_version > _WIN_OS_WINNT) { | |
784 | HMODULE ADVAPI32Handle; | |
785 | PFChangeServiceConfig2 ChangeServiceConfig2; | |
786 | DWORD dwInfoLevel = SERVICE_CONFIG_DESCRIPTION; | |
787 | ||
788 | ADVAPI32Handle = GetModuleHandle("advapi32"); | |
789 | ChangeServiceConfig2 = (PFChangeServiceConfig2) GetProcAddress(ADVAPI32Handle, CHANGESERVICECONFIG2); | |
790 | ChangeServiceConfig2(schService, dwInfoLevel, &Squid_ServiceDescription); | |
791 | dwInfoLevel = SERVICE_CONFIG_FAILURE_ACTIONS; | |
792 | ChangeServiceConfig2(schService, dwInfoLevel, &Squid_ServiceFailureActions); | |
793 | } | |
794 | ||
795 | CloseServiceHandle(schService); | |
796 | /* Now store the config file location in the registry */ | |
797 | ||
798 | if (!ConfigFile) | |
799 | ConfigFile = xstrdup(DefaultConfigFile); | |
800 | ||
801 | WIN32_StoreKey(CONFIGFILE, REG_SZ, (unsigned char *) ConfigFile, strlen(ConfigFile) + 1); | |
802 | ||
cf2155f2 AJ |
803 | printf("Squid Cache version %s for %s\n", version_string, CONFIG_HOST_TYPE); |
804 | printf("installed successfully as %s Windows System Service.\n", service_name); | |
805 | printf("To run, start it from the Services Applet of Control Panel.\n"); | |
9c8434f6 | 806 | printf("Don't forget to edit squid.conf before starting it.\n\n"); |
807 | } else { | |
808 | fprintf(stderr, "CreateService failed\n"); | |
809 | exit(1); | |
810 | } | |
811 | ||
812 | CloseServiceHandle(schSCManager); | |
813 | } | |
814 | } | |
815 | ||
816 | void | |
817 | WIN32_sendSignal(int WIN32_signal) | |
818 | { | |
819 | SERVICE_STATUS ssStatus; | |
820 | DWORD fdwAccess, fdwControl; | |
821 | SC_HANDLE schService; | |
822 | SC_HANDLE schSCManager; | |
823 | ||
cf2155f2 AJ |
824 | if (!service_name) |
825 | service_name = xstrdup(APP_SHORTNAME); | |
9c8434f6 | 826 | |
827 | schSCManager = OpenSCManager(NULL, /* machine (NULL == local) */ | |
828 | NULL, /* database (NULL == default) */ | |
829 | SC_MANAGER_ALL_ACCESS /* access required */ | |
830 | ); | |
831 | ||
832 | if (!schSCManager) { | |
833 | fprintf(stderr, "OpenSCManager failed\n"); | |
834 | exit(1); | |
835 | } | |
836 | ||
837 | /* The required service object access depends on the control. */ | |
838 | switch (WIN32_signal) { | |
839 | ||
840 | case 0: /* SIGNULL */ | |
841 | fdwAccess = SERVICE_INTERROGATE; | |
842 | fdwControl = _WIN_SQUID_SERVICE_CONTROL_INTERROGATE; | |
843 | break; | |
844 | ||
845 | case SIGUSR1: | |
846 | fdwAccess = SERVICE_USER_DEFINED_CONTROL; | |
847 | fdwControl = _WIN_SQUID_SERVICE_CONTROL_ROTATE; | |
848 | break; | |
849 | ||
850 | case SIGUSR2: | |
851 | fdwAccess = SERVICE_USER_DEFINED_CONTROL; | |
852 | fdwControl = _WIN_SQUID_SERVICE_CONTROL_DEBUG; | |
853 | break; | |
854 | ||
855 | case SIGHUP: | |
856 | fdwAccess = SERVICE_USER_DEFINED_CONTROL; | |
857 | fdwControl = _WIN_SQUID_SERVICE_CONTROL_RECONFIGURE; | |
858 | break; | |
859 | ||
860 | case SIGTERM: | |
861 | fdwAccess = SERVICE_STOP; | |
862 | fdwControl = _WIN_SQUID_SERVICE_CONTROL_STOP; | |
863 | break; | |
864 | ||
865 | case SIGINT: | |
866 | ||
867 | case SIGKILL: | |
868 | fdwAccess = SERVICE_USER_DEFINED_CONTROL; | |
869 | fdwControl = _WIN_SQUID_SERVICE_CONTROL_INTERRUPT; | |
870 | break; | |
871 | ||
872 | default: | |
873 | exit(1); | |
874 | } | |
875 | ||
876 | /* Open a handle to the service. */ | |
877 | schService = OpenService(schSCManager, /* SCManager database */ | |
cf2155f2 | 878 | service_name, /* name of service */ |
9c8434f6 | 879 | fdwAccess); /* specify access */ |
880 | ||
881 | if (schService == NULL) { | |
cf2155f2 | 882 | fprintf(stderr, "%s: ERROR: Could not open Service %s\n", APP_SHORTNAME, service_name); |
9c8434f6 | 883 | exit(1); |
884 | } else { | |
885 | /* Send a control value to the service. */ | |
886 | ||
887 | if (!ControlService(schService, /* handle of service */ | |
888 | fdwControl, /* control value to send */ | |
889 | &ssStatus)) { /* address of status info */ | |
890 | fprintf(stderr, "%s: ERROR: Could not Control Service %s\n", | |
cf2155f2 | 891 | APP_SHORTNAME, service_name); |
9c8434f6 | 892 | exit(1); |
893 | } else { | |
894 | /* Print the service status. */ | |
cf2155f2 | 895 | printf("\nStatus of %s Service:\n", service_name); |
9c8434f6 | 896 | printf(" Service Type: 0x%lx\n", ssStatus.dwServiceType); |
897 | printf(" Current State: 0x%lx\n", ssStatus.dwCurrentState); | |
898 | printf(" Controls Accepted: 0x%lx\n", ssStatus.dwControlsAccepted); | |
899 | printf(" Exit Code: %ld\n", ssStatus.dwWin32ExitCode); | |
900 | printf(" Service Specific Exit Code: %ld\n", | |
901 | ssStatus.dwServiceSpecificExitCode); | |
902 | printf(" Check Point: %ld\n", ssStatus.dwCheckPoint); | |
903 | printf(" Wait Hint: %ld\n", ssStatus.dwWaitHint); | |
904 | } | |
905 | ||
906 | CloseServiceHandle(schService); | |
907 | } | |
908 | ||
909 | CloseServiceHandle(schSCManager); | |
910 | } | |
911 | ||
912 | int main(int argc, char **argv) | |
913 | { | |
914 | SERVICE_TABLE_ENTRY DispatchTable[] = { | |
26ac0430 AJ |
915 | {NULL, SquidWinSvcMain}, |
916 | {NULL, NULL} | |
917 | }; | |
9c8434f6 | 918 | char *c; |
919 | char stderr_path[256]; | |
920 | ||
91d63ce7 | 921 | SetErrorMode(SEM_NOGPFAULTERRORBOX); |
9c8434f6 | 922 | if ((argc == 2) && strstr(argv[1], _WIN_SQUID_SERVICE_OPTION)) { |
923 | strcpy(stderr_path, argv[0]); | |
924 | strcat(stderr_path,".log"); | |
925 | freopen(stderr_path, "w", stderr); | |
926 | setmode(fileno(stderr), O_TEXT); | |
927 | WIN32_run_mode = _WIN_SQUID_RUN_MODE_SERVICE; | |
9c8434f6 | 928 | |
929 | if (!(c=strchr(argv[1],':'))) { | |
930 | fprintf(stderr, "Bad Service Parameter: %s\n", argv[1]); | |
931 | return 1; | |
932 | } | |
933 | ||
cf2155f2 AJ |
934 | service_name = xstrdup(c+1); |
935 | DispatchTable[0].lpServiceName=service_name; | |
936 | strcat(REGKEY, service_name); | |
937 | keys[4] = service_name; | |
9c8434f6 | 938 | |
939 | if (!StartServiceCtrlDispatcher(DispatchTable)) { | |
940 | fprintf(stderr, "StartServiceCtrlDispatcher error = %ld\n", | |
941 | GetLastError()); | |
942 | return 1; | |
943 | } | |
944 | } else { | |
945 | WIN32_run_mode = _WIN_SQUID_RUN_MODE_INTERACTIVE; | |
9c8434f6 | 946 | opt_no_daemon = 1; |
947 | ||
9c8434f6 | 948 | return SquidMain(argc, argv); |
949 | } | |
950 | ||
951 | return 0; | |
952 | } | |
953 | ||
954 | #endif /* USE_WIN32_SERVICE */ | |
955 | ||
9c8434f6 | 956 | static int Win32SockInit(void) |
957 | { | |
958 | int iVersionRequested; | |
959 | WSADATA wsaData; | |
960 | int err, opt; | |
961 | int optlen = sizeof(opt); | |
962 | ||
963 | if (s_iInitCount > 0) { | |
14942edd | 964 | ++s_iInitCount; |
9c8434f6 | 965 | return (0); |
966 | } else if (s_iInitCount < 0) | |
967 | return (s_iInitCount); | |
968 | ||
969 | /* s_iInitCount == 0. Do the initailization */ | |
970 | iVersionRequested = MAKEWORD(2, 0); | |
971 | ||
972 | err = WSAStartup((WORD) iVersionRequested, &wsaData); | |
973 | ||
974 | if (err) { | |
975 | s_iInitCount = -1; | |
976 | return (s_iInitCount); | |
977 | } | |
978 | ||
979 | if (LOBYTE(wsaData.wVersion) != 2 || | |
980 | HIBYTE(wsaData.wVersion) != 0) { | |
981 | s_iInitCount = -2; | |
982 | WSACleanup(); | |
983 | return (s_iInitCount); | |
984 | } | |
985 | ||
986 | if (WIN32_OS_version !=_WIN_OS_WINNT) { | |
987 | if (::getsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&opt, &optlen)) { | |
988 | s_iInitCount = -3; | |
989 | WSACleanup(); | |
990 | return (s_iInitCount); | |
991 | } else { | |
992 | opt = opt | SO_SYNCHRONOUS_NONALERT; | |
993 | ||
994 | if (::setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *) &opt, optlen)) { | |
995 | s_iInitCount = -3; | |
996 | WSACleanup(); | |
997 | return (s_iInitCount); | |
998 | } | |
999 | } | |
1000 | } | |
1001 | ||
1002 | WIN32_Socks_initialized = 1; | |
14942edd | 1003 | ++s_iInitCount; |
9c8434f6 | 1004 | return (s_iInitCount); |
1005 | } | |
1006 | ||
1007 | static void Win32SockCleanup(void) | |
1008 | { | |
1009 | if (--s_iInitCount == 0) | |
1010 | WSACleanup(); | |
1011 | ||
1012 | return; | |
1013 | } | |
1014 | ||
1015 | void Squid_Win32InvalidParameterHandler(const wchar_t* expression, const wchar_t* function, const wchar_t* file, unsigned int line, uintptr_t pReserved) | |
1016 | { | |
1017 | return; | |
1018 | } |