a22b6fbb860bb30dd7392d52a2b11c5a471933d2
[shibboleth/sp.git] / shar / shar_win32.cpp
1 /*
2  * The Shibboleth License, Version 1.
3  * Copyright (c) 2002
4  * University Corporation for Advanced Internet Development, Inc.
5  * All rights reserved
6  *
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  *
11  * Redistributions of source code must retain the above copyright notice, this
12  * list of conditions and the following disclaimer.
13  *
14  * Redistributions in binary form must reproduce the above copyright notice,
15  * this list of conditions and the following disclaimer in the documentation
16  * and/or other materials provided with the distribution, if any, must include
17  * the following acknowledgment: "This product includes software developed by
18  * the University Corporation for Advanced Internet Development
19  * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement
20  * may appear in the software itself, if and wherever such third-party
21  * acknowledgments normally appear.
22  *
23  * Neither the name of Shibboleth nor the names of its contributors, nor
24  * Internet2, nor the University Corporation for Advanced Internet Development,
25  * Inc., nor UCAID may be used to endorse or promote products derived from this
26  * software without specific prior written permission. For written permission,
27  * please contact shibboleth@shibboleth.org
28  *
29  * Products derived from this software may not be called Shibboleth, Internet2,
30  * UCAID, or the University Corporation for Advanced Internet Development, nor
31  * may Shibboleth appear in their name, without prior written permission of the
32  * University Corporation for Advanced Internet Development.
33  *
34  *
35  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
36  * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
38  * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK
39  * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE.
40  * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY
41  * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT,
42  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
43  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
44  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
47  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48  */
49
50 /*
51  * shar_win32.cpp -- the SHAR "main" code on Win32
52  *
53  * Created By:  Scott Cantor (cantor.2@osu.edu)
54  *
55  * $Id$
56  */
57
58 #include "config_win32.h"
59 #include "shar-utils.h"
60
61 extern int shar_run;                    // signals shutdown to Unix side
62 extern const char* shar_schemadir;
63 extern const char* shar_config;
64 extern bool shar_checkonly;
65
66 // internal variables
67 SERVICE_STATUS          ssStatus;       // current status of the service
68 SERVICE_STATUS_HANDLE   sshStatusHandle;
69 DWORD                   dwErr = 0;
70 BOOL                    bConsole = FALSE;
71 char                    szErr[256];
72 LPCSTR                  lpszInstall = NULL;
73 LPCSTR                  lpszRemove = NULL;
74
75 // internal function prototypes
76 VOID WINAPI service_ctrl(DWORD dwCtrlCode);
77 VOID WINAPI service_main(DWORD dwArgc, LPSTR *lpszArgv);
78 VOID CmdInstallService(LPCSTR);
79 VOID CmdRemoveService(LPCSTR);
80 LPTSTR GetLastErrorText( LPSTR lpszBuf, DWORD dwSize );
81
82 BOOL LogEvent(
83     LPCTSTR  lpUNCServerName,
84     WORD  wType,
85     DWORD  dwEventID,
86     PSID  lpUserSid,
87     LPCTSTR  message);
88
89 VOID ServiceStart(DWORD dwArgc, LPSTR *lpszArgv);
90 VOID ServiceStop();
91 BOOL ReportStatusToSCMgr(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint);
92 void AddToMessageLog(LPSTR lpszMsg);
93
94 BOOL WINAPI BreakHandler(DWORD dwCtrlType)
95 {
96    switch(dwCtrlType)
97     {
98         case CTRL_BREAK_EVENT:  // use Ctrl+C or Ctrl+Break to simulate
99         case CTRL_C_EVENT:      // SERVICE_CONTROL_STOP in console mode
100             ServiceStop();
101             return TRUE;
102     }
103     return FALSE;
104 }
105
106
107 int real_main(int);  // The revised two-phase main() in shar.cpp
108
109 int main(int argc, char *argv[])
110 {
111     int i=1;
112     while ((argc > i) && ((*argv[i] == '-') || (*argv[i] == '/')))
113     {
114         if (_stricmp("install", argv[i]+1) == 0)
115         {
116             if (argc > ++i)
117                 lpszInstall = argv[i++];
118         }
119         else if (_stricmp("remove", argv[i]+1) == 0)
120         {
121             if (argc > ++i)
122                 lpszRemove = argv[i++];
123         }
124         else if (_stricmp( "console", argv[i]+1) == 0)
125         {
126             i++;
127             bConsole = TRUE;
128         }
129         else if (_stricmp( "check", argv[i]+1) == 0)
130         {
131             i++;
132             bConsole = TRUE;
133             shar_checkonly=true;
134         }
135         else if (_stricmp( "config", argv[i]+1) == 0)
136         {
137             if (argc > ++i)
138                 shar_config = argv[i++];
139         }
140         else if (_stricmp( "schemadir", argv[i]+1) == 0)
141         {
142             if (argc > ++i)
143                 shar_schemadir = argv[i++];
144         }
145         else
146         {
147             goto dispatch;
148         }
149     }
150     
151     if (bConsole)
152     {
153         // Install break handler, then run the C routine twice, once to setup, once to start running.
154         SetConsoleCtrlHandler(&BreakHandler,TRUE);
155         if (real_main(1)!=0)
156         {
157             LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, "shibd startup failed, check shibd.log for further details");
158             return -1;
159         }
160         return real_main(0);
161     }
162     else if (lpszInstall)
163     {
164         CmdInstallService(lpszInstall);
165         return 0;
166     }
167     else if (lpszRemove)
168     {
169         CmdRemoveService(lpszRemove);
170         return 0;
171     }
172     
173
174     // if it doesn't match any of the above parameters
175     // the service control manager may be starting the service
176     // so we must call StartServiceCtrlDispatcher
177     dispatch:
178         // this is just to be friendly
179         printf("%s -install <name>   to install the named service\n", argv[0]);
180         printf("%s -remove <name>    to remove the named service\n", argv[0]);
181         printf("%s -console          to run as a console app for debugging\n", argv[0]);
182         printf("%s -check            to run as a console app and check configuration\n", argv[0]);
183         printf("\t-config <file> to specify the config file to use\n");
184         printf("\t-schemadir <dir> to specify where schemas are\n");
185         printf("\nService starting.\nThis may take several seconds. Please wait.\n" );
186
187     SERVICE_TABLE_ENTRY dispatchTable[] =
188     {
189         { "SHIBD", (LPSERVICE_MAIN_FUNCTION)service_main },
190         { NULL, NULL }
191     };
192
193     if (!StartServiceCtrlDispatcher(dispatchTable))
194         LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, "StartServiceCtrlDispatcher failed.");
195     return 0;
196 }
197
198 //
199 //  FUNCTION: ServiceStart
200 //
201 //  PURPOSE: Actual code of the service
202 //          that does the work.
203 //
204 VOID ServiceStart (DWORD dwArgc, LPSTR *lpszArgv)
205 {
206
207     if (real_main(1)!=0)
208     {
209         LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, "shibd startup failed, check shibd.log for further details");
210         return;
211     }
212
213     LogEvent(NULL, EVENTLOG_INFORMATION_TYPE, 7700, NULL, "shibd started successfully.");
214
215     if (!ReportStatusToSCMgr(SERVICE_RUNNING, NO_ERROR, 0))
216         return;
217
218     real_main(0);
219 }
220
221
222 //
223 //  FUNCTION: ServiceStop
224 //
225 //   PURPOSE: Stops the service
226 //
227 VOID ServiceStop()
228 {
229     if (!bConsole)
230         LogEvent(NULL, EVENTLOG_INFORMATION_TYPE, 7701, NULL, "shibd stopping...");
231     shar_run=0;
232 }
233
234
235 void WINAPI service_main(DWORD dwArgc, LPSTR *lpszArgv)
236 {
237
238     // register our service control handler:
239     sshStatusHandle=RegisterServiceCtrlHandler(lpszArgv[0], service_ctrl);
240     if (!sshStatusHandle)
241         goto cleanup;
242
243     // SERVICE_STATUS members that don't change in example
244     ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
245     ssStatus.dwServiceSpecificExitCode = 0;
246
247
248     // report the status to the service control manager.
249     if (!ReportStatusToSCMgr(
250         SERVICE_START_PENDING, // service state
251         NO_ERROR,              // exit code
252         3000))                 // wait hint
253         goto cleanup;
254
255
256     ServiceStart(dwArgc, lpszArgv);
257
258 cleanup:
259
260     // try to report the stopped status to the service control manager.
261     //
262     if (sshStatusHandle)
263         (VOID)ReportStatusToSCMgr(
264                             SERVICE_STOPPED,
265                             dwErr,
266                             0);
267
268     return;
269 }
270
271
272 //
273 //  FUNCTION: service_ctrl
274 //
275 //  PURPOSE: This function is called by the SCM whenever
276 //           ControlService() is called on this service.
277 //
278 //  PARAMETERS:
279 //    dwCtrlCode - type of control requested
280 //
281 //  RETURN VALUE:
282 //    none
283 //
284 VOID WINAPI service_ctrl(DWORD dwCtrlCode)
285 {
286     // Handle the requested control code.
287     //
288     switch(dwCtrlCode)
289     {
290         // Stop the service.
291         //
292         case SERVICE_CONTROL_STOP:
293             ssStatus.dwCurrentState = SERVICE_STOP_PENDING;
294             ServiceStop();
295             break;
296
297         // Update the service status.
298         //
299         case SERVICE_CONTROL_INTERROGATE:
300             break;
301
302         // invalid control code
303         //
304         default:
305             break;
306
307     }
308
309     ReportStatusToSCMgr(ssStatus.dwCurrentState, NO_ERROR, 0);
310 }
311
312
313 //
314 //  FUNCTION: ReportStatusToSCMgr()
315 //
316 //  PURPOSE: Sets the current status of the service and
317 //           reports it to the Service Control Manager
318 //
319 //  PARAMETERS:
320 //    dwCurrentState - the state of the service
321 //    dwWin32ExitCode - error code to report
322 //    dwWaitHint - worst case estimate to next checkpoint
323 //
324 //  RETURN VALUE:
325 //    TRUE  - success
326 //    FALSE - failure
327 //
328 BOOL ReportStatusToSCMgr(DWORD dwCurrentState,
329                          DWORD dwWin32ExitCode,
330                          DWORD dwWaitHint)
331 {
332     static DWORD dwCheckPoint = 1;
333     BOOL fResult = TRUE;
334
335
336     if (!bConsole) // when console we don't report to the SCM
337     {
338         if (dwCurrentState == SERVICE_START_PENDING)
339             ssStatus.dwControlsAccepted = 0;
340         else
341             ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
342
343         ssStatus.dwCurrentState = dwCurrentState;
344         ssStatus.dwWin32ExitCode = dwWin32ExitCode;
345         ssStatus.dwWaitHint = dwWaitHint;
346
347         if ( ( dwCurrentState == SERVICE_RUNNING ) ||
348              ( dwCurrentState == SERVICE_STOPPED ) )
349             ssStatus.dwCheckPoint = 0;
350         else
351             ssStatus.dwCheckPoint = dwCheckPoint++;
352
353
354         // Report the status of the service to the service control manager.
355         //
356         if (!(fResult = SetServiceStatus(sshStatusHandle, &ssStatus)))
357             LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, "SetServiceStatus failed.");
358     }
359     return fResult;
360 }
361
362
363 ///////////////////////////////////////////////////////////////////
364 //
365 //  The following code handles service installation and removal
366 //
367 //
368 void CmdInstallService(LPCSTR name)
369 {
370     SC_HANDLE   schService;
371     SC_HANDLE   schSCManager;
372
373     char szPath[256];
374     char dispName[512];
375     char realName[512];
376     char cmd[2048];
377
378     if ( GetModuleFileName( NULL, szPath, 256 ) == 0 )
379     {
380         printf("Unable to install %s - %s\n", name, GetLastErrorText(szErr, 256));
381         return;
382     }
383     
384     sprintf(dispName,"Shibboleth %s Daemon (%s)",PACKAGE_VERSION,name);
385     sprintf(realName,"shibd_%s",name);
386     if (shar_config && shar_schemadir)
387         sprintf(cmd,"%s -config %s -schemadir %s",szPath,shar_config,shar_schemadir);
388     else if (shar_config)
389         sprintf(cmd,"%s -config %s",szPath,shar_config);
390     else if (shar_schemadir)
391         sprintf(cmd,"%s -schemadir %s",szPath,shar_schemadir);
392     else
393         sprintf(cmd,"%s",szPath);
394
395     schSCManager = OpenSCManager(
396                         NULL,                   // machine (NULL == local)
397                         NULL,                   // database (NULL == default)
398                         SC_MANAGER_ALL_ACCESS   // access required
399                         );
400     
401     
402     if ( schSCManager )
403     {
404         schService = CreateService(
405             schSCManager,               // SCManager database
406             realName,                   // name of service
407             dispName,                   // name to display
408             SERVICE_ALL_ACCESS,         // desired access
409             SERVICE_WIN32_OWN_PROCESS,  // service type
410             SERVICE_AUTO_START,         // start type
411             SERVICE_ERROR_NORMAL,       // error control type
412             cmd,                        // service's command line
413             NULL,                       // no load ordering group
414             NULL,                       // no tag identifier
415             NULL,                       // dependencies
416             NULL,                       // LocalSystem account
417             NULL);                      // no password
418
419         if ( schService )
420         {
421             printf("%s installed.\n",realName);
422             CloseServiceHandle(schService);
423         }
424         else
425         {
426             printf("CreateService failed - %s\n", GetLastErrorText(szErr, 256));
427         }
428
429         CloseServiceHandle(schSCManager);
430     }
431     else
432         printf("OpenSCManager failed - %s\n", GetLastErrorText(szErr,256));
433 }
434
435 void CmdRemoveService(LPCSTR name)
436 {
437     SC_HANDLE   schService;
438     SC_HANDLE   schSCManager;
439     char        realName[512];
440
441     sprintf(realName,"shibd_%s",name);
442
443     schSCManager = OpenSCManager(
444                         NULL,                   // machine (NULL == local)
445                         NULL,                   // database (NULL == default)
446                         SC_MANAGER_ALL_ACCESS   // access required
447                         );
448     if ( schSCManager )
449     {
450         schService = OpenService(schSCManager, realName, SERVICE_ALL_ACCESS);
451
452         if (schService)
453         {
454             // try to stop the service
455             if ( ControlService( schService, SERVICE_CONTROL_STOP, &ssStatus ) )
456             {
457                 printf("Stopping shibd (%s).", name);
458                 Sleep( 1000 );
459
460                 while( QueryServiceStatus( schService, &ssStatus ) )
461                 {
462                     if ( ssStatus.dwCurrentState == SERVICE_STOP_PENDING )
463                     {
464                         printf(".");
465                         Sleep( 1000 );
466                     }
467                     else
468                         break;
469                 }
470
471                 if ( ssStatus.dwCurrentState == SERVICE_STOPPED )
472                     printf("\n%s stopped.\n", realName);
473                 else
474                     printf("\n%s failed to stop.\n", realName);
475
476             }
477
478             // now remove the service
479             if( DeleteService(schService) )
480                 printf("%s removed.\n", realName);
481             else
482                 printf("DeleteService failed - %s\n", GetLastErrorText(szErr,256));
483
484
485             CloseServiceHandle(schService);
486         }
487         else
488             printf("OpenService failed - %s\n", GetLastErrorText(szErr,256));
489
490         CloseServiceHandle(schSCManager);
491     }
492     else
493         printf("OpenSCManager failed - %s\n", GetLastErrorText(szErr,256));
494 }
495
496
497 //
498 //  FUNCTION: GetLastErrorText
499 //
500 //  PURPOSE: copies error message text to string
501 //
502 //  PARAMETERS:
503 //    lpszBuf - destination buffer
504 //    dwSize - size of buffer
505 //
506 //  RETURN VALUE:
507 //    destination buffer
508 //
509 //  COMMENTS:
510 //
511 LPTSTR GetLastErrorText( LPSTR lpszBuf, DWORD dwSize )
512 {
513     DWORD dwRet;
514     LPSTR lpszTemp = NULL;
515
516     dwRet = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
517                            NULL,
518                            GetLastError(),
519                            LANG_NEUTRAL,
520                            (LPSTR)&lpszTemp,
521                            0,
522                            NULL );
523
524     // supplied buffer is not long enough
525     if ( !dwRet || ( (long)dwSize < (long)dwRet+14 ) )
526         lpszBuf[0] = '\0';
527     else
528     {
529         lpszTemp[lstrlen(lpszTemp)-2] = '\0';  //remove cr and newline character
530         sprintf( lpszBuf, "%s (0x%x)", lpszTemp, GetLastError() );
531     }
532
533     if ( lpszTemp )
534         LocalFree((HLOCAL) lpszTemp );
535
536     return lpszBuf;
537 }
538
539 BOOL LogEvent(
540     LPCSTR  lpUNCServerName,
541     WORD  wType,
542     DWORD  dwEventID,
543     PSID  lpUserSid,
544     LPCSTR  message)
545 {
546     LPCSTR  messages[] = {message, NULL};
547     
548     HANDLE hElog = RegisterEventSource(lpUNCServerName, "Shibboleth Daemon");
549     BOOL res = ReportEvent(hElog, wType, 0, dwEventID, lpUserSid, 1, 0, messages, NULL);
550     return (DeregisterEventSource(hElog) && res);
551 }