/* 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 * * 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 */