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