diff options
Diffstat (limited to 'framework/src/suricata/src/win32-service.c')
-rw-r--r-- | framework/src/suricata/src/win32-service.c | 394 |
1 files changed, 394 insertions, 0 deletions
diff --git a/framework/src/suricata/src/win32-service.c b/framework/src/suricata/src/win32-service.c new file mode 100644 index 00000000..257447e5 --- /dev/null +++ b/framework/src/suricata/src/win32-service.c @@ -0,0 +1,394 @@ +/* Copyright (C) 2007-2010 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + * + * \author Ondrej Slanina <oslanina@kerio.com> + * + * Windows service functions + */ + +#ifdef OS_WIN32 + +#include "suricata-common.h" +#include "suricata.h" +#include "win32-service.h" + +static SERVICE_STATUS_HANDLE service_status_handle = 0; + +static int service_argc = 0; + +static char **service_argv = NULL; + +static int service_initialized = 0; + +int main(int argc, char **argv); + +/** + * \brief Detect if running as service or console app + */ +int SCRunningAsService(void) +{ + HANDLE h = INVALID_HANDLE_VALUE; + if ((h = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) { + SCLogInfo("Running as service: yes"); + return 1; + } + CloseHandle(h); + SCLogInfo("Running as service: no"); + return 0; +} + +/** + * \brief Detect if running as service or console app + */ +void SCAtExitHandler(void) +{ + SERVICE_STATUS status = { + SERVICE_WIN32, + SERVICE_STOPPED, + SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN, + NO_ERROR, + NO_ERROR, + 0, + 0 + }; + + SCLogInfo("Exit handler called."); + + /* mark service as stopped */ + if (!SetServiceStatus(service_status_handle, &status)) { + SCLogWarning(SC_ERR_SVC, "Can't set service status: %d", (int)GetLastError()); + } else { + SCLogInfo("Service status set to: SERVICE_STOPPED"); + } +} + +/** + * \brief Service handler + */ +static DWORD WINAPI SCServiceCtrlHandlerEx(DWORD code, DWORD etype, LPVOID edata, LPVOID context) +{ + if (code == SERVICE_CONTROL_SHUTDOWN || code == SERVICE_CONTROL_STOP) { + SERVICE_STATUS status = { + SERVICE_WIN32, + SERVICE_STOP_PENDING, + SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN, + NO_ERROR, + NO_ERROR, + 0, + 0 + }; + + SCLogInfo("Service control handler called with %s control code.", + ((code == SERVICE_CONTROL_SHUTDOWN) ? ("SERVICE_CONTROL_SHUTDOWN") : ("SERVICE_CONTROL_STOP"))); + + /* mark service as stop pending */ + if (!SetServiceStatus(service_status_handle, &status)) { + SCLogWarning(SC_ERR_SVC, "Can't set service status: %d", (int)GetLastError()); + } else { + SCLogInfo("Service status set to: SERVICE_STOP_PENDING"); + } + + /* mark engine as stopping */ + EngineStop(); + + return NO_ERROR; + } + + return ERROR_CALL_NOT_IMPLEMENTED; +} + +/** + * \brief Service main function + */ +static void WINAPI SCServiceMain(uint32_t argc, char** argv) +{ + SERVICE_STATUS status = { + SERVICE_WIN32, + SERVICE_RUNNING, + SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN, + NO_ERROR, + NO_ERROR, + 0, + 0 + }; + + if ((service_status_handle = RegisterServiceCtrlHandlerEx(PROG_NAME, SCServiceCtrlHandlerEx, NULL)) == (SERVICE_STATUS_HANDLE)0) { + SCLogError(SC_ERR_SVC, "Can't register service control handler: %d", (int)GetLastError()); + return; + } + + /* register exit handler */ + if (atexit(SCAtExitHandler)) { + SCLogWarning(SC_ERR_SVC, "Can't register exit handler: %d", (int)GetLastError()); + } + + /* mark service as running immediately */ + if (!SetServiceStatus(service_status_handle, &status)) { + SCLogWarning(SC_ERR_SVC, "Can't set service status: %d", (int)GetLastError()); + } else { + SCLogInfo("Service status set to: SERVICE_RUNNING"); + } + + SCLogInfo("Entering main function..."); + + /* suricata initialization -> main loop -> uninitialization */ + main(service_argc, service_argv); + + SCLogInfo("Leaving main function."); + + /* mark service as stopped */ + status.dwCurrentState = SERVICE_STOPPED; + + if (!SetServiceStatus(service_status_handle, &status)) { + SCLogWarning(SC_ERR_SVC, "Can't set service status: %d", (int)GetLastError()); + } else { + SCLogInfo("Service status set to: SERVICE_STOPPED"); + } +} + +/** + * \brief Init suricata service + * + * \param argc num of arguments + * \param argv passed arguments + */ +int SCServiceInit(int argc, char **argv) +{ + SERVICE_TABLE_ENTRY DispatchTable[] = { + {PROG_NAME, (LPSERVICE_MAIN_FUNCTION) SCServiceMain}, + {NULL, NULL} + }; + + /* continue with suricata initialization */ + if (service_initialized) { + SCLogWarning(SC_ERR_SVC, "Service is already initialized."); + return 0; + } + + /* save args */ + service_argc = argc; + service_argv = argv; + + service_initialized = 1; + + SCLogInfo("Entering service control dispatcher..."); + + if (!StartServiceCtrlDispatcher(DispatchTable)) { + /* exit with failure */ + exit(EXIT_FAILURE); + } + + SCLogInfo("Leaving service control dispatcher."); + + /* exit with success */ + exit(EXIT_SUCCESS); +} + +/** + * \brief Install suricata as service + * + * \param argc num of arguments + * \param argv passed arguments + */ +int SCServiceInstall(int argc, char **argv) +{ + char path[2048]; + SC_HANDLE service = NULL; + SC_HANDLE scm = NULL; + int ret = -1; + int i = 0; + + do { + memset(path, 0, sizeof(path)); + + if (GetModuleFileName(NULL, path, MAX_PATH) == 0 ){ + SCLogError(SC_ERR_SVC, "Can't get path to service binary: %d", (int)GetLastError()); + break; + } + + /* skip name of binary itself */ + for (i = 1; i < argc; i++) { + if ((strlen(argv[i]) <= strlen("--service-install")) && (strncmp("--service-install", argv[i], strlen(argv[i])) == 0)) { + continue; + } + strlcat(path, " ", sizeof(path) - strlen(path) - 1); + strlcat(path, argv[i], sizeof(path) - strlen(path) - 1); + } + + if ((scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)) == NULL) { + SCLogError(SC_ERR_SVC, "Can't open SCM: %d", (int)GetLastError()); + break; + } + + service = CreateService( + scm, + PROG_NAME, + PROG_NAME, + SERVICE_ALL_ACCESS, + SERVICE_WIN32_OWN_PROCESS, + SERVICE_DEMAND_START, + SERVICE_ERROR_NORMAL, + path, + NULL, + NULL, + NULL, + NULL, + NULL); + + if (service == NULL) { + SCLogError(SC_ERR_SVC, "Can't create service: %d", (int)GetLastError()); + break; + } + + ret = 0; + + } while(0); + + if (service) { + CloseServiceHandle(service); + } + + if (scm) { + CloseServiceHandle(scm); + } + + return ret; +} + +/** + * \brief Remove suricata service + * + * \param argc num of arguments + * \param argv passed arguments + */ +int SCServiceRemove(int argc, char **argv) +{ + SERVICE_STATUS status; + SC_HANDLE service = NULL; + SC_HANDLE scm = NULL; + int ret = -1; + + do { + if ((scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)) == NULL) { + SCLogError(SC_ERR_SVC, "Can't open SCM: %d", (int)GetLastError()); + break; + } + + if ((service = OpenService(scm, PROG_NAME, SERVICE_ALL_ACCESS)) == NULL) { + SCLogError(SC_ERR_SVC, "Can't open service: %d", (int)GetLastError()); + break; + } + + if (!QueryServiceStatus(service, &status)) { + SCLogError(SC_ERR_SVC, "Can't query service status: %d", (int)GetLastError()); + break; + } + + if (status.dwCurrentState != SERVICE_STOPPED) { + SCLogError(SC_ERR_SVC, "Service isn't in stopped state: %d", (int)GetLastError()); + break; + } + + if (!DeleteService(service)) { + SCLogError(SC_ERR_SVC, "Can't delete service: %d", (int)GetLastError()); + break; + } + + ret = 0; + + } while(0); + + if (service) { + CloseServiceHandle(service); + } + + if (scm) { + CloseServiceHandle(scm); + } + + return ret; +} + +/** + * \brief Change suricata service startup parameters + * + * \param argc num of arguments + * \param argv passed arguments + */ +int SCServiceChangeParams(int argc, char **argv) +{ + char path[2048]; + SC_HANDLE service = NULL; + SC_HANDLE scm = NULL; + int ret = -1; + int i = 0; + + do { + memset(path, 0, sizeof(path)); + + if (GetModuleFileName(NULL, path, MAX_PATH) == 0 ){ + SCLogError(SC_ERR_SVC, "Can't get path to service binary: %d", (int)GetLastError()); + break; + } + + /* skip name of binary itself */ + for (i = 1; i < argc; i++) { + if ((strlen(argv[i]) <= strlen("--service-change-params")) && (strncmp("--service-change-params", argv[i], strlen(argv[i])) == 0)) { + continue; + } + strlcat(path, " ", sizeof(path) - strlen(path) - 1); + strlcat(path, argv[i], sizeof(path) - strlen(path) - 1); + } + + if ((scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)) == NULL) { + SCLogError(SC_ERR_SVC, "Can't open SCM: %d", (int)GetLastError()); + break; + } + + if ((service = OpenService(scm, PROG_NAME, SERVICE_ALL_ACCESS)) == NULL) { + SCLogError(SC_ERR_SVC, "Can't open service: %d", (int)GetLastError()); + break; + } + + if (!ChangeServiceConfig( + service, + SERVICE_WIN32_OWN_PROCESS, + SERVICE_DEMAND_START, + SERVICE_ERROR_NORMAL, + path, + NULL, + NULL, + NULL, + NULL, + NULL, + PROG_NAME)) + { + SCLogError(SC_ERR_SVC, "Can't change service configuration: %d", (int)GetLastError()); + break; + } + + ret = 0; + + } while(0); + + return ret; +} + +#endif /* OS_WIN32 */ |