]> git.ipfire.org Git - thirdparty/squid.git/blame - src/WinSvc.cc
Boilerplate: update copyright blurbs on src/
[thirdparty/squid.git] / src / WinSvc.cc
CommitLineData
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 */
28static void WIN32_Exit(void);
9c8434f6 29static unsigned int GetOSVersion();
30void WIN32_svcstatusupdate(DWORD, DWORD);
31void WINAPI WIN32_svcHandler(DWORD);
91d63ce7
AJ
32extern "C" void WINAPI SquidWinSvcMain(DWORD, char **);
33
9c8434f6 34#if USE_WIN32_SERVICE
91d63ce7 35static void WIN32_Abort(int);
9c8434f6 36static int WIN32_StoreKey(const char *, DWORD, unsigned char *, int);
37static int WIN32_create_key(void);
38static void WIN32_build_argv (char *);
39#endif
9c8434f6 40
9c8434f6 41#if defined(_MSC_VER) /* Microsoft C Compiler ONLY */
42void Squid_Win32InvalidParameterHandler(const wchar_t*, const wchar_t*, const wchar_t*, unsigned int, uintptr_t);
43#endif
44static int Win32SockInit(void);
45static void Win32SockCleanup(void);
46SQUIDCEXTERN LPCRITICAL_SECTION dbg_mutex;
47void WIN32_ExceptionHandlerCleanup(void);
48static int s_iInitCount = 0;
353942c5 49static HANDLE NotifyAddrChange_thread = INVALID_HANDLE_VALUE;
9c8434f6 50
babd1fd5 51#undef NotifyAddrChange
52typedef DWORD(WINAPI * PFNotifyAddrChange) (OUT PHANDLE, IN LPOVERLAPPED);
53#define NOTIFYADDRCHANGE "NotifyAddrChange"
9c8434f6 54
55#if USE_WIN32_SERVICE
56static SERVICE_STATUS svcStatus;
57static SERVICE_STATUS_HANDLE svcHandle;
58static int WIN32_argc;
59static char ** WIN32_argv;
60static char * WIN32_module_name;
61
d2b4d181 62#define VENDOR "squid-cache.org"
9c8434f6 63static char VENDORString[] = VENDOR;
d2b4d181 64#define SOFTWARENAME PACKAGE_NAME
9c8434f6 65static char SOFTWARENAMEString[] = SOFTWARENAME;
9c8434f6 66#define SOFTWARE "SOFTWARE"
67static char SOFTWAREString[] = SOFTWARE;
68#define COMMANDLINE "CommandLine"
69#define CONFIGFILE "ConfigFile"
70#undef ChangeServiceConfig2
71typedef BOOL (WINAPI * PFChangeServiceConfig2) (SC_HANDLE, DWORD, LPVOID);
72#ifdef UNICODE
73#define CHANGESERVICECONFIG2 "ChangeServiceConfig2W"
74#else
75#define CHANGESERVICECONFIG2 "ChangeServiceConfig2A"
76#endif
77static SC_ACTION Squid_SCAction[] = { { SC_ACTION_RESTART, 60000 } };
78static char Squid_ServiceDescriptionString[] = SOFTWARENAME " " VERSION " WWW Proxy Server";
79static SERVICE_DESCRIPTION Squid_ServiceDescription = { Squid_ServiceDescriptionString };
80static SERVICE_FAILURE_ACTIONS Squid_ServiceFailureActions = { INFINITE, NULL, NULL, 1, Squid_SCAction };
d2b4d181 81static char REGKEY[256]=SOFTWARE"\\"VENDOR"\\"SOFTWARENAME"\\";
9c8434f6 82static 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
90static int Squid_Aborting = 0;
9c8434f6 91#endif
92
93/* ====================================================================== */
94/* LOCAL FUNCTIONS */
95/* ====================================================================== */
96
97#if USE_WIN32_SERVICE
98static int
99WIN32_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
164static int
165WIN32_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. */
236static 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
282static unsigned int
283GetOSVersion()
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 362GetVerError:
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 372void
373WIN32_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 382void
383WIN32_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 393void
394WIN32_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 415static DWORD WINAPI
416WIN32_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
438DWORD
439WIN32_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 457int 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
564void
565WIN32_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
575VOID WINAPI
576WIN32_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
645void
646WIN32_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
701void
702WIN32_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
716void
717WIN32_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
796void
797WIN32_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
892int 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 937static 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
988static void Win32SockCleanup(void)
989{
990 if (--s_iInitCount == 0)
991 WSACleanup();
992
993 return;
994}
995
996void Squid_Win32InvalidParameterHandler(const wchar_t* expression, const wchar_t* function, const wchar_t* file, unsigned int line, uintptr_t pReserved)
997{
998 return;
999}