1 /**
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.
6 *
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
10 * License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
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.
19 */
20
21 /*
22 * shar_win32.cpp -- the SHAR "main" code on Win32
23 */
24
25 #include "config_win32.h"
26
27 #define _CRT_NONSTDC_NO_DEPRECATE 1
28 #define _CRT_SECURE_NO_DEPRECATE 1
29
30 #include <shibsp/base.h>
31 #include <xmltooling/logging.h>
32
33 #include <string>
34 #include <windows.h>
35
36 using namespace std;
37
38 using xmltooling::logging::Priority;
39 using xmltooling::logging::Category;
40
41 extern bool shibd_shutdown; // signals shutdown to Unix side
42 extern const char* shar_schemadir;
43 extern const char* shar_config;
44 extern const char* shar_prefix;
45 extern bool shar_checkonly;
46 extern bool shar_version;
47
48 // internal variables
49 SERVICE_STATUS ssStatus; // current status of the service
50 SERVICE_STATUS_HANDLE sshStatusHandle;
51 DWORD dwErr = 0;
52 BOOL bConsole = FALSE;
53 char szErr[256];
54 LPCSTR lpszInstall = nullptr;
55 LPCSTR lpszRemove = nullptr;
56 LPCSTR lpszStdout = nullptr;
57 LPCSTR lpszStderr = nullptr;
58
59 // internal function prototypes
60 VOID WINAPI service_ctrl(DWORD dwCtrlCode);
61 VOID WINAPI service_main(DWORD dwArgc, LPSTR *lpszArgv);
62 VOID CmdInstallService(LPCSTR);
63 VOID CmdRemoveService(LPCSTR);
64 LPTSTR GetLastErrorText( LPSTR lpszBuf, DWORD dwSize );
65
66 BOOL LogEvent(
67 WORD wType,
68 Priority::PriorityLevel priority,
69 DWORD dwEventID,
70 LPCTSTR message);
71
72 VOID ServiceStart(DWORD dwArgc, LPSTR *lpszArgv);
73 VOID ServiceStop();
74 BOOL ReportStatusToSCMgr(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint);
75
BreakHandler(DWORD dwCtrlType)76 BOOL WINAPI BreakHandler(DWORD dwCtrlType)
77 {
78 switch(dwCtrlType)
79 {
80 case CTRL_BREAK_EVENT: // use Ctrl+C or Ctrl+Break to simulate
81 case CTRL_C_EVENT: // SERVICE_CONTROL_STOP in console mode
82 ServiceStop();
83 return TRUE;
84 }
85 return FALSE;
86 }
87
88
89 int real_main(int); // The revised two-phase main() in shibd.cpp
90
main(int argc,char * argv[])91 int main(int argc, char *argv[])
92 {
93 int i=1;
94 while ((argc > i) && ((*argv[i] == '-') || (*argv[i] == '/')))
95 {
96 if (_stricmp("install", argv[i]+1) == 0)
97 {
98 if (argc > ++i)
99 lpszInstall = argv[i++];
100 }
101 else if (_stricmp("remove", argv[i]+1) == 0)
102 {
103 if (argc > ++i)
104 lpszRemove = argv[i++];
105 }
106 else if (_stricmp("stdout", argv[i]+1) == 0)
107 {
108 if (argc > ++i) {
109 lpszStdout = argv[i++];
110 freopen(lpszStdout, "a+", stdout);
111 }
112 }
113 else if (_stricmp("stderr", argv[i]+1) == 0)
114 {
115 if (argc > ++i) {
116 lpszStderr = argv[i++];
117 freopen(lpszStderr, "a+", stderr);
118 }
119 }
120 else if (_stricmp( "console", argv[i]+1) == 0)
121 {
122 i++;
123 bConsole = TRUE;
124 }
125 else if (_stricmp( "check", argv[i]+1) == 0)
126 {
127 i++;
128 bConsole = TRUE;
129 shar_checkonly = true;
130 }
131 else if (_stricmp( "version", argv[i]+1) == 0)
132 {
133 i++;
134 bConsole = TRUE;
135 shar_version = true;
136 }
137 else if (_stricmp( "config", argv[i]+1) == 0)
138 {
139 if (argc > ++i)
140 shar_config = argv[i++];
141 }
142 else if (_stricmp( "prefix", argv[i]+1) == 0)
143 {
144 if (argc > ++i)
145 shar_prefix = argv[i++];
146 }
147 else if (_stricmp( "catalogs", argv[i]+1) == 0)
148 {
149 if (argc > ++i)
150 shar_schemadir = argv[i++];
151 }
152 else
153 {
154 goto dispatch;
155 }
156 }
157
158 if (bConsole)
159 {
160 // Install break handler, then run the C routine twice, once to setup, once to start running.
161 SetConsoleCtrlHandler(&BreakHandler,TRUE);
162 if ((i=real_main(1))!=0)
163 {
164 Category::getInstance(SHIBSP_LOGCAT ".shibd").fatal("daemon startup failed, check log for details");
165 return i;
166 }
167 return real_main(0);
168 }
169 else if (lpszInstall)
170 {
171 CmdInstallService(lpszInstall);
172 return 0;
173 }
174 else if (lpszRemove)
175 {
176 CmdRemoveService(lpszRemove);
177 return 0;
178 }
179
180
181 // if it doesn't match any of the above parameters
182 // the service control manager may be starting the service
183 // so we must call StartServiceCtrlDispatcher
184 dispatch:
185 // this is just to be friendly
186 printf("%s -install <name> to install the named service\n", argv[0]);
187 printf("%s -remove <name> to remove the named service\n", argv[0]);
188 printf("%s -console to run as a console app for debugging\n", argv[0]);
189 printf("%s -check to run as a console app and check configuration\n", argv[0]);
190 printf("%s -version to run as a console app and print the version\n", argv[0]);
191 printf("\t-stdout <path> to redirect stdout stream\n");
192 printf("\t-stderr <path> to redirect stderr stream\n");
193 printf("\t-prefix <dir> to specify the installation directory\n");
194 printf("\t-config <file> to specify the config file to use\n");
195 printf("\t-catalogs <catalog1:catalog2> to specify schema catalogs\n");
196 printf("\nService starting.\nThis may take several seconds. Please wait.\n" );
197
198 static char svcname[] = "SHIBD";
199
200 SERVICE_TABLE_ENTRY dispatchTable[] =
201 {
202 { svcname, (LPSERVICE_MAIN_FUNCTION)service_main },
203 { nullptr, nullptr }
204 };
205
206 if (!StartServiceCtrlDispatcher(dispatchTable))
207 Category::getInstance(SHIBSP_LOGCAT ".shibd").fatal("StartServiceCtrlDispatcher failed");
208 return 0;
209 }
210
211 //
212 // FUNCTION: ServiceStart
213 //
214 // PURPOSE: Actual code of the service
215 // that does the work.
216 //
ServiceStart(DWORD dwArgc,LPSTR * lpszArgv)217 VOID ServiceStart (DWORD dwArgc, LPSTR *lpszArgv)
218 {
219
220 if (real_main(1)!=0)
221 {
222 Category::getInstance(SHIBSP_LOGCAT ".shibd").fatal("daemon startup failed, check log for details");
223 return;
224 }
225
226 Category::getInstance(SHIBSP_LOGCAT ".shibd").info("daemon service startup complete");
227
228 if (!ReportStatusToSCMgr(SERVICE_RUNNING, NO_ERROR, 0))
229 return;
230
231 real_main(0);
232 }
233
234
235 //
236 // FUNCTION: ServiceStop
237 //
238 // PURPOSE: Stops the service
239 //
ServiceStop()240 VOID ServiceStop()
241 {
242 if (!bConsole)
243 Category::getInstance(SHIBSP_LOGCAT ".shibd").info("daemon service stopping...");
244 shibd_shutdown=true;
245 }
246
247
service_main(DWORD dwArgc,LPSTR * lpszArgv)248 void WINAPI service_main(DWORD dwArgc, LPSTR *lpszArgv)
249 {
250
251 // register our service control handler:
252 sshStatusHandle=RegisterServiceCtrlHandler(lpszArgv[0], service_ctrl);
253 if (!sshStatusHandle)
254 goto cleanup;
255
256 // SERVICE_STATUS members that don't change in example
257 ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
258 ssStatus.dwServiceSpecificExitCode = 0;
259
260
261 // report the status to the service control manager.
262 if (!ReportStatusToSCMgr(
263 SERVICE_START_PENDING, // service state
264 NO_ERROR, // exit code
265 3000)) // wait hint
266 goto cleanup;
267
268
269 ServiceStart(dwArgc, lpszArgv);
270
271 cleanup:
272
273 // try to report the stopped status to the service control manager.
274 //
275 if (sshStatusHandle)
276 (VOID)ReportStatusToSCMgr(
277 SERVICE_STOPPED,
278 dwErr,
279 0);
280
281 return;
282 }
283
284
285 //
286 // FUNCTION: service_ctrl
287 //
288 // PURPOSE: This function is called by the SCM whenever
289 // ControlService() is called on this service.
290 //
291 // PARAMETERS:
292 // dwCtrlCode - type of control requested
293 //
294 // RETURN VALUE:
295 // none
296 //
service_ctrl(DWORD dwCtrlCode)297 VOID WINAPI service_ctrl(DWORD dwCtrlCode)
298 {
299 // Handle the requested control code.
300 //
301 switch(dwCtrlCode)
302 {
303 // Stop the service.
304 //
305 case SERVICE_CONTROL_STOP:
306 ssStatus.dwCurrentState = SERVICE_STOP_PENDING;
307 ServiceStop();
308 break;
309
310 // Update the service status.
311 //
312 case SERVICE_CONTROL_INTERROGATE:
313 break;
314
315 // invalid control code
316 //
317 default:
318 break;
319
320 }
321
322 ReportStatusToSCMgr(ssStatus.dwCurrentState, NO_ERROR, 0);
323 }
324
325
326 //
327 // FUNCTION: ReportStatusToSCMgr()
328 //
329 // PURPOSE: Sets the current status of the service and
330 // reports it to the Service Control Manager
331 //
332 // PARAMETERS:
333 // dwCurrentState - the state of the service
334 // dwWin32ExitCode - error code to report
335 // dwWaitHint - worst case estimate to next checkpoint
336 //
337 // RETURN VALUE:
338 // TRUE - success
339 // FALSE - failure
340 //
ReportStatusToSCMgr(DWORD dwCurrentState,DWORD dwWin32ExitCode,DWORD dwWaitHint)341 BOOL ReportStatusToSCMgr(DWORD dwCurrentState,
342 DWORD dwWin32ExitCode,
343 DWORD dwWaitHint)
344 {
345 static DWORD dwCheckPoint = 1;
346 BOOL fResult = TRUE;
347
348
349 if (!bConsole) // when console we don't report to the SCM
350 {
351 if (dwCurrentState == SERVICE_START_PENDING)
352 ssStatus.dwControlsAccepted = 0;
353 else
354 ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
355
356 ssStatus.dwCurrentState = dwCurrentState;
357 ssStatus.dwWin32ExitCode = dwWin32ExitCode;
358 ssStatus.dwWaitHint = dwWaitHint;
359
360 if ( ( dwCurrentState == SERVICE_RUNNING ) ||
361 ( dwCurrentState == SERVICE_STOPPED ) )
362 ssStatus.dwCheckPoint = 0;
363 else
364 ssStatus.dwCheckPoint = dwCheckPoint++;
365
366
367 // Report the status of the service to the service control manager.
368 //
369 if (!(fResult = SetServiceStatus(sshStatusHandle, &ssStatus)))
370 Category::getInstance(SHIBSP_LOGCAT ".shibd").error("SetServiceStatus failed");
371 }
372 return fResult;
373 }
374
375
376 ///////////////////////////////////////////////////////////////////
377 //
378 // The following code handles service installation and removal
379 //
380 //
CmdInstallService(LPCSTR name)381 void CmdInstallService(LPCSTR name)
382 {
383 SC_HANDLE schService;
384 SC_HANDLE schSCManager;
385
386 char szPath[256];
387
388 if ( GetModuleFileName( nullptr, szPath, 256 ) == 0 )
389 {
390 printf("Unable to install %s - %s\n", name, GetLastErrorText(szErr, 256));
391 return;
392 }
393
394 string dispName = string("Shibboleth SP Daemon (") + name + ")";
395 string realName = string("shibd_") + name;
396 string cmd(szPath);
397 if (shar_prefix)
398 cmd = cmd + " -prefix " + shar_prefix;
399 if (shar_config)
400 cmd = cmd + " -config " + shar_config;
401 if (shar_schemadir)
402 cmd = cmd + " -schemadir " + shar_schemadir;
403 if (lpszStdout)
404 cmd = cmd + " -stdout " + lpszStdout;
405 if (lpszStderr)
406 cmd = cmd + " -stderr " + lpszStderr;
407
408 schSCManager = OpenSCManager(
409 nullptr, // machine (nullptr == local)
410 nullptr, // database (nullptr == default)
411 SC_MANAGER_ALL_ACCESS // access required
412 );
413
414
415 if ( schSCManager )
416 {
417 schService = CreateService(
418 schSCManager, // SCManager database
419 realName.c_str(), // name of service
420 dispName.c_str(), // name to display
421 SERVICE_ALL_ACCESS, // desired access
422 SERVICE_WIN32_OWN_PROCESS, // service type
423 SERVICE_AUTO_START, // start type
424 SERVICE_ERROR_NORMAL, // error control type
425 cmd.c_str(), // service's command line
426 nullptr, // no load ordering group
427 nullptr, // no tag identifier
428 nullptr, // dependencies
429 nullptr, // LocalSystem account
430 nullptr); // no password
431
432 if ( schService )
433 {
434 printf("%s installed.\n", realName.c_str());
435 CloseServiceHandle(schService);
436 }
437 else
438 {
439 printf("CreateService failed - %s\n", GetLastErrorText(szErr, 256));
440 }
441
442 CloseServiceHandle(schSCManager);
443 }
444 else
445 printf("OpenSCManager failed - %s\n", GetLastErrorText(szErr,256));
446 }
447
CmdRemoveService(LPCSTR name)448 void CmdRemoveService(LPCSTR name)
449 {
450 SC_HANDLE schService;
451 SC_HANDLE schSCManager;
452 char realName[512];
453
454 _snprintf(realName,sizeof(realName),"shibd_%s",name);
455
456 schSCManager = OpenSCManager(
457 nullptr, // machine (nullptr == local)
458 nullptr, // database (nullptr == default)
459 SC_MANAGER_ALL_ACCESS // access required
460 );
461 if ( schSCManager )
462 {
463 schService = OpenService(schSCManager, realName, SERVICE_ALL_ACCESS);
464
465 if (schService)
466 {
467 // try to stop the service
468 if ( ControlService( schService, SERVICE_CONTROL_STOP, &ssStatus ) )
469 {
470 printf("Stopping shibd (%s).", name);
471 Sleep( 1000 );
472
473 while( QueryServiceStatus( schService, &ssStatus ) )
474 {
475 if ( ssStatus.dwCurrentState == SERVICE_STOP_PENDING )
476 {
477 printf(".");
478 Sleep( 1000 );
479 }
480 else
481 break;
482 }
483
484 if ( ssStatus.dwCurrentState == SERVICE_STOPPED )
485 printf("\n%s stopped.\n", realName);
486 else
487 printf("\n%s failed to stop.\n", realName);
488
489 }
490
491 // now remove the service
492 if( DeleteService(schService) )
493 printf("%s removed.\n", realName);
494 else
495 printf("DeleteService failed - %s\n", GetLastErrorText(szErr,256));
496
497
498 CloseServiceHandle(schService);
499 }
500 else
501 printf("OpenService failed - %s\n", GetLastErrorText(szErr,256));
502
503 CloseServiceHandle(schSCManager);
504 }
505 else
506 printf("OpenSCManager failed - %s\n", GetLastErrorText(szErr,256));
507 }
508
509
510 //
511 // FUNCTION: GetLastErrorText
512 //
513 // PURPOSE: copies error message text to string
514 //
515 // PARAMETERS:
516 // lpszBuf - destination buffer
517 // dwSize - size of buffer
518 //
519 // RETURN VALUE:
520 // destination buffer
521 //
522 // COMMENTS:
523 //
GetLastErrorText(LPSTR lpszBuf,DWORD dwSize)524 LPTSTR GetLastErrorText( LPSTR lpszBuf, DWORD dwSize )
525 {
526 DWORD dwRet;
527 LPSTR lpszTemp = nullptr;
528
529 dwRet = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
530 nullptr,
531 GetLastError(),
532 LANG_NEUTRAL,
533 (LPSTR)&lpszTemp,
534 0,
535 nullptr );
536
537 // supplied buffer is not long enough
538 if ( !dwRet || ( (long)dwSize < (long)dwRet+14 ) )
539 lpszBuf[0] = '\0';
540 else
541 {
542 lpszTemp[lstrlen(lpszTemp)-2] = '\0'; //remove cr and newline character
543 sprintf( lpszBuf, "%s (0x%x)", lpszTemp, GetLastError() );
544 }
545
546 if ( lpszTemp )
547 LocalFree((HLOCAL) lpszTemp );
548
549 return lpszBuf;
550 }
551