2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
10 /* This file implements the AFS BOS control service. Basically, it provides
11 * a mechanism to start and stop the AFS bosserver via the NT SCM; it also
12 * supports bosserver restart.
16 #include <afs/param.h>
27 #include <WINNT/afsevent.h>
28 #include <WINNT/afsreg.h>
29 #include <afs/procmgmt.h>
30 #include <afs/dirpath.h>
31 #include <afs/bnode.h>
32 #include <afs/afsicf.h>
36 #define BOSSERVER_STARTMSG_EXE "afslegal.exe"
38 #define BOSSERVER_RESTART_ARG_MAX 2 /* "-noauth", "-log" */
39 #define BOSSERVER_WAIT_TIME_HINT 60 /* seconds */
40 #define BOSSERVER_STOP_TIME_MAX (FSSDTIME + 60) /* seconds */
42 #define BOS_CONTROLS_ACCEPTED SERVICE_ACCEPT_STOP
44 static CRITICAL_SECTION bosCtlStatusLock; /* protects bosCtlStatus */
45 static SERVICE_STATUS bosCtlStatus;
46 static SERVICE_STATUS_HANDLE bosCtlStatusHandle;
48 /* note: events arranged in priority order in case multiple signaled */
49 #define BOS_STOP_EVENT 0
50 #define BOS_EXIT_EVENT 1
51 #define BOS_EVENT_COUNT 2
52 static HANDLE bosCtlEvent[BOS_EVENT_COUNT];
55 /* Declare local functions */
57 static void AsyncSignalCatcher(int signo);
59 static void BosCtlStatusInit(DWORD initState);
61 static DWORD BosCtlStatusUpdate(DWORD newState,
65 static DWORD BosCtlStatusReport(void);
67 static void WINAPI BosCtlHandler(DWORD controlCode);
69 static void WINAPI BosCtlMain(DWORD argc,
72 static void BosserverDoStopEvent(pid_t cpid,
76 static void BosserverDoExitEvent(pid_t cpid,
83 static void BosserverRun(DWORD argc,
88 static void BosserverStartupMsgDisplay(void);
94 * AsyncSignalCatcher() -- Handle asynchronous signals sent to process
97 AsyncSignalCatcher(int signo)
99 if (signo == SIGCHLD) {
100 (void) SetEvent(bosCtlEvent[BOS_EXIT_EVENT]);
106 * BosCtlStatusInit() -- initialize BOS control service status structure
109 BosCtlStatusInit(DWORD initState)
111 bosCtlStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
112 bosCtlStatus.dwCurrentState = initState;
114 if (initState == SERVICE_RUNNING) {
115 bosCtlStatus.dwControlsAccepted = BOS_CONTROLS_ACCEPTED;
117 bosCtlStatus.dwControlsAccepted = 0;
120 bosCtlStatus.dwWin32ExitCode = 0;
121 bosCtlStatus.dwServiceSpecificExitCode = 0;
122 bosCtlStatus.dwCheckPoint = 0;
123 bosCtlStatus.dwWaitHint = BOSSERVER_WAIT_TIME_HINT * 1000; /* millisecs */
125 InitializeCriticalSection(&bosCtlStatusLock);
130 * BosCtlStatusUpdate() -- update BOS control service status and report to SCM
133 BosCtlStatusUpdate(DWORD newState, DWORD exitCode, BOOL isWin32Code)
137 EnterCriticalSection(&bosCtlStatusLock);
139 /* SERVICE_STOPPED is a terminal state; never transition out of it */
140 if (bosCtlStatus.dwCurrentState != SERVICE_STOPPED) {
142 if ((bosCtlStatus.dwCurrentState == newState) &&
143 (newState == SERVICE_START_PENDING ||
144 newState == SERVICE_STOP_PENDING)) {
145 /* continuing a pending state; increment checkpoint value */
146 bosCtlStatus.dwCheckPoint++;
148 /* not continuing a pending state; reset checkpoint value */
149 bosCtlStatus.dwCheckPoint = 0;
152 bosCtlStatus.dwCurrentState = newState;
154 if (newState == SERVICE_RUNNING) {
155 bosCtlStatus.dwControlsAccepted = BOS_CONTROLS_ACCEPTED;
157 bosCtlStatus.dwControlsAccepted = 0;
161 bosCtlStatus.dwWin32ExitCode = exitCode;
162 bosCtlStatus.dwServiceSpecificExitCode = 0;
164 bosCtlStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
165 bosCtlStatus.dwServiceSpecificExitCode = exitCode;
169 if (!SetServiceStatus(bosCtlStatusHandle, &bosCtlStatus)) {
173 LeaveCriticalSection(&bosCtlStatusLock);
180 * BosCtlStatusReport() -- report current BOS control service status to SCM
183 BosCtlStatusReport(void)
187 EnterCriticalSection(&bosCtlStatusLock);
189 if (!SetServiceStatus(bosCtlStatusHandle, &bosCtlStatus)) {
193 LeaveCriticalSection(&bosCtlStatusLock);
200 * BosCtlHandler() -- control handler for BOS control service
203 BosCtlHandler(DWORD controlCode)
205 switch (controlCode) {
206 case SERVICE_CONTROL_STOP:
207 (void) SetEvent(bosCtlEvent[BOS_STOP_EVENT]);
208 (void) BosCtlStatusUpdate(SERVICE_STOP_PENDING, 0, TRUE);
212 (void) BosCtlStatusReport();
219 * BosCtlMain() -- main function for BOS control service
222 BosCtlMain(DWORD argc, LPTSTR *argv)
226 struct sigaction childAction;
228 /* Initialize status structure */
229 BosCtlStatusInit(SERVICE_START_PENDING);
231 /* Create events used by service control handler and signal handler */
232 if ((bosCtlEvent[BOS_STOP_EVENT] = CreateEvent(NULL,
233 FALSE /* manual reset */,
234 FALSE /* initial state */,
235 TEXT("BosCtlSvc Stop Event"))) == NULL) {
236 status = GetLastError();
239 if ((bosCtlEvent[BOS_EXIT_EVENT] = CreateEvent(NULL,
240 FALSE /* manual reset */,
241 FALSE /* initial state */,
242 TEXT("BosCtlSvc Exit Event"))) == NULL) {
243 status = GetLastError();
246 /* Register service control handler */
247 bosCtlStatusHandle = RegisterServiceCtrlHandler(AFSREG_SVR_SVC_NAME,
249 if (bosCtlStatusHandle == 0) {
250 /* failed to register control handler for service; can not continue */
251 (void) ReportErrorEventAlt(AFSEVT_SVR_BCS_HANDLER_REG_FAILED,
252 (int)GetLastError(), NULL);
253 /* can not report status to SCM w/o a valid bosCtlStatusHandle */
257 /* Stop immediately if required system resources could not be obtained */
258 if (bosCtlEvent[BOS_STOP_EVENT] == NULL ||
259 bosCtlEvent[BOS_EXIT_EVENT] == NULL) {
260 (void) ReportErrorEventAlt(AFSEVT_SVR_BCS_INSUFFICIENT_RESOURCES,
262 (void) BosCtlStatusUpdate(SERVICE_STOPPED, status, TRUE);
266 /* Report pending start status */
267 if (status = BosCtlStatusUpdate(SERVICE_START_PENDING, 0, TRUE)) {
268 /* can't inform SCM of pending start; give up before really start */
269 (void) ReportErrorEventAlt(AFSEVT_SVR_BCS_SCM_COMM_FAILED,
271 (void) BosCtlStatusUpdate(SERVICE_STOPPED, status, TRUE);
275 /* For XP SP2 and above, open required ports */
276 icf_CheckAndAddAFSPorts(AFS_PORTSET_SERVER);
278 /* Initialize the dirpath package so can access local bosserver binary */
279 if (!(initAFSDirPath() & AFSDIR_SERVER_PATHS_OK)) {
280 /* sw install directory probably not in registry; can not continue */
281 (void) ReportErrorEventAlt(AFSEVT_SVR_BCS_NO_INSTALL_DIR, 0, NULL);
282 (void) BosCtlStatusUpdate(SERVICE_STOPPED, 0, TRUE);
286 /* Install SIGCHLD handler to catch bosserver restarts and failures */
287 childAction.sa_handler = AsyncSignalCatcher;
288 sigfillset(&childAction.sa_mask);
289 childAction.sa_flags = 0;
291 if (sigaction(SIGCHLD, &childAction, NULL)) {
292 /* handler install should never fail, but can't continue if it does */
294 (void) ReportErrorEventAlt(AFSEVT_SVR_BCS_INTERNAL_ERROR,
296 (void) BosCtlStatusUpdate(SERVICE_STOPPED, status, FALSE);
300 /* Run the AFS bosserver, handling stop and exit events */
301 BosserverRun(argc, argv, &status, &isWin32Code);
303 (void) BosCtlStatusUpdate(SERVICE_STOPPED, status, isWin32Code);
308 * BosserverDoStopEvent() -- Handle a stop event for the AFS bosserver.
311 BosserverDoStopEvent(pid_t cpid, DWORD *stopStatus, BOOL *isWin32Code)
313 (void) BosCtlStatusUpdate(SERVICE_STOP_PENDING, 0, TRUE);
315 if (kill(cpid, SIGQUIT) == 0) {
316 /* bosserver has been told to stop; wait for this to happen */
317 BOOL gotWaitStatus = FALSE;
318 time_t timeStart = time(NULL);
324 if (waitpid(cpid, &waitStatus, WNOHANG) == cpid) {
325 /* bosserver status available */
326 if (WIFEXITED(waitStatus) && WEXITSTATUS(waitStatus) == 0) {
327 /* bosserver exited w/o any error */
331 *stopStatus = waitStatus;
332 *isWin32Code = FALSE;
334 gotWaitStatus = TRUE;
338 /* wait for bosserver status to become available;
339 * update BOS control service status periodically.
341 status = WaitForSingleObject(bosCtlEvent[BOS_EXIT_EVENT],
342 BOSSERVER_WAIT_TIME_HINT * 1000 / 2);
343 if (status == WAIT_FAILED) {
344 /* failed to wait on event; should never happen */
345 Sleep(2000); /* sleep to avoid tight loop if event problem */
347 (void) BosCtlStatusUpdate(SERVICE_STOP_PENDING, 0, TRUE);
348 } while (difftime(time(NULL), timeStart) < BOSSERVER_STOP_TIME_MAX);
350 if (!gotWaitStatus) {
351 /* timed out waiting to get bosserver status */
353 *isWin32Code = FALSE;
355 (void) ReportWarningEventAlt(AFSEVT_SVR_BCS_BOSSERVER_STOP_TIMEOUT,
356 (int)*stopStatus, NULL);
360 /* can't tell bosserver to stop; should never happen */
362 *isWin32Code = FALSE;
364 (void) ReportErrorEventAlt(AFSEVT_SVR_BCS_BOSSERVER_STOP_FAILED,
365 (int)*stopStatus, NULL);
371 * BosserverDoExitEvent() -- Handle an exit event for the AFS bosserver.
373 * The output arguments for this function are set as follows:
374 * Case 1: bosserver did not exit (spurious SIGCHLD);
375 * *doWait is set to TRUE.
376 * Case 2: bosserver exited with restart code;
377 * *doRestart is set to TRUE, restartArgv[] is defined.
378 * Case 3: bosserver exited with non-restart code;
379 * *stopStatus and *isWin32Code are defined.
382 BosserverDoExitEvent(pid_t cpid,
394 if (waitpid(cpid, &waitStatus, WNOHANG) == cpid) {
395 /* bosserver status available */
397 if (WIFEXITED(waitStatus)) {
398 /* bosserver exited normally; check for restart code */
399 int exitCode = WEXITSTATUS(waitStatus);
401 if (BOSEXIT_DORESTART(exitCode)) {
402 /* bosserver requests restart */
406 /* set up bosserver argument list */
407 restartArgv[0] = (char *)AFSDIR_SERVER_BOSVR_FILEPATH;
410 if (exitCode & BOSEXIT_NOAUTH_FLAG) {
411 /* pass "-noauth" to new bosserver */
412 restartArgv[i] = "-noauth";
415 if (exitCode & BOSEXIT_LOGGING_FLAG) {
416 /* pass "-log" to new bosserver */
417 restartArgv[i] = "-log";
420 if (exitCode & BOSEXIT_RXBIND_FLAG) {
421 /* pass "-rxbind" to new bosserver */
422 restartArgv[i] = "-rxbind";
425 restartArgv[i] = NULL;
430 /* bosserver exited with non-restart code; set status */
431 *stopStatus = waitStatus;
432 *isWin32Code = FALSE;
434 (void) ReportWarningEventAlt(AFSEVT_SVR_BCS_BOSSERVER_EXIT,
435 (int)*stopStatus, NULL);
439 /* bosserver status NOT available; assume spurious SIGCHLD */
446 * BosserverRun() -- Run the AFS bosserver, handling stop and exit events.
448 * Input args are those passed to the service's main function (BosCtlMain).
449 * Output args are the stop status and status type of the bosserver.
452 BosserverRun(DWORD argc,
458 BOOL doRestart, doWait;
461 /* Display bosserver startup (legal) message; first start only */
462 /* BosserverStartupMsgDisplay(); */
464 /* Set env variable forcing process mgmt lib to spawn processes detached */
465 (void)putenv(PMGT_SPAWN_DETACHED_ENV_NAME "=1");
467 /* Alloc block with room for at least BOSSERVER_RESTART_ARG_MAX args */
468 i = max((argc + 1), (BOSSERVER_RESTART_ARG_MAX + 2));
469 spawn_argv = (char **)malloc(i * sizeof(char *));
471 if (spawn_argv == NULL) {
472 /* failed to malloc required space; can not continue */
473 *stopStatus = ENOMEM;
474 *isWin32Code = FALSE;
476 (void) ReportErrorEventAlt(AFSEVT_SVR_BCS_INSUFFICIENT_RESOURCES,
477 (int)*stopStatus, NULL);
481 /* Set initial bosserver args to those supplied via StartService() */
482 spawn_argv[0] = (char *)AFSDIR_SERVER_BOSVR_FILEPATH;
484 for (i = 1; i < argc; i++) {
485 spawn_argv[i] = argv[i];
487 spawn_argv[i] = NULL;
489 /* Start/restart bosserver and wait for either a stop or exit event */
496 /* restarting bosserver; log informational message */
497 (void) ReportInformationEventAlt(AFSEVT_SVR_BCS_BOSSERVER_RESTART,
502 cpid = spawnprocve(spawn_argv[0], spawn_argv, NULL, 0);
504 if (cpid == (pid_t)-1) {
505 /* failed to start/restart the bosserver process */
507 *isWin32Code = FALSE;
509 (void) ReportErrorEventAlt(AFSEVT_SVR_BCS_BOSSERVER_START_FAILED,
510 (int)*stopStatus, NULL);
514 if (status = BosCtlStatusUpdate(SERVICE_RUNNING, 0, TRUE)) {
515 /* can't tell SCM we're running so quit; should never occur */
516 (void) ReportErrorEventAlt(AFSEVT_SVR_BCS_SCM_COMM_FAILED,
518 (void) SetEvent(bosCtlEvent[BOS_STOP_EVENT]);
521 /* bosserver is running; wait for an event of interest */
523 Sleep(5000); /* bosserver needs time to register signal handler */
528 status = WaitForMultipleObjects(BOS_EVENT_COUNT,
529 bosCtlEvent, FALSE, INFINITE);
531 if ((status - WAIT_OBJECT_0) == BOS_STOP_EVENT) {
532 /* stop event signaled */
533 BosserverDoStopEvent(cpid, stopStatus, isWin32Code);
535 } else if ((status - WAIT_OBJECT_0) == BOS_EXIT_EVENT) {
536 /* exit event signaled; see function comment for outcomes */
537 BosserverDoExitEvent(cpid,
539 &doRestart, spawn_argv,
540 stopStatus, isWin32Code);
543 /* failed to wait on events; should never happen */
544 Sleep(2000); /* sleep to avoid tight loop if event problem */
555 * BosserverStartupMsgDisplay() -- display Windows version of AFS bosserver
556 * startup (legal) message.
559 BosserverStartupMsgDisplay(void)
563 if (!ConstructLocalBinPath(BOSSERVER_STARTMSG_EXE, &msgPath)) {
564 /* Use C runtime spawn; don't need all the machinery in the
565 * process management library.
567 (void)_spawnl(_P_DETACH, msgPath, BOSSERVER_STARTMSG_EXE, NULL);
574 * main() -- start dispatcher thread for BOS control service
578 SERVICE_TABLE_ENTRY dispatchTable[] = {{AFSREG_SVR_SVC_NAME, BosCtlMain},
581 (void) ReportInformationEventAlt(AFSEVT_SVR_BCS_STARTED, NULL);
583 if (!StartServiceCtrlDispatcher(dispatchTable)) {
584 /* unable to connect to SCM */
585 (void) ReportErrorEventAlt(AFSEVT_SVR_BCS_SCM_COMM_FAILED,
586 (int)GetLastError(), NULL);
589 (void) ReportInformationEventAlt(AFSEVT_SVR_BCS_STOPPED, NULL);