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