/* * BRLTTY - A background process providing access to the console screen (when in * text mode) for a blind person using a refreshable braille display. * * Copyright (C) 1995-2019 by The BRLTTY Developers. * * BRLTTY comes with ABSOLUTELY NO WARRANTY. * * This is free software, placed under the terms of the * GNU Lesser General Public License, as published by the Free Software * Foundation; either version 2.1 of the License, or (at your option) any * later version. Please see the file LICENSE-LGPL for details. * * Web Page: http://brltty.app/ * * This software is maintained by Dave Mielke . */ #include "prologue.h" #include #include #include #include #include #include "embed.h" #include "log.h" #include "api_control.h" #include "core.h" static ProgramExitStatus brlttyRun (void) { while (brlttyWait(INT_MAX)); return PROG_EXIT_SUCCESS; } #ifdef __MINGW32__ static SERVICE_STATUS_HANDLE serviceStatusHandle; static DWORD serviceState; static ProgramExitStatus serviceExitStatus; static BOOL setServiceState (DWORD state, DWORD exitStatus, const char *name) { SERVICE_STATUS status = { .dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS, .dwCurrentState = state, .dwWin32ExitCode = (exitStatus != PROG_EXIT_SUCCESS)? ERROR_SERVICE_SPECIFIC_ERROR: NO_ERROR, .dwServiceSpecificExitCode = exitStatus }; switch (status.dwCurrentState) { default: status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE; case SERVICE_START_PENDING: case SERVICE_STOP_PENDING: case SERVICE_STOPPED: break; } switch (status.dwCurrentState) { case SERVICE_START_PENDING: case SERVICE_PAUSE_PENDING: case SERVICE_CONTINUE_PENDING: case SERVICE_STOP_PENDING: status.dwWaitHint = 10000; status.dwCheckPoint = 0; default: break; } serviceState = state; if (SetServiceStatus(serviceStatusHandle, &status)) return 1; logWindowsSystemError(name); return 0; } #define SET_SERVICE_STATE(state,code) setServiceState(state, code, #state) static void WINAPI serviceControlHandler (DWORD code) { switch (code) { case SERVICE_CONTROL_STOP: SET_SERVICE_STATE(SERVICE_STOP_PENDING, PROG_EXIT_SUCCESS); raise(SIGTERM); break; case SERVICE_CONTROL_PAUSE: SET_SERVICE_STATE(SERVICE_PAUSE_PENDING, PROG_EXIT_SUCCESS); api.suspend(); SET_SERVICE_STATE(SERVICE_PAUSED, PROG_EXIT_SUCCESS); break; case SERVICE_CONTROL_CONTINUE: SET_SERVICE_STATE(SERVICE_CONTINUE_PENDING, PROG_EXIT_SUCCESS); if (api.resume()) { SET_SERVICE_STATE(SERVICE_RUNNING, PROG_EXIT_SUCCESS); } else { SET_SERVICE_STATE(SERVICE_PAUSED, PROG_EXIT_SUCCESS); } break; default: logMessage(LOG_WARNING, "unexpected service control code: %lu", code); break; } } static void exitService (void) { SET_SERVICE_STATE(SERVICE_STOPPED, PROG_EXIT_SUCCESS); } static void WINAPI serviceMain (DWORD argc, LPSTR *argv) { atexit(exitService); if ((serviceStatusHandle = RegisterServiceCtrlHandler("", serviceControlHandler))) { if ((SET_SERVICE_STATE(SERVICE_START_PENDING, PROG_EXIT_SUCCESS))) { if ((serviceExitStatus = brlttyConstruct(argc, argv)) == PROG_EXIT_SUCCESS) { if ((SET_SERVICE_STATE(SERVICE_RUNNING, PROG_EXIT_SUCCESS))) { serviceExitStatus = brlttyRun(); } else { serviceExitStatus = PROG_EXIT_FATAL; } brlttyDestruct(); } else if (serviceExitStatus == PROG_EXIT_FORCE) { serviceExitStatus = PROG_EXIT_SUCCESS; } SET_SERVICE_STATE(SERVICE_STOPPED, serviceExitStatus); } } else { logWindowsSystemError("RegisterServiceCtrlHandler"); } } #endif /* __MINGW32__ */ int main (int argc, char *argv[]) { #ifdef __MINGW32__ { static SERVICE_TABLE_ENTRY serviceTable[] = { { .lpServiceName="", .lpServiceProc=serviceMain }, {} }; isWindowsService = 1; if (StartServiceCtrlDispatcher(serviceTable)) return serviceExitStatus; isWindowsService = 0; if (GetLastError() != ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) { logWindowsSystemError("StartServiceCtrlDispatcher"); return PROG_EXIT_FATAL; } } #endif /* __MINGW32__ */ #ifdef INIT_PATH #define INIT_NAME "init" if ((getpid() == 1) || strstr(argv[0], "linuxrc")) { fprintf(stderr, gettext("\"%s\" started as \"%s\"\n"), PACKAGE_NAME, argv[0]); fflush(stderr); switch (fork()) { case -1: /* failed */ fprintf(stderr, gettext("fork of \"%s\" failed: %s\n"), PACKAGE_NAME, strerror(errno)); fflush(stderr); default: /* parent */ fprintf(stderr, gettext("executing \"%s\" (from \"%s\")\n"), INIT_NAME, INIT_PATH); fflush(stderr); executeInit: execv(INIT_PATH, argv); /* execv() shouldn't return */ fprintf(stderr, gettext("execution of \"%s\" failed: %s\n"), INIT_NAME, strerror(errno)); fflush(stderr); exit(1); case 0: { /* child */ static char *arguments[] = {"brltty", "-E", "-n", "-e", "-linfo", NULL}; argv = arguments; argc = ARRAY_COUNT(arguments) - 1; break; } } } else if (!strstr(argv[0], "brltty")) { /* * If we are substituting the real init binary, then we may consider * when someone might want to call that binary even when pid != 1. * One example is /sbin/telinit which is a symlink to /sbin/init. */ goto executeInit; } #endif /* INIT_PATH */ #ifdef STDERR_PATH freopen(STDERR_PATH, "a", stderr); #endif /* STDERR_PATH */ { ProgramExitStatus exitStatus = brlttyConstruct(argc, argv); if (exitStatus == PROG_EXIT_SUCCESS) { exitStatus = brlttyRun(); brlttyDestruct(); } else if (exitStatus == PROG_EXIT_FORCE) { exitStatus = PROG_EXIT_SUCCESS; } return exitStatus; } }