diff options
Diffstat (limited to 'examples/winexe/winexesvc.c')
-rw-r--r-- | examples/winexe/winexesvc.c | 745 |
1 files changed, 745 insertions, 0 deletions
diff --git a/examples/winexe/winexesvc.c b/examples/winexe/winexesvc.c new file mode 100644 index 0000000..3d2ebcc --- /dev/null +++ b/examples/winexe/winexesvc.c @@ -0,0 +1,745 @@ +/* + * Copyright (C) Andrzej Hajda 2009-2013 + * Contact: andrzej.hajda@wp.pl + * + * Source of this file: https://git.code.sf.net/p/winexe/winexe-waf + * commit b787d2a2c4b1abc3653bad10aec943b8efcd7aab. + * + * ** NOTE! The following "GPLv3 only" license applies to the winexe + * ** service files. This does NOT imply that all of Samba is released + * ** under the "GPLv3 only" license. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include <windows.h> +#include <aclapi.h> +#include <userenv.h> + +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> + +#include "winexesvc.h" + +#define BUFSIZE 256 + +#if 0 +#define dbg(arg...) \ +({\ + FILE *f = fopen("C:\\" SERVICE_NAME ".log", "at");\ + if (f) {\ + fprintf(f, arg);\ + fclose(f);\ + }\ +}) +#else +#define dbg(arg...) +#endif + +static SECURITY_ATTRIBUTES sa; + +/* Creates SECURITY_ATTRIBUTES sa with full access for BUILTIN\Administrators */ +static int CreatePipesSA() +{ + DWORD dwRes; + PSID pAdminSID = NULL; + PACL pACL = NULL; + PSECURITY_DESCRIPTOR pSD = NULL; + EXPLICIT_ACCESS ea; + SID_IDENTIFIER_AUTHORITY SIDAuthNT = {SECURITY_NT_AUTHORITY}; + + /* Create a SID for the BUILTIN\Administrators group. */ + if ( + !AllocateAndInitializeSid( + &SIDAuthNT, 2, + SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, + 0, 0, 0, 0, 0, 0, &pAdminSID + ) + ) { + dbg("AllocateAndInitializeSid Error %lu\n", GetLastError()); + return 0; + } + /* Initialize an EXPLICIT_ACCESS structure for an ACE. + The ACE will allow the Administrators group full access to the key. + */ + ea.grfAccessPermissions = FILE_ALL_ACCESS; + ea.grfAccessMode = SET_ACCESS; + ea.grfInheritance = NO_INHERITANCE; + ea.Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea.Trustee.TrusteeType = TRUSTEE_IS_GROUP; + ea.Trustee.ptstrName = (LPTSTR) pAdminSID; + + /* Create a new ACL that contains the new ACEs */ + dwRes = SetEntriesInAcl(1, &ea, NULL, &pACL); + if (ERROR_SUCCESS != dwRes) { + dbg("SetEntriesInAcl Error %lu\n", GetLastError()); + return 0; + } + /* Initialize a security descriptor */ + pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); + if (NULL == pSD) { + dbg("LocalAlloc Error %lu\n", GetLastError()); + return 0; + } + + if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) + { + dbg("InitializeSecurityDescriptor Error %lu\n", GetLastError()); + return 0; + } + /* Add the ACL to the security descriptor */ + if ( + !SetSecurityDescriptorDacl( + pSD, TRUE, /* bDaclPresent flag */ + pACL, FALSE /* not a default DACL */ + ) + ) { + dbg("SetSecurityDescriptorDacl Error %lu\n", GetLastError()); + return 0; + } + /* Initialize a security attributes structure */ + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.lpSecurityDescriptor = pSD; + sa.bInheritHandle = FALSE; + return 1; +} + +typedef struct { + HANDLE h; + OVERLAPPED o; +} OV_HANDLE; + +static int hgets(char *str, int n, OV_HANDLE *pipe) +{ + DWORD res; + DWORD count = 0; + --n; + while (--n >= 0) { + if (!ReadFile(pipe->h, str, 1, NULL, &pipe->o) && GetLastError() != ERROR_IO_PENDING) + goto finish; + if (!GetOverlappedResult(pipe->h, &pipe->o, &res, TRUE) || !res) + goto finish; + if (*str == '\n') + goto finish; + ++count; + ++str; + } +finish: + *str = 0; + return count; +} + +static int hprintf(OV_HANDLE *pipe, const char *fmt, ...) +{ + int res; + char buf[1024]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + if (!WriteFile(pipe->h, buf, strlen(buf), NULL, &pipe->o) && GetLastError() == ERROR_IO_PENDING) + GetOverlappedResult(pipe->h, &pipe->o, (LPDWORD)&res, TRUE); + FlushFileBuffers(pipe->h); + return res; +} + +typedef struct { + OV_HANDLE *pipe; + const char *cmd; + HANDLE pin; + HANDLE pout; + HANDLE perr; + HANDLE token; + int implevel; + int system; + int profile; + char *runas; + int conn_number; +} connection_context; + +typedef int CMD_FUNC(connection_context *); + +typedef struct { + const char *name; + CMD_FUNC *func; +} CMD_ITEM; + +static int cmd_set(connection_context *c) +{ + static const char* var_system = "system"; + static const char* var_implevel = "implevel"; + static const char* var_runas = "runas"; + static const char* var_profile = "profile"; + char *cmdline; + int res = 0; + + cmdline = strchr(c->cmd, ' '); + if (!cmdline) { + goto finish; + } + ++cmdline; + int l; + if ((strstr(cmdline, var_system) == cmdline) && (cmdline[l = strlen(var_system)] == ' ')) { + c->system = atoi(cmdline + l + 1); + } else if ((strstr(cmdline, var_implevel) == cmdline) && (cmdline[l = strlen(var_implevel)] == ' ')) { + c->implevel = atoi(cmdline + l + 1); + } else if ((strstr(cmdline, var_profile) == cmdline) && (cmdline[l = strlen(var_profile)] == ' ')) { + c->profile = atoi(cmdline + l + 1); + } else if ((strstr(cmdline, var_runas) == cmdline) && (cmdline[l = strlen(var_runas)] == ' ')) { + c->runas = strdup(cmdline + l + 1); + } else { + hprintf(c->pipe, "error Unknown command (%s)\n", c->cmd); + goto finish; + } + res = 1; +finish: + return res; +} + +static int cmd_get(connection_context *c) +{ + static const char* var_version = "version"; + static const char* var_codepage = "codepage"; + char *cmdline; + int res = 0; + + cmdline = strchr(c->cmd, ' '); + if (!cmdline) { + goto finish; + } + ++cmdline; + int l; + if ((strstr(cmdline, var_version) == cmdline) + && (cmdline[l = strlen(var_version)] == 0)) { + hprintf(c->pipe, "version 0x%04X\n", VERSION); + } else if ((strstr(cmdline, var_codepage) == cmdline) + && (cmdline[l = strlen(var_codepage)] == 0)) { + hprintf(c->pipe, "codepage %d\n", GetOEMCP()); + } else { + hprintf(c->pipe, "error Unknown argument (%s)\n", c->cmd); + goto finish; + } + res = 1; +finish: + return res; +} + +typedef struct { + char *user; + char *domain; + char *password; +} credentials; + +static int prepare_credentials(char *str, credentials *crd) +{ + char *p; + p = strchr(str, '/'); + if (!p) p = strchr(str, '\\'); + if (p) { + *p++ = 0; + crd->domain = str; + } else { + p = str; + crd->domain = "."; + } + crd->user = p; + p = strchr(p, '%'); + if (p) + *p++ = 0; + crd->password = p; + return 1; +} + +static int get_token(connection_context *c) +{ + int res = 0; + int wres; + HANDLE token; + + if (c->runas) { + credentials crd; + if (!prepare_credentials(c->runas, &crd)) { + hprintf(c->pipe, "error Incorrect runas credentials\n"); + goto finish; + } + wres = LogonUser(crd.user, crd.domain, crd.password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, &c->token); + if (!wres) { + hprintf(c->pipe, "error Cannot LogonUser(%s,%s,%s) %d\n", + crd.user, crd.domain, crd.password, GetLastError()); + goto finish; + } + res = 1; + goto finish; + } else if (c->system) { + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token)) { + hprintf(c->pipe, "error Cannot OpenProcessToken %d\n", GetLastError()); + goto finish; + } + } else { + if (!ImpersonateNamedPipeClient(c->pipe->h)) { + hprintf(c->pipe, "error Cannot ImpersonateNamedPipeClient %d\n", GetLastError()); + goto finish; + } + if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &token)) { + hprintf(c->pipe, "error Cannot OpenThreadToken %d\n", GetLastError()); + goto finishRevertToSelf; + } + } + if (!DuplicateTokenEx(token, MAXIMUM_ALLOWED, 0, c->implevel, TokenPrimary, &c->token)) { + hprintf(c->pipe, "error Cannot Duplicate Token %d\n", GetLastError()); + goto finishCloseToken; + } + res = 1; +finishCloseToken: + CloseHandle(token); +finishRevertToSelf: + if (!c->system) { + if (!RevertToSelf()) { + hprintf(c->pipe, "error Cannot RevertToSelf %d\n", GetLastError()); + res = 0; + } + } +finish: + return res; +} + +static int load_user_profile(connection_context *c) +{ + PROFILEINFO pi = { .dwSize = sizeof(PROFILEINFO) }; + DWORD ulen = 256; + TCHAR username[ulen]; + + GetUserName(username, &ulen); + pi.lpUserName = username; + + return LoadUserProfile(c->token, &pi); +} + +static int cmd_run(connection_context *c) +{ + char buf[256]; + int res = 0; + char *cmdline; + DWORD pipe_nr; + + cmdline = strchr(c->cmd, ' '); + if (!cmdline) { + goto finish; + } + ++cmdline; + + if (!get_token(c)) + return 0; + + pipe_nr = (GetCurrentProcessId() << 16) + (DWORD) c->conn_number; + + sprintf(buf, "\\\\.\\pipe\\" PIPE_NAME_IN, (unsigned int) pipe_nr); + c->pin = CreateNamedPipe(buf, + PIPE_ACCESS_DUPLEX, + PIPE_WAIT, + 1, + BUFSIZE, + BUFSIZE, + NMPWAIT_USE_DEFAULT_WAIT, + &sa); + if (c->pin == INVALID_HANDLE_VALUE) { + hprintf(c->pipe, "error Cannot create in pipe(%s), error 0x%08X\n", buf, GetLastError()); + goto finishCloseToken; + } + + sprintf(buf, "\\\\.\\pipe\\" PIPE_NAME_OUT, (unsigned int) pipe_nr); + c->pout = CreateNamedPipe(buf, + PIPE_ACCESS_DUPLEX, + PIPE_WAIT, + 1, + BUFSIZE, + BUFSIZE, + NMPWAIT_USE_DEFAULT_WAIT, + &sa); + if (c->pout == INVALID_HANDLE_VALUE) { + hprintf(c->pipe, "error Cannot create out pipe(%s), error 0x%08X\n", buf, GetLastError()); + goto finishClosePin; + } + + sprintf(buf, "\\\\.\\pipe\\" PIPE_NAME_ERR, (unsigned int) pipe_nr); + c->perr = CreateNamedPipe(buf, + PIPE_ACCESS_DUPLEX, + PIPE_WAIT, + 1, + BUFSIZE, + BUFSIZE, + NMPWAIT_USE_DEFAULT_WAIT, + &sa); + if (c->perr == INVALID_HANDLE_VALUE) { + hprintf(c->pipe, "error Cannot create err pipe(%s), error 0x%08x\n", buf, GetLastError()); + goto finishClosePout; + } + + /* Send handle to client (it will use it to connect pipes) */ + hprintf(c->pipe, CMD_STD_IO_ERR " %08X\n", pipe_nr); + + HANDLE ph[] = { c->pin, c->pout, c->perr }; + int i; + + for (i = 0; i < 3; ++i) { + if (ConnectNamedPipe(ph[i], NULL)) + continue; + int err = GetLastError(); + if (err != ERROR_PIPE_CONNECTED) { + hprintf(c->pipe, "error ConnectNamedPipe(pin) %d\n", err); + while (--i >= 0) + DisconnectNamedPipe(ph[i]); + goto finishClosePerr; + } + } + + SetHandleInformation(c->pin, HANDLE_FLAG_INHERIT, 1); + SetHandleInformation(c->pout, HANDLE_FLAG_INHERIT, 1); + SetHandleInformation(c->perr, HANDLE_FLAG_INHERIT, 1); + + if (c->profile) + load_user_profile(c); + + PROCESS_INFORMATION pi; + ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); + + STARTUPINFO si; + ZeroMemory(&si, sizeof(STARTUPINFO)); + si.cb = sizeof(STARTUPINFO); + si.hStdInput = c->pin; + si.hStdOutput = c->pout; + si.hStdError = c->perr; + si.dwFlags |= STARTF_USESTDHANDLES; + + if (CreateProcessAsUser( + c->token, + NULL, + cmdline, /* command line */ + NULL, /* process security attributes */ + NULL, /* primary thread security attributes */ + TRUE, /* handles are inherited */ + 0, /* creation flags */ + NULL, /* use parent's environment */ + NULL, /* use parent's current directory */ + &si, /* STARTUPINFO pointer */ + &pi) /* receives PROCESS_INFORMATION */ + ) { + HANDLE hlist[2] = {c->pipe->o.hEvent, pi.hProcess}; + DWORD ec; + char str[1]; + + if (!ResetEvent(c->pipe->o.hEvent)) + dbg("ResetEvent error - %lu\n", GetLastError()); + if (!ReadFile(c->pipe->h, str, 1, NULL, &c->pipe->o) && GetLastError() != ERROR_IO_PENDING) + dbg("ReadFile(control_pipe) error - %lu\n", GetLastError()); + ec = WaitForMultipleObjects(2, hlist, FALSE, INFINITE); + dbg("WaitForMultipleObjects=%lu\n", ec - WAIT_OBJECT_0); + if (ec != WAIT_OBJECT_0) + GetExitCodeProcess(pi.hProcess, &ec); + else + TerminateProcess(pi.hProcess, ec = 0x1234); + FlushFileBuffers(c->pout); + FlushFileBuffers(c->perr); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + hprintf(c->pipe, CMD_RETURN_CODE " %08X\n", ec); + } else { + hprintf(c->pipe, "error Creating process(%s) %d\n", cmdline, GetLastError()); + } + + DisconnectNamedPipe(c->perr); + DisconnectNamedPipe(c->pout); + DisconnectNamedPipe(c->pin); +finishClosePerr: + CloseHandle(c->perr); +finishClosePout: + CloseHandle(c->pout); +finishClosePin: + CloseHandle(c->pin); +finishCloseToken: + CloseHandle(c->token); +finish: + return res; +} + +static CMD_ITEM cmd_table[] = { + {"run", cmd_run}, + {"set", cmd_set}, + {"get", cmd_get}, + {NULL, NULL} +}; + +typedef struct { + OV_HANDLE *pipe; + int conn_number; +} connection_data; + +#define MAX_COMMAND_LENGTH (32768) + +static VOID handle_connection(connection_data *data) +{ + char *cmd = 0; + int res; + connection_context _c, *c = &_c; + cmd = malloc(MAX_COMMAND_LENGTH); + if (!cmd) { + hprintf(data->pipe, + "error: unable to allocate buffer for command\n"); + return; + } + ZeroMemory(cmd, MAX_COMMAND_LENGTH); + ZeroMemory(c, sizeof(connection_context)); + c->pipe = data->pipe; + c->cmd = cmd; + c->conn_number = data->conn_number; + free(data); + /* FIXME make wait for end of process or ctrl_pipe input */ + while (1) { + res = hgets(cmd, MAX_COMMAND_LENGTH, c->pipe); + if (res <= 0) { + dbg("Error reading from pipe(%p)\n", c->pipe->h); + goto finish; + } + dbg("Retrieved line: \"%s\"\n", cmd); + CMD_ITEM *ci; + for (ci = cmd_table; ci->name; ++ci) { + if (strstr(cmd, ci->name) != cmd) + continue; + char c = cmd[strlen(ci->name)]; + if (!c || (c == ' ')) + break; + } + if (ci->name) { + if (!ci->func(c)) + goto finish; + } else { + hprintf(c->pipe, "error Ignoring unknown command (%s)\n", cmd); + } + } +finish: + FlushFileBuffers(c->pipe->h); + DisconnectNamedPipe(c->pipe->h); + CloseHandle(c->pipe->h); + CloseHandle(c->pipe->o.hEvent); + free(c->pipe); + free(cmd); +} + +static int conn_number = 0; + +DWORD WINAPI winexesvc_loop(LPVOID lpParameter) +{ + BOOL res; + + dbg("server_loop: alive\n"); + if (!CreatePipesSA()) { + dbg("CreatePipesSA failed (%08lX)\n", GetLastError()); + return -1; + } + dbg("server_loop: CreatePipesSA done\n"); + for (;;) { + dbg("server_loop: Create Pipe\n"); + OV_HANDLE *pipe; + pipe = (OV_HANDLE *)malloc(sizeof(OV_HANDLE)); + ZeroMemory(&pipe->o, sizeof(OVERLAPPED)); + pipe->o.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); + pipe->h = CreateNamedPipe("\\\\.\\pipe\\" PIPE_NAME, + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, + PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, + BUFSIZE, + BUFSIZE, + NMPWAIT_USE_DEFAULT_WAIT, + &sa); + if (pipe->h == INVALID_HANDLE_VALUE) { + dbg("CreatePipe failed(%08lX)\n", + GetLastError()); + CloseHandle(pipe->o.hEvent); + free(pipe); + return 0; + } + + dbg("server_loop: Connect Pipe\n"); + if (ConnectNamedPipe(pipe->h, &pipe->o)) { + dbg("server_loop: Connect Pipe err %08lX\n", GetLastError()); + res = FALSE; + } else { + switch (GetLastError()) { + case ERROR_IO_PENDING: + dbg("server_loop: Connect Pipe(0) pending\n"); + DWORD t; + res = GetOverlappedResult(pipe->h, &pipe->o, &t, TRUE); + break; + case ERROR_PIPE_CONNECTED: + dbg("server_loop: Connect Pipe(0) connected\n"); + res = TRUE; + break; + default: + dbg("server_loop: Connect Pipe(0) err %08lX\n", GetLastError()); + res = FALSE; + } + } + + if (res) { + connection_data *cd = malloc(sizeof(connection_data)); + cd->pipe = pipe; + cd->conn_number = ++conn_number; + dbg("server_loop: CreateThread\n"); + HANDLE th = CreateThread(NULL, /* no security attribute */ + 0, /* default stack size */ + (LPTHREAD_START_ROUTINE) + handle_connection, + (LPVOID) cd, /* thread parameter */ + 0, /* not suspended */ + NULL); /* returns thread ID */ + if (!th) { + dbg("Cannot create thread\n"); + CloseHandle(pipe->h); + CloseHandle(pipe->o.hEvent); + free(pipe); + } else { + CloseHandle(th); + dbg("server_loop: Thread created\n"); + } + } else { + dbg("server_loop: Pipe not connected\n"); + CloseHandle(pipe->h); + CloseHandle(pipe->o.hEvent); + free(pipe); + } + } + dbg("server_loop: STH wrong\n"); + return 0; +} + +static SERVICE_STATUS winexesvcStatus; +static SERVICE_STATUS_HANDLE winexesvcStatusHandle; + +static VOID WINAPI winexesvcCtrlHandler(DWORD Opcode) +{ + switch (Opcode) { + case SERVICE_CONTROL_PAUSE: + dbg(SERVICE_NAME ": winexesvcCtrlHandler: pause\n", 0); + winexesvcStatus.dwCurrentState = SERVICE_PAUSED; + break; + + case SERVICE_CONTROL_CONTINUE: + dbg(SERVICE_NAME ": winexesvcCtrlHandler: continue\n", 0); + winexesvcStatus.dwCurrentState = SERVICE_RUNNING; + break; + + case SERVICE_CONTROL_STOP: + dbg(SERVICE_NAME ": winexesvcCtrlHandler: stop\n", 0); + winexesvcStatus.dwWin32ExitCode = 0; + winexesvcStatus.dwCurrentState = SERVICE_STOPPED; + winexesvcStatus.dwCheckPoint = 0; + winexesvcStatus.dwWaitHint = 0; + + if (!SetServiceStatus (winexesvcStatusHandle, &winexesvcStatus)) + dbg(SERVICE_NAME ": SetServiceStatus error %ld\n", GetLastError()); + + dbg(SERVICE_NAME ": Leaving winexesvc\n", 0); + return; + + case SERVICE_CONTROL_INTERROGATE: + dbg(SERVICE_NAME ": winexesvcCtrlHandler: interrogate\n", 0); + break; + + default: + dbg(SERVICE_NAME ": Unrecognized opcode %ld\n", Opcode); + } + + if (!SetServiceStatus(winexesvcStatusHandle, &winexesvcStatus)) + dbg(SERVICE_NAME ": SetServiceStatus error 0x%08X\n", GetLastError()); + + return; +} + +static DWORD winexesvcInitialization(DWORD argc, LPTSTR * argv, DWORD * specificError) +{ + HANDLE th = CreateThread(NULL, 0, winexesvc_loop, NULL, 0, NULL); + if (th) { + CloseHandle(th); + return NO_ERROR; + } + return !NO_ERROR; +} + +static void WINAPI winexesvcStart(DWORD argc, LPTSTR * argv) +{ + DWORD status; + DWORD specificError; + + winexesvcStatus.dwServiceType = SERVICE_WIN32; + winexesvcStatus.dwCurrentState = SERVICE_START_PENDING; + winexesvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE; + winexesvcStatus.dwWin32ExitCode = 0; + winexesvcStatus.dwServiceSpecificExitCode = 0; + winexesvcStatus.dwCheckPoint = 0; + winexesvcStatus.dwWaitHint = 0; + + dbg(SERVICE_NAME ": RegisterServiceCtrlHandler\n", 0); + + winexesvcStatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, winexesvcCtrlHandler); + + if (winexesvcStatusHandle == (SERVICE_STATUS_HANDLE) 0) { + dbg(SERVICE_NAME + ": RegisterServiceCtrlHandler failed %d\n", + GetLastError()); + return; + } + status = winexesvcInitialization(argc, argv, &specificError); + + if (status != NO_ERROR) { + winexesvcStatus.dwCurrentState = SERVICE_STOPPED; + winexesvcStatus.dwCheckPoint = 0; + winexesvcStatus.dwWaitHint = 0; + winexesvcStatus.dwWin32ExitCode = status; + winexesvcStatus.dwServiceSpecificExitCode = specificError; + + SetServiceStatus(winexesvcStatusHandle, &winexesvcStatus); + return; + } + + winexesvcStatus.dwCurrentState = SERVICE_RUNNING; + winexesvcStatus.dwCheckPoint = 0; + winexesvcStatus.dwWaitHint = 0; + + if (!SetServiceStatus(winexesvcStatusHandle, &winexesvcStatus)) { + status = GetLastError(); + dbg(SERVICE_NAME ": SetServiceStatus error %ld\n", status); + } + + dbg(SERVICE_NAME ": Returning the Main Thread \n", 0); + + return; +} + +int main(int argc, char *argv[]) +{ + SERVICE_TABLE_ENTRY DispatchTable[] = { + {SERVICE_NAME, winexesvcStart}, + {NULL, NULL} + }; + + dbg(SERVICE_NAME ": StartServiceCtrlDispatcher %d\n", GetLastError()); + if (!StartServiceCtrlDispatcher(DispatchTable)) { + dbg(SERVICE_NAME + ": StartServiceCtrlDispatcher (%d)\n", + GetLastError()); + } + return 0; +} |