1 /* Copyright (C) 1998 Transarc Corporation. All rights reserved.
5 /* This file implements the AFS BOS control service. Basically, it provides
6 * a mechanism to start and stop the AFS bosserver via the NT SCM; it also
7 * supports bosserver restart.
11 #include <afs/param.h>
23 #include <WINNT/afsevent.h>
24 #include <WINNT/afsreg.h>
25 #include <afs/procmgmt.h>
26 #include <afs/dirpath.h>
27 #include <afs/bnode.h>
32 #define BOSSERVER_STARTMSG_EXE "afslegal.exe"
34 #define BOSSERVER_RESTART_ARG_MAX 2 /* "-noauth", "-log" */
35 #define BOSSERVER_WAIT_TIME_HINT 60 /* seconds */
36 #define BOSSERVER_STOP_TIME_MAX (FSSDTIME + 60) /* seconds */
38 #define BOS_CONTROLS_ACCEPTED SERVICE_ACCEPT_STOP
40 static CRITICAL_SECTION bosCtlStatusLock; /* protects bosCtlStatus */
41 static SERVICE_STATUS bosCtlStatus;
42 static SERVICE_STATUS_HANDLE bosCtlStatusHandle;
44 /* note: events arranged in priority order in case multiple signaled */
45 #define BOS_STOP_EVENT 0
46 #define BOS_EXIT_EVENT 1
47 #define BOS_EVENT_COUNT 2
48 static HANDLE bosCtlEvent[BOS_EVENT_COUNT];
51 /* Declare local functions */
53 static void AsyncSignalCatcher(int signo);
55 static void BosCtlStatusInit(DWORD initState);
57 static DWORD BosCtlStatusUpdate(DWORD newState,
61 static DWORD BosCtlStatusReport(void);
63 static void WINAPI BosCtlHandler(DWORD controlCode);
65 static void WINAPI BosCtlMain(DWORD argc,
68 static void BosserverDoStopEvent(pid_t cpid,
72 static void BosserverDoExitEvent(pid_t cpid,
79 static void BosserverRun(DWORD argc,
84 static void BosserverStartupMsgDisplay(void);
90 * AsyncSignalCatcher() -- Handle asynchronous signals sent to process
93 AsyncSignalCatcher(int signo)
95 if (signo == SIGCHLD) {
96 (void) SetEvent(bosCtlEvent[BOS_EXIT_EVENT]);
102 * BosCtlStatusInit() -- initialize BOS control service status structure
105 BosCtlStatusInit(DWORD initState)
107 bosCtlStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
108 bosCtlStatus.dwCurrentState = initState;
110 if (initState == SERVICE_RUNNING) {
111 bosCtlStatus.dwControlsAccepted = BOS_CONTROLS_ACCEPTED;
113 bosCtlStatus.dwControlsAccepted = 0;
116 bosCtlStatus.dwWin32ExitCode = 0;
117 bosCtlStatus.dwServiceSpecificExitCode = 0;
118 bosCtlStatus.dwCheckPoint = 0;
119 bosCtlStatus.dwWaitHint = BOSSERVER_WAIT_TIME_HINT * 1000; /* millisecs */
121 InitializeCriticalSection(&bosCtlStatusLock);
126 * BosCtlStatusUpdate() -- update BOS control service status and report to SCM
129 BosCtlStatusUpdate(DWORD newState, DWORD exitCode, BOOL isWin32Code)
133 EnterCriticalSection(&bosCtlStatusLock);
135 /* SERVICE_STOPPED is a terminal state; never transition out of it */
136 if (bosCtlStatus.dwCurrentState != SERVICE_STOPPED) {
138 if ((bosCtlStatus.dwCurrentState == newState) &&
139 (newState == SERVICE_START_PENDING ||
140 newState == SERVICE_STOP_PENDING)) {
141 /* continuing a pending state; increment checkpoint value */
142 bosCtlStatus.dwCheckPoint++;
144 /* not continuing a pending state; reset checkpoint value */
145 bosCtlStatus.dwCheckPoint = 0;
148 bosCtlStatus.dwCurrentState = newState;
150 if (newState == SERVICE_RUNNING) {
151 bosCtlStatus.dwControlsAccepted = BOS_CONTROLS_ACCEPTED;
153 bosCtlStatus.dwControlsAccepted = 0;
157 bosCtlStatus.dwWin32ExitCode = exitCode;
158 bosCtlStatus.dwServiceSpecificExitCode = 0;
160 bosCtlStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
161 bosCtlStatus.dwServiceSpecificExitCode = exitCode;
165 if (!SetServiceStatus(bosCtlStatusHandle, &bosCtlStatus)) {
169 LeaveCriticalSection(&bosCtlStatusLock);
176 * BosCtlStatusReport() -- report current BOS control service status to SCM
179 BosCtlStatusReport(void)
183 EnterCriticalSection(&bosCtlStatusLock);
185 if (!SetServiceStatus(bosCtlStatusHandle, &bosCtlStatus)) {
189 LeaveCriticalSection(&bosCtlStatusLock);
196 * BosCtlHandler() -- control handler for BOS control service
199 BosCtlHandler(DWORD controlCode)
201 switch (controlCode) {
202 case SERVICE_CONTROL_STOP:
203 (void) SetEvent(bosCtlEvent[BOS_STOP_EVENT]);
204 (void) BosCtlStatusUpdate(SERVICE_STOP_PENDING, 0, TRUE);
208 (void) BosCtlStatusReport();
215 * BosCtlMain() -- main function for BOS control service
218 BosCtlMain(DWORD argc, LPTSTR *argv)
222 struct sigaction childAction;
224 /* Initialize status structure */
225 BosCtlStatusInit(SERVICE_START_PENDING);
227 /* Create events used by service control handler and signal handler */
228 if ((bosCtlEvent[BOS_STOP_EVENT] = CreateEvent(NULL,
229 FALSE /* manual reset */,
230 FALSE /* initial state */,
232 status = GetLastError();
235 if ((bosCtlEvent[BOS_EXIT_EVENT] = CreateEvent(NULL,
236 FALSE /* manual reset */,
237 FALSE /* initial state */,
239 status = GetLastError();
242 /* Register service control handler */
243 bosCtlStatusHandle = RegisterServiceCtrlHandler(AFSREG_SVR_SVC_NAME,
245 if (bosCtlStatusHandle == 0) {
246 /* failed to register control handler for service; can not continue */
247 (void) ReportErrorEventAlt(AFSEVT_SVR_BCS_HANDLER_REG_FAILED,
248 (int)GetLastError(), NULL);
249 /* can not report status to SCM w/o a valid bosCtlStatusHandle */
253 /* Stop immediately if required system resources could not be obtained */
254 if (bosCtlEvent[BOS_STOP_EVENT] == NULL ||
255 bosCtlEvent[BOS_EXIT_EVENT] == NULL) {
256 (void) ReportErrorEventAlt(AFSEVT_SVR_BCS_INSUFFICIENT_RESOURCES,
258 (void) BosCtlStatusUpdate(SERVICE_STOPPED, status, TRUE);
262 /* Report pending start status */
263 if (status = BosCtlStatusUpdate(SERVICE_START_PENDING, 0, TRUE)) {
264 /* can't inform SCM of pending start; give up before really start */
265 (void) ReportErrorEventAlt(AFSEVT_SVR_BCS_SCM_COMM_FAILED,
267 (void) BosCtlStatusUpdate(SERVICE_STOPPED, status, TRUE);
271 /* Initialize the dirpath package so can access local bosserver binary */
272 if (!(initAFSDirPath() & AFSDIR_SERVER_PATHS_OK)) {
273 /* sw install directory probably not in registry; can not continue */
274 (void) ReportErrorEventAlt(AFSEVT_SVR_BCS_NO_INSTALL_DIR, 0, NULL);
275 (void) BosCtlStatusUpdate(SERVICE_STOPPED, 0, TRUE);
279 /* Install SIGCHLD handler to catch bosserver restarts and failures */
280 childAction.sa_handler = AsyncSignalCatcher;
281 sigfillset(&childAction.sa_mask);
282 childAction.sa_flags = 0;
284 if (sigaction(SIGCHLD, &childAction, NULL)) {
285 /* handler install should never fail, but can't continue if it does */
287 (void) ReportErrorEventAlt(AFSEVT_SVR_BCS_INTERNAL_ERROR,
289 (void) BosCtlStatusUpdate(SERVICE_STOPPED, status, FALSE);
293 /* Run the AFS bosserver, handling stop and exit events */
294 BosserverRun(argc, argv, &status, &isWin32Code);
296 (void) BosCtlStatusUpdate(SERVICE_STOPPED, status, isWin32Code);
301 * BosserverDoStopEvent() -- Handle a stop event for the AFS bosserver.
304 BosserverDoStopEvent(pid_t cpid, DWORD *stopStatus, BOOL *isWin32Code)
306 (void) BosCtlStatusUpdate(SERVICE_STOP_PENDING, 0, TRUE);
308 if (kill(cpid, SIGQUIT) == 0) {
309 /* bosserver has been told to stop; wait for this to happen */
310 BOOL gotWaitStatus = FALSE;
311 time_t timeStart = time(NULL);
317 if (waitpid(cpid, &waitStatus, WNOHANG) == cpid) {
318 /* bosserver status available */
319 if (WIFEXITED(waitStatus) && WEXITSTATUS(waitStatus) == 0) {
320 /* bosserver exited w/o any error */
324 *stopStatus = waitStatus;
325 *isWin32Code = FALSE;
327 gotWaitStatus = TRUE;
331 /* wait for bosserver status to become available;
332 * update BOS control service status periodically.
334 status = WaitForSingleObject(bosCtlEvent[BOS_EXIT_EVENT],
335 BOSSERVER_WAIT_TIME_HINT * 1000 / 2);
336 if (status == WAIT_FAILED) {
337 /* failed to wait on event; should never happen */
338 Sleep(2000); /* sleep to avoid tight loop if event problem */
340 (void) BosCtlStatusUpdate(SERVICE_STOP_PENDING, 0, TRUE);
341 } while (difftime(time(NULL), timeStart) < BOSSERVER_STOP_TIME_MAX);
343 if (!gotWaitStatus) {
344 /* timed out waiting to get bosserver status */
346 *isWin32Code = FALSE;
348 (void) ReportWarningEventAlt(AFSEVT_SVR_BCS_BOSSERVER_STOP_TIMEOUT,
349 (int)*stopStatus, NULL);
353 /* can't tell bosserver to stop; should never happen */
355 *isWin32Code = FALSE;
357 (void) ReportErrorEventAlt(AFSEVT_SVR_BCS_BOSSERVER_STOP_FAILED,
358 (int)*stopStatus, NULL);
364 * BosserverDoExitEvent() -- Handle an exit event for the AFS bosserver.
366 * The output arguments for this function are set as follows:
367 * Case 1: bosserver did not exit (spurious SIGCHLD);
368 * *doWait is set to TRUE.
369 * Case 2: bosserver exited with restart code;
370 * *doRestart is set to TRUE, restartArgv[] is defined.
371 * Case 3: bosserver exited with non-restart code;
372 * *stopStatus and *isWin32Code are defined.
375 BosserverDoExitEvent(pid_t cpid,
387 if (waitpid(cpid, &waitStatus, WNOHANG) == cpid) {
388 /* bosserver status available */
390 if (WIFEXITED(waitStatus)) {
391 /* bosserver exited normally; check for restart code */
392 int exitCode = WEXITSTATUS(waitStatus);
394 if (BOSEXIT_DORESTART(exitCode)) {
395 /* bosserver requests restart */
399 /* set up bosserver argument list */
400 restartArgv[0] = (char *)AFSDIR_SERVER_BOSVR_FILEPATH;
403 if (exitCode & BOSEXIT_NOAUTH_FLAG) {
404 /* pass "-noauth" to new bosserver */
405 restartArgv[i] = "-noauth";
408 if (exitCode & BOSEXIT_LOGGING_FLAG) {
409 /* pass "-log" to new bosserver */
410 restartArgv[i] = "-log";
413 restartArgv[i] = NULL;
418 /* bosserver exited with non-restart code; set status */
419 *stopStatus = waitStatus;
420 *isWin32Code = FALSE;
422 (void) ReportWarningEventAlt(AFSEVT_SVR_BCS_BOSSERVER_EXIT,
423 (int)*stopStatus, NULL);
427 /* bosserver status NOT available; assume spurious SIGCHLD */
434 * BosserverRun() -- Run the AFS bosserver, handling stop and exit events.
436 * Input args are those passed to the service's main function (BosCtlMain).
437 * Output args are the stop status and status type of the bosserver.
440 BosserverRun(DWORD argc,
446 BOOL doRestart, doWait;
449 /* Display bosserver startup (legal) message; first start only */
450 BosserverStartupMsgDisplay();
452 /* Set env variable forcing process mgmt lib to spawn processes detached */
453 (void)putenv(PMGT_SPAWN_DETACHED_ENV_NAME "=1");
455 /* Alloc block with room for at least BOSSERVER_RESTART_ARG_MAX args */
456 i = max((argc + 1), (BOSSERVER_RESTART_ARG_MAX + 2));
457 spawn_argv = (char **)malloc(i * sizeof(char *));
459 if (spawn_argv == NULL) {
460 /* failed to malloc required space; can not continue */
461 *stopStatus = ENOMEM;
462 *isWin32Code = FALSE;
464 (void) ReportErrorEventAlt(AFSEVT_SVR_BCS_INSUFFICIENT_RESOURCES,
465 (int)*stopStatus, NULL);
469 /* Set initial bosserver args to those supplied via StartService() */
470 spawn_argv[0] = (char *)AFSDIR_SERVER_BOSVR_FILEPATH;
472 for (i = 1; i < argc; i++) {
473 spawn_argv[i] = argv[i];
475 spawn_argv[i] = NULL;
477 /* Start/restart bosserver and wait for either a stop or exit event */
484 /* restarting bosserver; log informational message */
485 (void) ReportInformationEventAlt(AFSEVT_SVR_BCS_BOSSERVER_RESTART,
490 cpid = spawnprocve(spawn_argv[0], spawn_argv, NULL, 0);
492 if (cpid == (pid_t)-1) {
493 /* failed to start/restart the bosserver process */
495 *isWin32Code = FALSE;
497 (void) ReportErrorEventAlt(AFSEVT_SVR_BCS_BOSSERVER_START_FAILED,
498 (int)*stopStatus, NULL);
502 if (status = BosCtlStatusUpdate(SERVICE_RUNNING, 0, TRUE)) {
503 /* can't tell SCM we're running so quit; should never occur */
504 (void) ReportErrorEventAlt(AFSEVT_SVR_BCS_SCM_COMM_FAILED,
506 (void) SetEvent(bosCtlEvent[BOS_STOP_EVENT]);
509 /* bosserver is running; wait for an event of interest */
511 Sleep(5000); /* bosserver needs time to register signal handler */
516 status = WaitForMultipleObjects(BOS_EVENT_COUNT,
517 bosCtlEvent, FALSE, INFINITE);
519 if ((status - WAIT_OBJECT_0) == BOS_STOP_EVENT) {
520 /* stop event signaled */
521 BosserverDoStopEvent(cpid, stopStatus, isWin32Code);
523 } else if ((status - WAIT_OBJECT_0) == BOS_EXIT_EVENT) {
524 /* exit event signaled; see function comment for outcomes */
525 BosserverDoExitEvent(cpid,
527 &doRestart, spawn_argv,
528 stopStatus, isWin32Code);
531 /* failed to wait on events; should never happen */
532 Sleep(2000); /* sleep to avoid tight loop if event problem */
543 * BosserverStartupMsgDisplay() -- display Windows version of AFS bosserver
544 * startup (legal) message.
547 BosserverStartupMsgDisplay(void)
551 if (!ConstructLocalBinPath(BOSSERVER_STARTMSG_EXE, &msgPath)) {
552 /* Use C runtime spawn; don't need all the machinery in the
553 * process management library.
555 (void)_spawnl(_P_DETACH, msgPath, BOSSERVER_STARTMSG_EXE, NULL);
562 * main() -- start dispatcher thread for BOS control service
566 SERVICE_TABLE_ENTRY dispatchTable[] = {{AFSREG_SVR_SVC_NAME, BosCtlMain},
569 (void) ReportInformationEventAlt(AFSEVT_SVR_BCS_STARTED, NULL);
571 if (!StartServiceCtrlDispatcher(dispatchTable)) {
572 /* unable to connect to SCM */
573 (void) ReportErrorEventAlt(AFSEVT_SVR_BCS_SCM_COMM_FAILED,
574 (int)GetLastError(), NULL);
577 (void) ReportInformationEventAlt(AFSEVT_SVR_BCS_STOPPED, NULL);