2 * Licensed to the University Corporation for Advanced Internet
3 * Development, Inc. (UCAID) under one or more contributor license
4 * agreements. See the NOTICE file distributed with this work for
5 * additional information regarding copyright ownership.
7 * UCAID licenses this file to you under the Apache License,
8 * Version 2.0 (the "License"); you may not use this file except
9 * in compliance with the License. You may obtain a copy of the
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17 * either express or implied. See the License for the specific
18 * language governing permissions and limitations under the License.
22 * shar_win32.cpp -- the SHAR "main" code on Win32
25 #include "config_win32.h"
27 #define _CRT_NONSTDC_NO_DEPRECATE 1
28 #define _CRT_SECURE_NO_DEPRECATE 1
30 #include <shibsp/base.h>
36 extern bool shibd_shutdown; // signals shutdown to Unix side
37 extern const char* shar_schemadir;
38 extern const char* shar_config;
39 extern const char* shar_prefix;
40 extern bool shar_checkonly;
43 SERVICE_STATUS ssStatus; // current status of the service
44 SERVICE_STATUS_HANDLE sshStatusHandle;
46 BOOL bConsole = FALSE;
48 LPCSTR lpszInstall = nullptr;
49 LPCSTR lpszRemove = nullptr;
51 // internal function prototypes
52 VOID WINAPI service_ctrl(DWORD dwCtrlCode);
53 VOID WINAPI service_main(DWORD dwArgc, LPSTR *lpszArgv);
54 VOID CmdInstallService(LPCSTR);
55 VOID CmdRemoveService(LPCSTR);
56 LPTSTR GetLastErrorText( LPSTR lpszBuf, DWORD dwSize );
59 LPCTSTR lpUNCServerName,
65 VOID ServiceStart(DWORD dwArgc, LPSTR *lpszArgv);
67 BOOL ReportStatusToSCMgr(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint);
68 void AddToMessageLog(LPSTR lpszMsg);
70 BOOL WINAPI BreakHandler(DWORD dwCtrlType)
74 case CTRL_BREAK_EVENT: // use Ctrl+C or Ctrl+Break to simulate
75 case CTRL_C_EVENT: // SERVICE_CONTROL_STOP in console mode
83 int real_main(int); // The revised two-phase main() in shibd.cpp
85 int main(int argc, char *argv[])
88 while ((argc > i) && ((*argv[i] == '-') || (*argv[i] == '/')))
90 if (_stricmp("install", argv[i]+1) == 0)
93 lpszInstall = argv[i++];
95 else if (_stricmp("remove", argv[i]+1) == 0)
98 lpszRemove = argv[i++];
100 else if (_stricmp("stdout", argv[i]+1) == 0)
103 freopen(argv[i++], "a+", stdout);
105 else if (_stricmp("stderr", argv[i]+1) == 0)
108 freopen(argv[i++], "a+", stderr);
110 else if (_stricmp( "console", argv[i]+1) == 0)
115 else if (_stricmp( "check", argv[i]+1) == 0)
121 else if (_stricmp( "config", argv[i]+1) == 0)
124 shar_config = argv[i++];
126 else if (_stricmp( "prefix", argv[i]+1) == 0)
129 shar_prefix = argv[i++];
131 else if (_stricmp( "catalogs", argv[i]+1) == 0)
134 shar_schemadir = argv[i++];
144 // Install break handler, then run the C routine twice, once to setup, once to start running.
145 SetConsoleCtrlHandler(&BreakHandler,TRUE);
146 if ((i=real_main(1))!=0)
148 LogEvent(nullptr, EVENTLOG_ERROR_TYPE, 2100, nullptr, "shibd startup failed, check shibd.log for further details");
153 else if (lpszInstall)
155 CmdInstallService(lpszInstall);
160 CmdRemoveService(lpszRemove);
165 // if it doesn't match any of the above parameters
166 // the service control manager may be starting the service
167 // so we must call StartServiceCtrlDispatcher
169 // this is just to be friendly
170 printf("%s -install <name> to install the named service\n", argv[0]);
171 printf("%s -remove <name> to remove the named service\n", argv[0]);
172 printf("%s -console to run as a console app for debugging\n", argv[0]);
173 printf("%s -check to run as a console app and check configuration\n", argv[0]);
174 printf("\t-stdout <path> to redirect stdout stream\n");
175 printf("\t-stderr <path> to redirect stderr stream\n");
176 printf("\t-prefix <dir> to specify the installation directory\n");
177 printf("\t-config <file> to specify the config file to use\n");
178 printf("\t-catalogs <catalog1:catalog2> to specify schema catalogs\n");
179 printf("\nService starting.\nThis may take several seconds. Please wait.\n" );
181 SERVICE_TABLE_ENTRY dispatchTable[] =
183 { "SHIBD", (LPSERVICE_MAIN_FUNCTION)service_main },
187 if (!StartServiceCtrlDispatcher(dispatchTable))
188 LogEvent(nullptr, EVENTLOG_ERROR_TYPE, 2100, nullptr, "StartServiceCtrlDispatcher failed.");
193 // FUNCTION: ServiceStart
195 // PURPOSE: Actual code of the service
196 // that does the work.
198 VOID ServiceStart (DWORD dwArgc, LPSTR *lpszArgv)
203 LogEvent(nullptr, EVENTLOG_ERROR_TYPE, 2100, nullptr, "shibd startup failed, check shibd.log for further details");
207 LogEvent(nullptr, EVENTLOG_INFORMATION_TYPE, 7700, nullptr, "shibd started successfully.");
209 if (!ReportStatusToSCMgr(SERVICE_RUNNING, NO_ERROR, 0))
217 // FUNCTION: ServiceStop
219 // PURPOSE: Stops the service
224 LogEvent(nullptr, EVENTLOG_INFORMATION_TYPE, 7701, nullptr, "shibd stopping...");
229 void WINAPI service_main(DWORD dwArgc, LPSTR *lpszArgv)
232 // register our service control handler:
233 sshStatusHandle=RegisterServiceCtrlHandler(lpszArgv[0], service_ctrl);
234 if (!sshStatusHandle)
237 // SERVICE_STATUS members that don't change in example
238 ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
239 ssStatus.dwServiceSpecificExitCode = 0;
242 // report the status to the service control manager.
243 if (!ReportStatusToSCMgr(
244 SERVICE_START_PENDING, // service state
245 NO_ERROR, // exit code
250 ServiceStart(dwArgc, lpszArgv);
254 // try to report the stopped status to the service control manager.
257 (VOID)ReportStatusToSCMgr(
267 // FUNCTION: service_ctrl
269 // PURPOSE: This function is called by the SCM whenever
270 // ControlService() is called on this service.
273 // dwCtrlCode - type of control requested
278 VOID WINAPI service_ctrl(DWORD dwCtrlCode)
280 // Handle the requested control code.
286 case SERVICE_CONTROL_STOP:
287 ssStatus.dwCurrentState = SERVICE_STOP_PENDING;
291 // Update the service status.
293 case SERVICE_CONTROL_INTERROGATE:
296 // invalid control code
303 ReportStatusToSCMgr(ssStatus.dwCurrentState, NO_ERROR, 0);
308 // FUNCTION: ReportStatusToSCMgr()
310 // PURPOSE: Sets the current status of the service and
311 // reports it to the Service Control Manager
314 // dwCurrentState - the state of the service
315 // dwWin32ExitCode - error code to report
316 // dwWaitHint - worst case estimate to next checkpoint
322 BOOL ReportStatusToSCMgr(DWORD dwCurrentState,
323 DWORD dwWin32ExitCode,
326 static DWORD dwCheckPoint = 1;
330 if (!bConsole) // when console we don't report to the SCM
332 if (dwCurrentState == SERVICE_START_PENDING)
333 ssStatus.dwControlsAccepted = 0;
335 ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
337 ssStatus.dwCurrentState = dwCurrentState;
338 ssStatus.dwWin32ExitCode = dwWin32ExitCode;
339 ssStatus.dwWaitHint = dwWaitHint;
341 if ( ( dwCurrentState == SERVICE_RUNNING ) ||
342 ( dwCurrentState == SERVICE_STOPPED ) )
343 ssStatus.dwCheckPoint = 0;
345 ssStatus.dwCheckPoint = dwCheckPoint++;
348 // Report the status of the service to the service control manager.
350 if (!(fResult = SetServiceStatus(sshStatusHandle, &ssStatus)))
351 LogEvent(nullptr, EVENTLOG_ERROR_TYPE, 2100, nullptr, "SetServiceStatus failed.");
357 ///////////////////////////////////////////////////////////////////
359 // The following code handles service installation and removal
362 void CmdInstallService(LPCSTR name)
364 SC_HANDLE schService;
365 SC_HANDLE schSCManager;
369 if ( GetModuleFileName( nullptr, szPath, 256 ) == 0 )
371 printf("Unable to install %s - %s\n", name, GetLastErrorText(szErr, 256));
375 string dispName = string("Shibboleth 2 Daemon (") + name + ")";
376 string realName = string("shibd_") + name;
379 cmd = cmd + " -prefix " + shar_prefix;
381 cmd = cmd + " -config " + shar_config;
383 cmd = cmd + " -schemadir " + shar_schemadir;
385 schSCManager = OpenSCManager(
386 nullptr, // machine (nullptr == local)
387 nullptr, // database (nullptr == default)
388 SC_MANAGER_ALL_ACCESS // access required
394 schService = CreateService(
395 schSCManager, // SCManager database
396 realName.c_str(), // name of service
397 dispName.c_str(), // name to display
398 SERVICE_ALL_ACCESS, // desired access
399 SERVICE_WIN32_OWN_PROCESS, // service type
400 SERVICE_AUTO_START, // start type
401 SERVICE_ERROR_NORMAL, // error control type
402 cmd.c_str(), // service's command line
403 nullptr, // no load ordering group
404 nullptr, // no tag identifier
405 nullptr, // dependencies
406 nullptr, // LocalSystem account
407 nullptr); // no password
411 printf("%s installed.\n", realName.c_str());
412 CloseServiceHandle(schService);
416 printf("CreateService failed - %s\n", GetLastErrorText(szErr, 256));
419 CloseServiceHandle(schSCManager);
422 printf("OpenSCManager failed - %s\n", GetLastErrorText(szErr,256));
425 void CmdRemoveService(LPCSTR name)
427 SC_HANDLE schService;
428 SC_HANDLE schSCManager;
431 _snprintf(realName,sizeof(realName),"shibd_%s",name);
433 schSCManager = OpenSCManager(
434 nullptr, // machine (nullptr == local)
435 nullptr, // database (nullptr == default)
436 SC_MANAGER_ALL_ACCESS // access required
440 schService = OpenService(schSCManager, realName, SERVICE_ALL_ACCESS);
444 // try to stop the service
445 if ( ControlService( schService, SERVICE_CONTROL_STOP, &ssStatus ) )
447 printf("Stopping shibd (%s).", name);
450 while( QueryServiceStatus( schService, &ssStatus ) )
452 if ( ssStatus.dwCurrentState == SERVICE_STOP_PENDING )
461 if ( ssStatus.dwCurrentState == SERVICE_STOPPED )
462 printf("\n%s stopped.\n", realName);
464 printf("\n%s failed to stop.\n", realName);
468 // now remove the service
469 if( DeleteService(schService) )
470 printf("%s removed.\n", realName);
472 printf("DeleteService failed - %s\n", GetLastErrorText(szErr,256));
475 CloseServiceHandle(schService);
478 printf("OpenService failed - %s\n", GetLastErrorText(szErr,256));
480 CloseServiceHandle(schSCManager);
483 printf("OpenSCManager failed - %s\n", GetLastErrorText(szErr,256));
488 // FUNCTION: GetLastErrorText
490 // PURPOSE: copies error message text to string
493 // lpszBuf - destination buffer
494 // dwSize - size of buffer
497 // destination buffer
501 LPTSTR GetLastErrorText( LPSTR lpszBuf, DWORD dwSize )
504 LPSTR lpszTemp = nullptr;
506 dwRet = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
514 // supplied buffer is not long enough
515 if ( !dwRet || ( (long)dwSize < (long)dwRet+14 ) )
519 lpszTemp[lstrlen(lpszTemp)-2] = '\0'; //remove cr and newline character
520 sprintf( lpszBuf, "%s (0x%x)", lpszTemp, GetLastError() );
524 LocalFree((HLOCAL) lpszTemp );
530 LPCSTR lpUNCServerName,
536 LPCSTR messages[] = {message, nullptr};
538 HANDLE hElog = RegisterEventSource(lpUNCServerName, "Shibboleth Daemon");
539 BOOL res = ReportEvent(hElog, wType, 0, dwEventID, lpUserSid, 1, 0, messages, nullptr);
540 return (DeregisterEventSource(hElog) && res);