examples: Add winexe re-implemented on current Samba libs
[kamenim/samba-autobuild/.git] / examples / winexe / winexesvc.c
1 /*
2  * Copyright (C) Andrzej Hajda 2009-2013
3  * Contact: andrzej.hajda@wp.pl
4  *
5  * Source of this file: https://git.code.sf.net/p/winexe/winexe-waf
6  * commit b787d2a2c4b1abc3653bad10aec943b8efcd7aab.
7  *
8  * ** NOTE! The following "GPLv3 only" license applies to the winexe
9  * ** service files.  This does NOT imply that all of Samba is released
10  * ** under the "GPLv3 only" license.
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * version 3 as published by the Free Software Foundation.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, see <http://www.gnu.org/licenses/>.
23  */
24
25 #include <windows.h>
26 #include <aclapi.h>
27 #include <userenv.h>
28
29 #include <stdio.h>
30 #include <string.h>
31 #include <stdarg.h>
32 #include <stdlib.h>
33
34 #include "winexesvc.h"
35
36 #define BUFSIZE 256
37
38 #if 0
39 #define dbg(arg...) \
40 ({\
41         FILE *f = fopen("C:\\" SERVICE_NAME ".log", "at");\
42         if (f) {\
43                 fprintf(f, arg);\
44                 fclose(f);\
45         }\
46 })
47 #else
48 #define dbg(arg...)
49 #endif
50
51 static SECURITY_ATTRIBUTES sa;
52
53 /* Creates SECURITY_ATTRIBUTES sa with full access for BUILTIN\Administrators */
54 static int CreatePipesSA()
55 {
56         DWORD dwRes;
57         PSID pAdminSID = NULL;
58         PACL pACL = NULL;
59         PSECURITY_DESCRIPTOR pSD = NULL;
60         EXPLICIT_ACCESS ea;
61         SID_IDENTIFIER_AUTHORITY SIDAuthNT = {SECURITY_NT_AUTHORITY};
62
63         /* Create a SID for the BUILTIN\Administrators group. */
64         if (
65                 !AllocateAndInitializeSid(
66                         &SIDAuthNT, 2,
67                         SECURITY_BUILTIN_DOMAIN_RID,
68                         DOMAIN_ALIAS_RID_ADMINS,
69                         0, 0, 0, 0, 0, 0, &pAdminSID
70                 )
71         ) {
72                 dbg("AllocateAndInitializeSid Error %lu\n", GetLastError());
73                 return 0;
74         }
75         /* Initialize an EXPLICIT_ACCESS structure for an ACE.
76            The ACE will allow the Administrators group full access to the key.
77         */
78         ea.grfAccessPermissions = FILE_ALL_ACCESS;
79         ea.grfAccessMode = SET_ACCESS;
80         ea.grfInheritance = NO_INHERITANCE;
81         ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
82         ea.Trustee.TrusteeType = TRUSTEE_IS_GROUP;
83         ea.Trustee.ptstrName = (LPTSTR) pAdminSID;
84
85         /* Create a new ACL that contains the new ACEs */
86         dwRes = SetEntriesInAcl(1, &ea, NULL, &pACL);
87         if (ERROR_SUCCESS != dwRes) {
88                 dbg("SetEntriesInAcl Error %lu\n", GetLastError());
89                 return 0;
90         }
91         /* Initialize a security descriptor */
92         pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
93         if (NULL == pSD) {
94                 dbg("LocalAlloc Error %lu\n", GetLastError());
95                 return 0;
96         }
97
98         if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION))
99         {
100                 dbg("InitializeSecurityDescriptor Error %lu\n", GetLastError());
101                 return 0;
102         }
103         /* Add the ACL to the security descriptor */
104         if (
105                 !SetSecurityDescriptorDacl(
106                         pSD, TRUE,  /* bDaclPresent flag */
107                         pACL, FALSE  /* not a default DACL */
108                 )
109         ) {
110                 dbg("SetSecurityDescriptorDacl Error %lu\n", GetLastError());
111                 return 0;
112         }
113         /* Initialize a security attributes structure */
114         sa.nLength = sizeof(SECURITY_ATTRIBUTES);
115         sa.lpSecurityDescriptor = pSD;
116         sa.bInheritHandle = FALSE;
117         return 1;
118 }
119
120 typedef struct {
121         HANDLE h;
122         OVERLAPPED o;
123 } OV_HANDLE;
124
125 static int hgets(char *str, int n, OV_HANDLE *pipe)
126 {
127         DWORD res;
128         DWORD count = 0;
129         --n;
130         while (--n >= 0) {
131                 if (!ReadFile(pipe->h, str, 1, NULL, &pipe->o) && GetLastError() != ERROR_IO_PENDING)
132                         goto finish;
133                 if (!GetOverlappedResult(pipe->h, &pipe->o, &res, TRUE) || !res)
134                         goto finish;
135                 if (*str == '\n')
136                         goto finish;
137                 ++count;
138                 ++str;
139         }
140 finish:
141         *str = 0;
142         return count;
143 }
144
145 static int hprintf(OV_HANDLE *pipe, const char *fmt, ...)
146 {
147         int res;
148         char buf[1024];
149         va_list ap;
150         va_start(ap, fmt);
151         vsnprintf(buf, sizeof(buf), fmt, ap);
152         va_end(ap);
153         if (!WriteFile(pipe->h, buf, strlen(buf), NULL, &pipe->o) && GetLastError() == ERROR_IO_PENDING)
154                 GetOverlappedResult(pipe->h, &pipe->o, (LPDWORD)&res, TRUE);
155         FlushFileBuffers(pipe->h);
156         return res;
157 }
158
159 typedef struct {
160         OV_HANDLE *pipe;
161         const char *cmd;
162         HANDLE pin;
163         HANDLE pout;
164         HANDLE perr;
165         HANDLE token;
166         int implevel;
167         int system;
168         int profile;
169         char *runas;
170         int conn_number;
171 } connection_context;
172
173 typedef int CMD_FUNC(connection_context *);
174
175 typedef struct {
176         const char *name;
177         CMD_FUNC *func;
178 } CMD_ITEM;
179
180 static int cmd_set(connection_context *c)
181 {
182         static const char* var_system = "system";
183         static const char* var_implevel = "implevel";
184         static const char* var_runas = "runas";
185         static const char* var_profile = "profile";
186         char *cmdline;
187         int res = 0;
188
189         cmdline = strchr(c->cmd, ' ');
190         if (!cmdline) {
191                 goto finish;
192         }
193         ++cmdline;
194         int l;
195         if ((strstr(cmdline, var_system) == cmdline) && (cmdline[l = strlen(var_system)] == ' ')) {
196                 c->system = atoi(cmdline + l + 1);
197         } else if ((strstr(cmdline, var_implevel) == cmdline) && (cmdline[l = strlen(var_implevel)] == ' ')) {
198                 c->implevel = atoi(cmdline + l + 1);
199         } else if ((strstr(cmdline, var_profile) == cmdline) && (cmdline[l = strlen(var_profile)] == ' ')) {
200                 c->profile = atoi(cmdline + l + 1);
201         } else if ((strstr(cmdline, var_runas) == cmdline) && (cmdline[l = strlen(var_runas)] == ' ')) {
202                 c->runas = strdup(cmdline + l + 1);
203         } else {
204                 hprintf(c->pipe, "error Unknown commad (%s)\n", c->cmd);
205                 goto finish;
206         }
207         res = 1;
208 finish:
209         return res;
210 }
211
212 static int cmd_get(connection_context *c)
213 {
214         static const char* var_version = "version";
215         static const char* var_codepage = "codepage";
216         char *cmdline;
217         int res = 0;
218
219         cmdline = strchr(c->cmd, ' ');
220         if (!cmdline) {
221                 goto finish;
222         }
223         ++cmdline;
224         int l;
225         if ((strstr(cmdline, var_version) == cmdline)
226             && (cmdline[l = strlen(var_version)] == 0)) {
227                 hprintf(c->pipe, "version 0x%04X\n", VERSION);
228         } else if ((strstr(cmdline, var_codepage) == cmdline)
229                    && (cmdline[l = strlen(var_codepage)] == 0)) {
230                 hprintf(c->pipe, "codepage %d\n", GetOEMCP());
231         } else {
232                 hprintf(c->pipe, "error Unknown argument (%s)\n", c->cmd);
233                 goto finish;
234         }
235         res = 1;
236 finish:
237         return res;
238 }
239
240 typedef struct {
241         char *user;
242         char *domain;
243         char *password;
244 } credentials;
245
246 static int prepare_credentials(char *str, credentials *crd)
247 {
248         char *p;
249         p = strchr(str, '/');
250         if (!p) p = strchr(str, '\\');
251         if (p) {
252                 *p++ = 0;
253                 crd->domain = str;
254         } else {
255                 p = str;
256                 crd->domain = ".";
257         }
258         crd->user = p;
259         p = strchr(p, '%');
260         if (p)
261                 *p++ = 0;
262         crd->password = p;
263         return 1;
264 }
265
266 static int get_token(connection_context *c)
267 {
268         int res = 0;
269         int wres;
270         HANDLE token;
271
272         if (c->runas) {
273                 credentials crd;
274                 if (!prepare_credentials(c->runas, &crd)) {
275                         hprintf(c->pipe, "error Incorrect runas credentials\n");
276                         goto finish;
277                 }
278                 wres = LogonUser(crd.user, crd.domain, crd.password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, &c->token);
279                 if (!wres) {
280                         hprintf(c->pipe, "error Cannot LogonUser(%s,%s,%s) %d\n",
281                                 crd.user, crd.domain, crd.password, GetLastError());
282                         goto finish;
283                 }
284                 res = 1;
285                 goto finish;
286         } else if (c->system) {
287                 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token)) {
288                         hprintf(c->pipe, "error Cannot OpenProcessToken %d\n", GetLastError());
289                         goto finish;
290                 }
291         } else {
292                 if (!ImpersonateNamedPipeClient(c->pipe->h)) {
293                         hprintf(c->pipe, "error Cannot ImpersonateNamedPipeClient %d\n", GetLastError());
294                         goto finish;
295                 }
296                 if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &token)) {
297                         hprintf(c->pipe, "error Cannot OpenThreadToken %d\n", GetLastError());
298                         goto finishRevertToSelf;
299                 }
300         }
301         if (!DuplicateTokenEx(token, MAXIMUM_ALLOWED, 0, c->implevel, TokenPrimary, &c->token)) {
302                 hprintf(c->pipe, "error Cannot Duplicate Token %d\n", GetLastError());
303                 goto finishCloseToken;
304         }
305         res = 1;
306 finishCloseToken:
307         CloseHandle(token);
308 finishRevertToSelf:
309         if (!c->system) {
310                 if (!RevertToSelf()) {
311                         hprintf(c->pipe, "error Cannot RevertToSelf %d\n", GetLastError());
312                         res = 0;
313                 }
314         }
315 finish:
316         return res;
317 }
318
319 static int load_user_profile(connection_context *c)
320 {
321         PROFILEINFO pi = { .dwSize = sizeof(PROFILEINFO) };
322         DWORD ulen = 256;
323         TCHAR username[ulen];
324
325         GetUserName(username, &ulen);
326         pi.lpUserName = username;
327
328         return LoadUserProfile(c->token, &pi);
329 }
330
331 static int cmd_run(connection_context *c)
332 {
333         char buf[256];
334         int res = 0;
335         char *cmdline;
336         DWORD pipe_nr;
337
338         cmdline = strchr(c->cmd, ' ');
339         if (!cmdline) {
340                 goto finish;
341         }
342         ++cmdline;
343
344         if (!get_token(c))
345                 return 0;
346
347         pipe_nr = (GetCurrentProcessId() << 16) + (DWORD) c->conn_number;
348
349         sprintf(buf, "\\\\.\\pipe\\" PIPE_NAME_IN, (unsigned int) pipe_nr);
350         c->pin = CreateNamedPipe(buf,
351                                  PIPE_ACCESS_DUPLEX,
352                                  PIPE_WAIT,
353                                  1,
354                                  BUFSIZE,
355                                  BUFSIZE,
356                                  NMPWAIT_USE_DEFAULT_WAIT,
357                                  &sa);
358         if (c->pin == INVALID_HANDLE_VALUE) {
359                 hprintf(c->pipe, "error Cannot create in pipe(%s), error 0x%08X\n", buf, GetLastError());
360                 goto finishCloseToken;
361         }
362
363         sprintf(buf, "\\\\.\\pipe\\" PIPE_NAME_OUT, (unsigned int) pipe_nr);
364         c->pout = CreateNamedPipe(buf,
365                                   PIPE_ACCESS_DUPLEX,
366                                   PIPE_WAIT,
367                                   1,
368                                   BUFSIZE,
369                                   BUFSIZE,
370                                   NMPWAIT_USE_DEFAULT_WAIT,
371                                   &sa);
372         if (c->pout == INVALID_HANDLE_VALUE) {
373                 hprintf(c->pipe, "error Cannot create out pipe(%s), error 0x%08X\n", buf, GetLastError());
374                 goto finishClosePin;
375         }
376
377         sprintf(buf, "\\\\.\\pipe\\" PIPE_NAME_ERR, (unsigned int) pipe_nr);
378         c->perr = CreateNamedPipe(buf,
379                                   PIPE_ACCESS_DUPLEX,
380                                   PIPE_WAIT,
381                                   1,
382                                   BUFSIZE,
383                                   BUFSIZE,
384                                   NMPWAIT_USE_DEFAULT_WAIT,
385                                   &sa);
386         if (c->perr == INVALID_HANDLE_VALUE) {
387                 hprintf(c->pipe, "error Cannot create err pipe(%s), error 0x%08x\n", buf, GetLastError());
388                 goto finishClosePout;
389         }
390
391         /* Send handle to client (it will use it to connect pipes) */
392         hprintf(c->pipe, CMD_STD_IO_ERR " %08X\n", pipe_nr);
393
394         HANDLE ph[] = { c->pin, c->pout, c->perr };
395         int i;
396
397         for (i = 0; i < 3; ++i) {
398                 if (ConnectNamedPipe(ph[i], NULL))
399                         continue;
400                 int err = GetLastError();
401                 if (err != ERROR_PIPE_CONNECTED) {
402                         hprintf(c->pipe, "error ConnectNamedPipe(pin) %d\n", err);
403                         while (--i >= 0)
404                                 DisconnectNamedPipe(ph[i]);
405                         goto finishClosePerr;
406                 }
407         }
408
409         SetHandleInformation(c->pin, HANDLE_FLAG_INHERIT, 1);
410         SetHandleInformation(c->pout, HANDLE_FLAG_INHERIT, 1);
411         SetHandleInformation(c->perr, HANDLE_FLAG_INHERIT, 1);
412
413         if (c->profile)
414                 load_user_profile(c);
415
416         PROCESS_INFORMATION pi;
417         ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
418
419         STARTUPINFO si;
420         ZeroMemory(&si, sizeof(STARTUPINFO));
421         si.cb = sizeof(STARTUPINFO);
422         si.hStdInput = c->pin;
423         si.hStdOutput = c->pout;
424         si.hStdError = c->perr;
425         si.dwFlags |= STARTF_USESTDHANDLES;
426
427         if (CreateProcessAsUser(
428                 c->token,
429                 NULL,
430                 cmdline,        /* command line */
431                 NULL,   /* process security attributes */
432                 NULL,   /* primary thread security attributes */
433                 TRUE,   /* handles are inherited */
434                 0,      /* creation flags */
435                 NULL,   /* use parent's environment */
436                 NULL,   /* use parent's current directory */
437                 &si,    /* STARTUPINFO pointer */
438                 &pi)    /* receives PROCESS_INFORMATION */
439         ) {
440                 HANDLE hlist[2] = {c->pipe->o.hEvent, pi.hProcess};
441                 DWORD ec;
442                 char str[1];
443
444                 if (!ResetEvent(c->pipe->o.hEvent))
445                         dbg("ResetEvent error - %lu\n", GetLastError());
446                 if (!ReadFile(c->pipe->h, str, 1, NULL, &c->pipe->o) && GetLastError() != ERROR_IO_PENDING)
447                         dbg("ReadFile(control_pipe) error - %lu\n", GetLastError());
448                 ec = WaitForMultipleObjects(2, hlist, FALSE, INFINITE);
449                 dbg("WaitForMultipleObjects=%lu\n", ec - WAIT_OBJECT_0);
450                 if (ec != WAIT_OBJECT_0)
451                         GetExitCodeProcess(pi.hProcess, &ec);
452                 else
453                         TerminateProcess(pi.hProcess, ec = 0x1234);
454                 FlushFileBuffers(c->pout);
455                 FlushFileBuffers(c->perr);
456                 CloseHandle(pi.hProcess);
457                 CloseHandle(pi.hThread);
458                 hprintf(c->pipe, CMD_RETURN_CODE " %08X\n", ec);
459         } else {
460                 hprintf(c->pipe, "error Creating process(%s) %d\n", cmdline, GetLastError());
461         }
462
463         DisconnectNamedPipe(c->perr);
464         DisconnectNamedPipe(c->pout);
465         DisconnectNamedPipe(c->pin);
466 finishClosePerr:
467         CloseHandle(c->perr);
468 finishClosePout:
469         CloseHandle(c->pout);
470 finishClosePin:
471         CloseHandle(c->pin);
472 finishCloseToken:
473         CloseHandle(c->token);
474 finish:
475         return res;
476 }
477
478 static CMD_ITEM cmd_table[] = {
479         {"run", cmd_run},
480         {"set", cmd_set},
481         {"get", cmd_get},
482         {NULL, NULL}
483 };
484
485 typedef struct {
486         OV_HANDLE *pipe;
487         int conn_number;
488 } connection_data;
489
490 #define MAX_COMMAND_LENGTH (32768)
491
492 static VOID handle_connection(connection_data *data)
493 {
494         char *cmd = 0;
495         int res;
496         connection_context _c, *c = &_c;
497         cmd = malloc(MAX_COMMAND_LENGTH);
498         if (!cmd) {
499                 hprintf(data->pipe,
500                         "error: unable to allocate buffer for command\n");
501                 return;
502         }
503         ZeroMemory(cmd, MAX_COMMAND_LENGTH);
504         ZeroMemory(c, sizeof(connection_context));
505         c->pipe = data->pipe;
506         c->cmd = cmd;
507         c->conn_number = data->conn_number;
508         free(data);
509         /* FIXME make wait for end of process or ctrl_pipe input */
510         while (1) {
511                 res = hgets(cmd, MAX_COMMAND_LENGTH, c->pipe);
512                 if (res <= 0) {
513                         dbg("Error reading from pipe(%p)\n", c->pipe->h);
514                         goto finish;
515                 }
516                 dbg("Retrieved line: \"%s\"\n", cmd);
517                 CMD_ITEM *ci;
518                 for (ci = cmd_table; ci->name; ++ci) {
519                         if (strstr(cmd, ci->name) != cmd)
520                                 continue;
521                         char c = cmd[strlen(ci->name)];
522                         if (!c || (c == ' '))
523                                 break;
524                 }
525                 if (ci->name) {
526                         if (!ci->func(c))
527                                 goto finish;
528                 } else {
529                         hprintf(c->pipe, "error Ignoring unknown command (%s)\n", cmd);
530                 }
531         }
532 finish:
533         FlushFileBuffers(c->pipe->h);
534         DisconnectNamedPipe(c->pipe->h);
535         CloseHandle(c->pipe->h);
536         CloseHandle(c->pipe->o.hEvent);
537         free(c->pipe);
538         free(cmd);
539 }
540
541 static int conn_number = 0;
542
543 DWORD WINAPI winexesvc_loop(LPVOID lpParameter)
544 {
545         BOOL res;
546
547         dbg("server_loop: alive\n");
548         if (!CreatePipesSA()) {
549                 dbg("CreatePipesSA failed (%08lX)\n", GetLastError());
550                 return -1;
551         }
552         dbg("server_loop: CreatePipesSA done\n");
553         for (;;) {
554                 dbg("server_loop: Create Pipe\n");
555                 OV_HANDLE *pipe;
556                 pipe = (OV_HANDLE *)malloc(sizeof(OV_HANDLE));
557                 ZeroMemory(&pipe->o, sizeof(OVERLAPPED));
558                 pipe->o.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
559                 pipe->h = CreateNamedPipe("\\\\.\\pipe\\" PIPE_NAME,
560                                           PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
561                                           PIPE_WAIT,
562                                           PIPE_UNLIMITED_INSTANCES,
563                                           BUFSIZE,
564                                           BUFSIZE,
565                                           NMPWAIT_USE_DEFAULT_WAIT,
566                                           &sa);
567                 if (pipe->h == INVALID_HANDLE_VALUE) {
568                         dbg("CreatePipe failed(%08lX)\n",
569                                     GetLastError());
570                         CloseHandle(pipe->o.hEvent);
571                         free(pipe);
572                         return 0;
573                 }
574
575                 dbg("server_loop: Connect Pipe\n");
576                 if (ConnectNamedPipe(pipe->h, &pipe->o)) {
577                         dbg("server_loop: Connect Pipe err %08lX\n", GetLastError());
578                         res = FALSE;
579                 } else {
580                         switch (GetLastError()) {
581                           case ERROR_IO_PENDING:
582                                 dbg("server_loop: Connect Pipe(0) pending\n");
583                                 DWORD t;
584                                 res = GetOverlappedResult(pipe->h, &pipe->o, &t, TRUE);
585                                 break;
586                           case ERROR_PIPE_CONNECTED:
587                                 dbg("server_loop: Connect Pipe(0) connected\n");
588                                 res = TRUE;
589                                 break;
590                           default:
591                                 dbg("server_loop: Connect Pipe(0) err %08lX\n", GetLastError());
592                                 res = FALSE;
593                         }
594                 }
595
596                 if (res) {
597                         connection_data *cd = malloc(sizeof(connection_data));
598                         cd->pipe = pipe;
599                         cd->conn_number = ++conn_number;
600                         dbg("server_loop: CreateThread\n");
601                         HANDLE th = CreateThread(NULL,  /* no security attribute */
602                                                  0,     /* default stack size */
603                                                  (LPTHREAD_START_ROUTINE)
604                                                  handle_connection,
605                                                  (LPVOID) cd,   /* thread parameter */
606                                                  0,     /* not suspended */
607                                                  NULL); /* returns thread ID */
608                         if (!th) {
609                                 dbg("Cannot create thread\n");
610                                 CloseHandle(pipe->h);
611                                 CloseHandle(pipe->o.hEvent);
612                                 free(pipe);
613                         } else {
614                                 CloseHandle(th);
615                                 dbg("server_loop: Thread created\n");
616                         }
617                 } else {
618                         dbg("server_loop: Pipe not connected\n");
619                         CloseHandle(pipe->h);
620                         CloseHandle(pipe->o.hEvent);
621                         free(pipe);
622                 }
623         }
624         dbg("server_loop: STH wrong\n");
625         return 0;
626 }
627
628 static SERVICE_STATUS winexesvcStatus;
629 static SERVICE_STATUS_HANDLE winexesvcStatusHandle;
630
631 static VOID WINAPI winexesvcCtrlHandler(DWORD Opcode)
632 {
633         switch (Opcode) {
634           case SERVICE_CONTROL_PAUSE:
635                 dbg(SERVICE_NAME ": winexesvcCtrlHandler: pause\n", 0);
636                 winexesvcStatus.dwCurrentState = SERVICE_PAUSED;
637                 break;
638
639           case SERVICE_CONTROL_CONTINUE:
640                 dbg(SERVICE_NAME ": winexesvcCtrlHandler: continue\n", 0);
641                 winexesvcStatus.dwCurrentState = SERVICE_RUNNING;
642                 break;
643
644           case SERVICE_CONTROL_STOP:
645                 dbg(SERVICE_NAME ": winexesvcCtrlHandler: stop\n", 0);
646                 winexesvcStatus.dwWin32ExitCode = 0;
647                 winexesvcStatus.dwCurrentState = SERVICE_STOPPED;
648                 winexesvcStatus.dwCheckPoint = 0;
649                 winexesvcStatus.dwWaitHint = 0;
650
651                 if (!SetServiceStatus (winexesvcStatusHandle, &winexesvcStatus))
652                         dbg(SERVICE_NAME ": SetServiceStatus error %ld\n", GetLastError());
653
654                 dbg(SERVICE_NAME ": Leaving winexesvc\n", 0);
655                 return;
656
657           case SERVICE_CONTROL_INTERROGATE:
658                 dbg(SERVICE_NAME ": winexesvcCtrlHandler: interrogate\n", 0);
659                 break;
660
661           default:
662                 dbg(SERVICE_NAME ": Unrecognized opcode %ld\n", Opcode);
663         }
664
665         if (!SetServiceStatus(winexesvcStatusHandle, &winexesvcStatus))
666                 dbg(SERVICE_NAME ": SetServiceStatus error 0x%08X\n", GetLastError());
667
668         return;
669 }
670
671 static DWORD winexesvcInitialization(DWORD argc, LPTSTR * argv, DWORD * specificError)
672 {
673         HANDLE th = CreateThread(NULL, 0, winexesvc_loop, NULL, 0, NULL);
674         if (th) {
675                 CloseHandle(th);
676                 return NO_ERROR;
677         }
678         return !NO_ERROR;
679 }
680
681 static void WINAPI winexesvcStart(DWORD argc, LPTSTR * argv)
682 {
683         DWORD status;
684         DWORD specificError;
685
686         winexesvcStatus.dwServiceType = SERVICE_WIN32;
687         winexesvcStatus.dwCurrentState = SERVICE_START_PENDING;
688         winexesvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
689         winexesvcStatus.dwWin32ExitCode = 0;
690         winexesvcStatus.dwServiceSpecificExitCode = 0;
691         winexesvcStatus.dwCheckPoint = 0;
692         winexesvcStatus.dwWaitHint = 0;
693
694         dbg(SERVICE_NAME ": RegisterServiceCtrlHandler\n", 0);
695
696         winexesvcStatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, winexesvcCtrlHandler);
697
698         if (winexesvcStatusHandle == (SERVICE_STATUS_HANDLE) 0) {
699                 dbg(SERVICE_NAME
700                             ": RegisterServiceCtrlHandler failed %d\n",
701                             GetLastError());
702                 return;
703         }
704         status = winexesvcInitialization(argc, argv, &specificError);
705
706         if (status != NO_ERROR) {
707                 winexesvcStatus.dwCurrentState = SERVICE_STOPPED;
708                 winexesvcStatus.dwCheckPoint = 0;
709                 winexesvcStatus.dwWaitHint = 0;
710                 winexesvcStatus.dwWin32ExitCode = status;
711                 winexesvcStatus.dwServiceSpecificExitCode = specificError;
712
713                 SetServiceStatus(winexesvcStatusHandle, &winexesvcStatus);
714                 return;
715         }
716
717         winexesvcStatus.dwCurrentState = SERVICE_RUNNING;
718         winexesvcStatus.dwCheckPoint = 0;
719         winexesvcStatus.dwWaitHint = 0;
720
721         if (!SetServiceStatus(winexesvcStatusHandle, &winexesvcStatus)) {
722                 status = GetLastError();
723                 dbg(SERVICE_NAME ": SetServiceStatus error %ld\n", status);
724         }
725
726         dbg(SERVICE_NAME ": Returning the Main Thread \n", 0);
727
728         return;
729 }
730
731 int main(int argc, char *argv[])
732 {
733         SERVICE_TABLE_ENTRY DispatchTable[] = {
734                 {SERVICE_NAME, winexesvcStart},
735                 {NULL, NULL}
736         };
737
738         dbg(SERVICE_NAME ": StartServiceCtrlDispatcher %d\n", GetLastError());
739         if (!StartServiceCtrlDispatcher(DispatchTable)) {
740                 dbg(SERVICE_NAME
741                 ": StartServiceCtrlDispatcher (%d)\n",
742                 GetLastError());
743         }
744         return 0;
745 }