228 lines
7.3 KiB
C
228 lines
7.3 KiB
C
/*
|
|
* Copyright (C) 2021-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
|
* Copyright (C) 2008-2010 Gianluigi Tiesi <sherpya@netfarm.it>
|
|
*
|
|
* Authors: Gianluigi Tiesi
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it 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
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
* MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include <platform.h>
|
|
#include <winsvc.h>
|
|
|
|
#include "service.h"
|
|
#include "output.h"
|
|
|
|
static SERVICE_STATUS svc;
|
|
static SERVICE_STATUS_HANDLE svc_handle;
|
|
static SERVICE_TABLE_ENTRYA DT[] = {{"Service", ServiceMain}, {NULL, NULL}};
|
|
|
|
static HANDLE evStart;
|
|
static HANDLE DispatcherThread;
|
|
static int checkpoint_every = 5000;
|
|
|
|
int svc_uninstall(const char *name, int verbose)
|
|
{
|
|
SC_HANDLE sm, svc;
|
|
int ret = 1;
|
|
|
|
if (!(sm = OpenSCManagerA(NULL, NULL, DELETE))) {
|
|
if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
|
|
fprintf(stderr, "Windows Services are not supported on this Platform\n");
|
|
else
|
|
fprintf(stderr, "Unable to Open SCManager (%d)\n", GetLastError());
|
|
return 0;
|
|
}
|
|
|
|
if ((svc = OpenServiceA(sm, name, DELETE))) {
|
|
if (DeleteService(svc)) {
|
|
if (verbose) printf("Service %s successfully removed\n", name);
|
|
} else {
|
|
fprintf(stderr, "Unable to Open Service %s (%d)\n", name, GetLastError());
|
|
ret = 0;
|
|
}
|
|
} else {
|
|
if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
|
|
if (verbose) printf("Service %s does not exist\n", name);
|
|
} else {
|
|
fprintf(stderr, "Unable to Open Service %s (%d)\n", name, GetLastError());
|
|
ret = 0;
|
|
}
|
|
}
|
|
|
|
if (svc) CloseServiceHandle(svc);
|
|
CloseServiceHandle(sm);
|
|
return ret;
|
|
}
|
|
|
|
int svc_install(const char *name, const char *dname, const char *desc)
|
|
{
|
|
SC_HANDLE sm, svc;
|
|
char modulepath[MAX_PATH];
|
|
char binpath[MAX_PATH];
|
|
SERVICE_DESCRIPTIONA sdesc = {(char *)desc};
|
|
|
|
if (!GetModuleFileName(NULL, modulepath, MAX_PATH - 1)) {
|
|
fprintf(stderr, "Unable to get the executable name (%d)\n", GetLastError());
|
|
return 0;
|
|
}
|
|
|
|
if (!svc_uninstall(name, 0)) return 0;
|
|
|
|
if (!(sm = OpenSCManagerA(NULL, NULL, SC_MANAGER_CREATE_SERVICE | DELETE))) {
|
|
if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
|
|
fprintf(stderr, "Windows Services are not supported on this Platform\n");
|
|
else
|
|
fprintf(stderr, "Unable to Open SCManager (%d)\n", GetLastError());
|
|
return 0;
|
|
}
|
|
|
|
if (strchr(modulepath, ' '))
|
|
snprintf(binpath, MAX_PATH - 1, "\"%s\" --daemon --service-mode", modulepath);
|
|
else
|
|
snprintf(binpath, MAX_PATH - 1, "%s --daemon --service-mode", modulepath);
|
|
|
|
svc = CreateServiceA(sm, name, dname, SERVICE_CHANGE_CONFIG,
|
|
SERVICE_WIN32_OWN_PROCESS,
|
|
SERVICE_DEMAND_START,
|
|
SERVICE_ERROR_NORMAL,
|
|
binpath,
|
|
NULL, /* Load group order */
|
|
NULL, /* Tag Id */
|
|
NULL, /* Dependencies */
|
|
NULL, /* User -> Local System */
|
|
"");
|
|
|
|
if (!svc) {
|
|
fprintf(stderr, "Unable to Create Service %s (%d)\n", name, GetLastError());
|
|
CloseServiceHandle(sm);
|
|
return 0;
|
|
}
|
|
|
|
/* ChangeServiceConfig2A() */
|
|
if (!ChangeServiceConfig2A(svc, SERVICE_CONFIG_DESCRIPTION, &sdesc))
|
|
fprintf(stderr, "Unable to set description for Service %s (%d)\n", name, GetLastError());
|
|
|
|
CloseServiceHandle(svc);
|
|
CloseServiceHandle(sm);
|
|
|
|
printf("Service %s successfully created.\n", name);
|
|
printf("Use 'net start %s' and 'net stop %s' to start/stop the service.\n", name, name);
|
|
return 1;
|
|
}
|
|
|
|
static void svc_getcpvalue(const char *name)
|
|
{
|
|
HKEY hKey;
|
|
DWORD dwType;
|
|
DWORD value, vlen = sizeof(DWORD);
|
|
char subkey[MAX_PATH];
|
|
|
|
snprintf(subkey, MAX_PATH - 1, "SYSTEM\\CurrentControlSet\\Services\\%s", name);
|
|
|
|
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, subkey, 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS)
|
|
return;
|
|
|
|
if ((RegQueryValueExA(hKey, "Checkpoint", NULL, &dwType, (LPBYTE)&value, &vlen) == ERROR_SUCCESS) &&
|
|
(vlen == sizeof(DWORD) && (dwType == REG_DWORD)))
|
|
checkpoint_every = value;
|
|
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
void svc_register(const char *name)
|
|
{
|
|
DWORD tid;
|
|
DT->lpServiceName = (char *)name;
|
|
svc_getcpvalue(name);
|
|
|
|
evStart = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
DispatcherThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)StartServiceCtrlDispatcherA, (LPVOID)DT, 0, &tid);
|
|
}
|
|
|
|
void svc_ready(void)
|
|
{
|
|
WaitForSingleObject(evStart, INFINITE);
|
|
|
|
svc.dwCurrentState = SERVICE_RUNNING;
|
|
svc.dwControlsAccepted |= SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
|
|
svc.dwCheckPoint = 0;
|
|
|
|
if (!SetServiceStatus(svc_handle, &svc)) {
|
|
logg(LOGG_INFO, "[service] SetServiceStatus() failed with %d\n", GetLastError());
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
int svc_checkpoint(const char *type, const char *name, unsigned int custom, void *context)
|
|
{
|
|
if (svc.dwCurrentState == SERVICE_START_PENDING) {
|
|
svc.dwCheckPoint++;
|
|
if ((svc.dwCheckPoint % checkpoint_every) == 0)
|
|
SetServiceStatus(svc_handle, &svc);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void WINAPI ServiceCtrlHandler(DWORD code)
|
|
{
|
|
switch (code) {
|
|
case SERVICE_CONTROL_STOP:
|
|
case SERVICE_CONTROL_SHUTDOWN:
|
|
svc.dwCurrentState = SERVICE_STOPPED;
|
|
svc.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
|
|
SetServiceStatus(svc_handle, &svc);
|
|
return;
|
|
case SERVICE_CONTROL_INTERROGATE:
|
|
break;
|
|
}
|
|
|
|
SetServiceStatus(svc_handle, &svc);
|
|
}
|
|
|
|
BOOL WINAPI cw_stop_ctrl_handler(DWORD CtrlType)
|
|
{
|
|
if (CtrlType == CTRL_C_EVENT) {
|
|
SetConsoleCtrlHandler(cw_stop_ctrl_handler, FALSE);
|
|
fprintf(stderr, "Control+C pressed, aborting...\n");
|
|
exit(0);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void WINAPI ServiceMain(DWORD dwArgc, LPSTR *lpszArgv)
|
|
{
|
|
svc.dwServiceType = SERVICE_WIN32;
|
|
svc.dwCurrentState = SERVICE_START_PENDING;
|
|
svc.dwControlsAccepted = 0;
|
|
svc.dwWin32ExitCode = NO_ERROR;
|
|
svc.dwServiceSpecificExitCode = 0;
|
|
svc.dwCheckPoint = 0;
|
|
svc.dwWaitHint = 0;
|
|
|
|
if (!(svc_handle = RegisterServiceCtrlHandlerA(DT->lpServiceName, ServiceCtrlHandler))) {
|
|
logg(LOGG_INFO, "[service] RegisterServiceCtrlHandler() failed with %d\n", GetLastError());
|
|
exit(1);
|
|
}
|
|
|
|
if (!SetServiceStatus(svc_handle, &svc)) {
|
|
logg(LOGG_INFO, "[service] SetServiceStatus() failed with %d\n", GetLastError());
|
|
exit(1);
|
|
}
|
|
|
|
SetEvent(evStart);
|
|
WaitForSingleObject(DispatcherThread, INFINITE);
|
|
cw_stop_ctrl_handler(CTRL_C_EVENT);
|
|
} |