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