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