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