summaryrefslogtreecommitdiff
path: root/bin/bbackupd/Win32ServiceFunctions.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'bin/bbackupd/Win32ServiceFunctions.cpp')
-rw-r--r--bin/bbackupd/Win32ServiceFunctions.cpp384
1 files changed, 384 insertions, 0 deletions
diff --git a/bin/bbackupd/Win32ServiceFunctions.cpp b/bin/bbackupd/Win32ServiceFunctions.cpp
new file mode 100644
index 00000000..2df914a7
--- /dev/null
+++ b/bin/bbackupd/Win32ServiceFunctions.cpp
@@ -0,0 +1,384 @@
+//***************************************************************
+// 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 <unistd.h>
+#endif
+#ifdef HAVE_PROCESS_H
+ #include <process.h>
+#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)
+ {
+ EMU_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