//*************************************************************** // From the book "Win32 System Services: The Heart of Windows 98 // and Windows 2000" // by Marshall Brain // Published by Prentice Hall // Copyright 1995 Prentice Hall. // // This code implements the Windows API Service interface // for the Box Backup for Windows native port. // Adapted for Box Backup by Nick Knight. //*************************************************************** #ifdef WIN32 #include "Box.h" #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_PROCESS_H #include #endif extern void TerminateService(void); extern unsigned int WINAPI RunService(LPVOID lpParameter); // Global variables TCHAR* gServiceName = TEXT("Box Backup Service"); SERVICE_STATUS gServiceStatus; SERVICE_STATUS_HANDLE gServiceStatusHandle = 0; HANDLE gStopServiceEvent = 0; DWORD gServiceReturnCode = 0; #define SERVICE_NAME "boxbackup" void ShowMessage(char *s) { MessageBox(0, s, "Box Backup Message", MB_OK | MB_SETFOREGROUND | MB_DEFAULT_DESKTOP_ONLY); } void ErrorHandler(char *s, DWORD err) { char buf[256]; memset(buf, 0, sizeof(buf)); _snprintf(buf, sizeof(buf)-1, "%s: %s", s, GetErrorMessage(err).c_str()); BOX_ERROR(buf); MessageBox(0, buf, "Error", MB_OK | MB_SETFOREGROUND | MB_DEFAULT_DESKTOP_ONLY); ExitProcess(err); } void WINAPI ServiceControlHandler( DWORD controlCode ) { switch ( controlCode ) { case SERVICE_CONTROL_INTERROGATE: break; case SERVICE_CONTROL_SHUTDOWN: case SERVICE_CONTROL_STOP: Beep(1000,100); TerminateService(); gServiceStatus.dwCurrentState = SERVICE_STOP_PENDING; SetServiceStatus(gServiceStatusHandle, &gServiceStatus); SetEvent(gStopServiceEvent); return; case SERVICE_CONTROL_PAUSE: break; case SERVICE_CONTROL_CONTINUE: break; default: if ( controlCode >= 128 && controlCode <= 255 ) // user defined control code break; else // unrecognised control code break; } SetServiceStatus( gServiceStatusHandle, &gServiceStatus ); } // ServiceMain is called when the SCM wants to // start the service. When it returns, the service // has stopped. It therefore waits on an event // just before the end of the function, and // that event gets set when it is time to stop. // It also returns on any error because the // service cannot start if there is an eror. static char* spConfigFileName; VOID ServiceMain(DWORD argc, LPTSTR *argv) { // initialise service status gServiceStatus.dwServiceType = SERVICE_WIN32; gServiceStatus.dwCurrentState = SERVICE_STOPPED; gServiceStatus.dwControlsAccepted = 0; gServiceStatus.dwWin32ExitCode = NO_ERROR; gServiceStatus.dwServiceSpecificExitCode = NO_ERROR; gServiceStatus.dwCheckPoint = 0; gServiceStatus.dwWaitHint = 0; gServiceStatusHandle = RegisterServiceCtrlHandler(gServiceName, ServiceControlHandler); if (gServiceStatusHandle) { // service is starting gServiceStatus.dwCurrentState = SERVICE_START_PENDING; SetServiceStatus(gServiceStatusHandle, &gServiceStatus); // do initialisation here gStopServiceEvent = CreateEvent(0, TRUE, FALSE, 0); if (!gStopServiceEvent) { gServiceStatus.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN); gServiceStatus.dwCurrentState = SERVICE_STOPPED; SetServiceStatus(gServiceStatusHandle, &gServiceStatus); return; } HANDLE ourThread = (HANDLE)_beginthreadex( NULL, 0, RunService, spConfigFileName, CREATE_SUSPENDED, NULL); SetThreadPriority(ourThread, THREAD_PRIORITY_LOWEST); ResumeThread(ourThread); // we are now running so tell the SCM gServiceStatus.dwControlsAccepted |= (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN); gServiceStatus.dwCurrentState = SERVICE_RUNNING; SetServiceStatus(gServiceStatusHandle, &gServiceStatus); // do cleanup here WaitForSingleObject(gStopServiceEvent, INFINITE); CloseHandle(gStopServiceEvent); gStopServiceEvent = 0; // service was stopped gServiceStatus.dwCurrentState = SERVICE_STOP_PENDING; SetServiceStatus(gServiceStatusHandle, &gServiceStatus); // service is now stopped gServiceStatus.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN); gServiceStatus.dwCurrentState = SERVICE_STOPPED; if (gServiceReturnCode != 0) { gServiceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; gServiceStatus.dwServiceSpecificExitCode = gServiceReturnCode; } SetServiceStatus(gServiceStatusHandle, &gServiceStatus); } } int OurService(const char* pConfigFileName) { spConfigFileName = strdup(pConfigFileName); SERVICE_TABLE_ENTRY serviceTable[] = { { SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION) ServiceMain }, { NULL, NULL } }; BOOL success; // Register with the SCM success = StartServiceCtrlDispatcher(serviceTable); free(spConfigFileName); spConfigFileName = NULL; if (!success) { ErrorHandler("Failed to start service. Did you start " "Box Backup from the Service Control Manager? " "(StartServiceCtrlDispatcher)", GetLastError()); return 1; } return 0; } int InstallService(const char* pConfigFileName, const std::string& rServiceName) { if (pConfigFileName != NULL) { struct stat st; if (emu_stat(pConfigFileName, &st) != 0) { BOX_LOG_SYS_ERROR("Failed to open configuration file " "'" << pConfigFileName << "'"); return 1; } if (!(st.st_mode & S_IFREG)) { BOX_ERROR("Failed to open configuration file '" << pConfigFileName << "': not a file"); return 1; } } SC_HANDLE scm = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE); if (!scm) { BOX_ERROR("Failed to open service control manager: " << GetErrorMessage(GetLastError())); return 1; } char cmd[MAX_PATH]; GetModuleFileName(NULL, cmd, sizeof(cmd)-1); cmd[sizeof(cmd)-1] = 0; std::string cmdWithArgs(cmd); cmdWithArgs += " -s -S \"" + rServiceName + "\""; if (pConfigFileName != NULL) { cmdWithArgs += " \""; cmdWithArgs += pConfigFileName; cmdWithArgs += "\""; } std::string serviceDesc = "Box Backup (" + rServiceName + ")"; SC_HANDLE newService = CreateService( scm, rServiceName.c_str(), serviceDesc.c_str(), SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, cmdWithArgs.c_str(), 0,0,0,0,0); DWORD err = GetLastError(); CloseServiceHandle(scm); if (!newService) { switch (err) { case ERROR_SERVICE_EXISTS: { BOX_ERROR("Failed to create Box Backup " "service: it already exists"); } break; case ERROR_SERVICE_MARKED_FOR_DELETE: { BOX_ERROR("Failed to create Box Backup " "service: it is waiting to be deleted"); } break; case ERROR_DUPLICATE_SERVICE_NAME: { BOX_ERROR("Failed to create Box Backup " "service: a service with this name " "already exists"); } break; default: { BOX_ERROR("Failed to create Box Backup " "service: error " << GetErrorMessage(GetLastError())); } } return 1; } BOX_INFO("Created Box Backup service"); SERVICE_DESCRIPTION desc; desc.lpDescription = "Backs up your data files over the Internet"; if (!ChangeServiceConfig2(newService, SERVICE_CONFIG_DESCRIPTION, &desc)) { BOX_WARNING("Failed to set description for Box Backup " "service: " << GetErrorMessage(GetLastError())); } CloseServiceHandle(newService); return 0; } int RemoveService(const std::string& rServiceName) { SC_HANDLE scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE); if (!scm) { BOX_ERROR("Failed to open service control manager: " << GetErrorMessage(GetLastError())); return 1; } SC_HANDLE service = OpenService(scm, rServiceName.c_str(), SERVICE_ALL_ACCESS|DELETE); DWORD err = GetLastError(); CloseServiceHandle(scm); if (!service) { if (err == ERROR_SERVICE_DOES_NOT_EXIST || err == ERROR_IO_PENDING) // hello microsoft? anyone home? { BOX_ERROR("Failed to open Box Backup service: " "not installed or not found"); } else { BOX_ERROR("Failed to open Box Backup service: " << GetErrorMessage(err)); } return 1; } SERVICE_STATUS status; if (!ControlService(service, SERVICE_CONTROL_STOP, &status)) { err = GetLastError(); if (err != ERROR_SERVICE_NOT_ACTIVE) { BOX_WARNING("Failed to stop Box Backup service: " << GetErrorMessage(err)); } } BOOL deleted = DeleteService(service); err = GetLastError(); CloseServiceHandle(service); if (deleted) { BOX_INFO("Box Backup service deleted"); return 0; } else if (err == ERROR_SERVICE_MARKED_FOR_DELETE) { BOX_ERROR("Failed to remove Box Backup service: " "it is already being deleted"); } else { BOX_ERROR("Failed to remove Box Backup service: " << GetErrorMessage(err)); } return 1; } #endif // WIN32