1 /*
2 * %CopyrightBegin%
3 *
4 * Copyright Ericsson AB 1998-2016. All Rights Reserved.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * %CopyrightEnd%
19 */
20 #include <windows.h>
21 #include <winsvc.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include "erlsrv_global.h"
26 #include "erlsrv_registry.h"
27 #include "erlsrv_util.h"
28 #include "erlsrv_service.h"
29
30 static HANDLE eventStop;
31
32 static HANDLE eventKillErlang;
33
34 static CRITICAL_SECTION crit;
35
36 static SERVICE_STATUS_HANDLE statusHandle;
37
38 static DWORD currentState;
39
fill_status(SERVICE_STATUS * status)40 static void fill_status(SERVICE_STATUS *status){
41 status->dwServiceType = SERVICE_WIN32_OWN_PROCESS;
42 status->dwCurrentState = 0;
43 status->dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
44 status->dwWin32ExitCode = NO_ERROR;
45 status->dwServiceSpecificExitCode = 0;
46 status->dwCheckPoint = 0;
47 status->dwWaitHint = 0;
48 }
49
set_start_pending(int waithint,int checkpoint)50 static BOOL set_start_pending(int waithint, int checkpoint){
51 SERVICE_STATUS stat;
52 fill_status(&stat);
53 EnterCriticalSection(&crit);
54 currentState = stat.dwCurrentState = SERVICE_START_PENDING;
55 LeaveCriticalSection(&crit);
56 stat.dwControlsAccepted = 0;
57 stat.dwCheckPoint = checkpoint;
58 stat.dwWaitHint = waithint;
59 return SetServiceStatus(statusHandle, &stat);
60 }
61
set_stop_pending(int waithint,int checkpoint)62 static BOOL set_stop_pending(int waithint, int checkpoint){
63 SERVICE_STATUS stat;
64 fill_status(&stat);
65 EnterCriticalSection(&crit);
66 currentState = stat.dwCurrentState = SERVICE_STOP_PENDING;
67 LeaveCriticalSection(&crit);
68 stat.dwControlsAccepted = 0;
69 stat.dwCheckPoint = checkpoint;
70 stat.dwWaitHint = waithint;
71 return SetServiceStatus(statusHandle, &stat);
72 }
73
set_running()74 static BOOL set_running(){
75 SERVICE_STATUS stat;
76 fill_status(&stat);
77 EnterCriticalSection(&crit);
78 currentState = stat.dwCurrentState = SERVICE_RUNNING;
79 LeaveCriticalSection(&crit);
80 return SetServiceStatus(statusHandle, &stat);
81 }
82
set_stopped(int error)83 static BOOL set_stopped(int error){
84 SERVICE_STATUS stat;
85 fill_status(&stat);
86 EnterCriticalSection(&crit);
87 currentState = stat.dwCurrentState = SERVICE_STOPPED;
88 LeaveCriticalSection(&crit);
89 stat.dwWin32ExitCode = error;
90 return SetServiceStatus(statusHandle, &stat);
91 }
92
reset_current()93 static BOOL reset_current(){
94 SERVICE_STATUS stat;
95 fill_status(&stat);
96 EnterCriticalSection(&crit);
97 stat.dwCurrentState = currentState;
98 LeaveCriticalSection(&crit);
99 return SetServiceStatus(statusHandle, &stat);
100 }
101
handler(DWORD control)102 static VOID WINAPI handler(DWORD control){
103 wchar_t buffer[1024];
104 swprintf(buffer,1024,L"handler called with control = %d.",(int) control);
105 log_debug(buffer);
106 switch(control){
107 case SERVICE_CONTROL_STOP:
108 case SERVICE_CONTROL_SHUTDOWN:
109 set_stop_pending(30000,1);
110 SetEvent(eventStop);
111 return;
112 default:
113 reset_current();
114 break;
115 }
116 return;
117 }
118
119 typedef struct _server_info {
120 RegEntry *keys;
121 PROCESS_INFORMATION info;
122 HANDLE erl_stdin;
123 wchar_t *event_name;
124 } ServerInfo;
125
126
127 typedef struct {
128 BOOL initialized;
129 TOKEN_DEFAULT_DACL *defdacl;
130 PACL newacl;
131 PSID adminsid;
132 } SaveAclStruct;
133
134
reset_acl(SaveAclStruct * save_acl)135 static BOOL reset_acl(SaveAclStruct *save_acl){
136 HANDLE tokenh;
137
138 if(!save_acl->initialized)
139 return FALSE;
140 if(!OpenProcessToken(GetCurrentProcess(),
141 TOKEN_READ|TOKEN_WRITE,&tokenh)){
142 log_warning(L"Failed to open access token.");
143 return FALSE;
144 }
145 save_acl->initialized = FALSE;
146 if(!SetTokenInformation(tokenh,
147 TokenDefaultDacl,
148 save_acl->defdacl,
149 sizeof(TOKEN_DEFAULT_DACL))){
150 log_warning(L"Failed to get default ACL from token.");
151 CloseHandle(tokenh);
152 LocalFree(save_acl->defdacl);
153 LocalFree(save_acl->newacl);
154 FreeSid(save_acl->adminsid);
155 return FALSE;
156 }
157 CloseHandle(tokenh);
158 LocalFree(save_acl->defdacl);
159 LocalFree(save_acl->newacl);
160 FreeSid(save_acl->adminsid);
161 return TRUE;
162 }
163
164
new_acl(SaveAclStruct * save_acl)165 static BOOL new_acl(SaveAclStruct *save_acl){
166 HANDLE tokenh;
167 TOKEN_DEFAULT_DACL newdacl;
168 DWORD required;
169 PACL oldacl;
170 PACL newacl;
171 int i;
172 ACL_SIZE_INFORMATION si;
173 size_t newsize;
174 PSID extra_sid;
175 SID_IDENTIFIER_AUTHORITY nt_auth = SECURITY_NT_AUTHORITY;
176 TOKEN_DEFAULT_DACL dummy;
177
178 save_acl->initialized = FALSE;
179 if(!OpenProcessToken(GetCurrentProcess(),
180 TOKEN_READ|TOKEN_WRITE,&tokenh)){
181 log_warning(L"Failed to open access token.");
182 return FALSE;
183 }
184 save_acl->defdacl = &dummy;
185 required = sizeof(TOKEN_DEFAULT_DACL);
186 GetTokenInformation(tokenh,
187 TokenDefaultDacl,
188 &(save_acl->defdacl),
189 sizeof(TOKEN_DEFAULT_DACL),
190 &required);
191 if(required == 0){
192 log_warning(L"Failed to get any ACL info from token.");
193 CloseHandle(tokenh);
194 return FALSE;
195 }
196 save_acl->defdacl = LocalAlloc(LPTR,required);
197 if(!GetTokenInformation(tokenh,
198 TokenDefaultDacl,
199 save_acl->defdacl,
200 required,
201 &required)){
202 #ifdef HARDDEBUG
203 {
204 wchar_t *mes;
205 FormatMessage(
206 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
207 NULL,
208 GetLastError(),
209 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
210 (LPTSTR) &mes,
211 0,
212 NULL );
213 log_info(mes);
214 LocalFree(mes);
215 }
216 #endif
217 log_warning(L"Failed to get default ACL from token.");
218 CloseHandle(tokenh);
219 return FALSE;
220 }
221
222 oldacl = save_acl->defdacl->DefaultDacl;
223 if(!GetAclInformation(oldacl, &si, sizeof(si),
224 AclSizeInformation)){
225 log_warning(L"Failed to get size information for ACL");
226 CloseHandle(tokenh);
227 return FALSE;
228 }
229
230 if(!AllocateAndInitializeSid(&nt_auth,
231 2,
232 SECURITY_BUILTIN_DOMAIN_RID,
233 DOMAIN_ALIAS_RID_ADMINS,
234 0,
235 0,
236 0,
237 0,
238 0,
239 0,
240 &extra_sid)){
241 log_warning(L"Failed to initialize administrator SID.");
242 CloseHandle(tokenh);
243 return FALSE;
244 }
245
246 newsize = si.AclBytesInUse + sizeof(ACL) +
247 sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(extra_sid);
248
249 newacl = LocalAlloc(LPTR,newsize);
250
251 if(!InitializeAcl(newacl, newsize, ACL_REVISION)){
252 log_warning(L"Failed to initialize new ACL.");
253 LocalFree(newacl);
254 FreeSid(extra_sid);
255 CloseHandle(tokenh);
256 return FALSE;
257 }
258
259 for(i=0;i<((int)si.AceCount);++i){
260 ACE_HEADER *ace_header;
261 if (!GetAce (oldacl, i, &ace_header)){
262 log_warning(L"Failed to get ACE from old ACL.");
263 LocalFree(newacl);
264 FreeSid(extra_sid);
265 CloseHandle(tokenh);
266 return FALSE;
267 }
268 if(!AddAce(newacl,ACL_REVISION,0xffffffff,ace_header,
269 ace_header->AceSize)){
270 log_warning(L"Failed to set ACE in new ACL.");
271 LocalFree(newacl);
272 FreeSid(extra_sid);
273 CloseHandle(tokenh);
274 return FALSE;
275 }
276 }
277 if(!AddAccessAllowedAce(newacl,
278 ACL_REVISION2,
279 PROCESS_ALL_ACCESS,
280 extra_sid)){
281 log_warning(L"Failed to add system ACE to new ACL.");
282 LocalFree(newacl);
283 FreeSid(extra_sid);
284 return FALSE;
285 }
286
287 newdacl.DefaultDacl = newacl;
288 if(!SetTokenInformation(tokenh,
289 TokenDefaultDacl,
290 &newdacl,
291 sizeof(newdacl))){
292 log_warning(L"Failed to set token information");
293 LocalFree(newacl);
294 FreeSid(extra_sid);
295 CloseHandle(tokenh);
296 return FALSE;
297 }
298 save_acl->initialized = TRUE;
299 save_acl->newacl = newacl;
300 save_acl->adminsid = extra_sid;
301 CloseHandle(tokenh);
302
303 return TRUE;
304 }
305
find_arg(wchar_t ** arg,wchar_t * str)306 static wchar_t **find_arg(wchar_t **arg, wchar_t *str){
307 wchar_t *tmp;
308 int len;
309
310 str = wcsdup(str);
311 if((tmp = wcschr(str,L'=')) == NULL)
312 goto fail;
313 tmp++;
314 *tmp = L'\0';
315 len = tmp - str;
316 while(*arg != NULL){
317 if(!_wcsnicmp(*arg,str,len)){
318 free(str);
319 return arg;
320 }
321 ++arg;
322 }
323 fail:
324 free(str);
325 return NULL;
326 }
327
merge_environment(wchar_t * current,wchar_t * add)328 static wchar_t **merge_environment(wchar_t *current, wchar_t *add){
329 wchar_t **c_arg = env_to_arg(envdup(current));
330 wchar_t **a_arg = env_to_arg(envdup(add));
331 wchar_t **new;
332 wchar_t **tmp;
333 int i,j;
334
335 for(i=0;c_arg[i] != NULL;++i)
336 ;
337 for(j=0;a_arg[j] != NULL;++j)
338 ;
339
340 new = malloc(sizeof(wchar_t *)*(i + j + 3));
341
342 for(i = 0; c_arg[i] != NULL; ++i)
343 new[i] = wcsdup(c_arg[i]);
344
345 new[i] = NULL;
346
347 for(j = 0; a_arg[j] != NULL; ++j){
348 if((tmp = find_arg(new,a_arg[j])) != NULL){
349 free(*tmp);
350 *tmp = wcsdup(a_arg[j]);
351 } else {
352 new[i++] = wcsdup(a_arg[j]);
353 new[i] = NULL;
354 }
355 }
356 free(arg_to_env(c_arg));
357 free(arg_to_env(a_arg));
358 return new;
359 }
360
361
get_next_debug_file(wchar_t * prefix)362 static wchar_t *get_next_debug_file(wchar_t *prefix){
363 wchar_t *buffer = malloc((wcslen(prefix)+12)*sizeof(wchar_t));
364 int i;
365 for(i=1;i<100;++i){
366 swprintf(buffer,wcslen(prefix)+12,L"%s.%d",prefix,i);
367 if(GetFileAttributesW(buffer) == 0xFFFFFFFF)
368 return buffer;
369 }
370 return NULL;
371 }
372
373
374
start_a_service(ServerInfo * srvi)375 static BOOL start_a_service(ServerInfo *srvi){
376 STARTUPINFOW start;
377 wchar_t namebuff[MAX_PATH];
378 wchar_t *execbuff;
379 wchar_t *errbuff;
380 HANDLE write_pipe = NULL, read_pipe = NULL;
381 SECURITY_ATTRIBUTES pipe_security;
382 SECURITY_ATTRIBUTES attr;
383 HANDLE nul;
384 SaveAclStruct save_acl;
385 wchar_t *my_environ;
386 BOOL console_allocated = FALSE;
387 int bufflen=0;
388
389 if(!(*(srvi->keys[Env].data.string))){
390 my_environ = NULL;
391 } else {
392 wchar_t *tmp;
393 wchar_t **merged = merge_environment((tmp = GetEnvironmentStringsW()),
394 srvi->keys[Env].data.string);
395 FreeEnvironmentStringsW(tmp);
396 my_environ = arg_to_env(merged);
397 }
398
399 if(!*(srvi->keys[Machine].data.string) ||
400 (!*(srvi->keys[SName].data.string) &&
401 !*(srvi->keys[Name].data.string))){
402 log_error(L"Not enough parameters for erlang service.");
403 if(my_environ)
404 free(my_environ);
405 return FALSE;
406 }
407
408 if(*(srvi->keys[SName].data.string))
409 swprintf(namebuff,MAX_PATH,L"-nohup -sname %s",srvi->keys[SName].data.string);
410 else
411 swprintf(namebuff,MAX_PATH,L"-nohup -name %s",srvi->keys[Name].data.string);
412
413 if(srvi->keys[DebugType].data.value == DEBUG_TYPE_CONSOLE)
414 wcscat(namebuff,L" -keep_window");
415
416 bufflen = MAX_PATH +
417 wcslen(srvi->keys[Machine].data.string) +
418 wcslen(srvi->event_name) +
419 wcslen(namebuff) +
420 wcslen(srvi->keys[Args].data.string);
421
422 execbuff = malloc(bufflen * sizeof(wchar_t));
423 errbuff = malloc((MAX_PATH + bufflen) * sizeof(wchar_t));
424
425 if (srvi->event_name != NULL) {
426 swprintf(execbuff,bufflen,L"\"%s\" -service_event %s %s %s",
427 srvi->keys[Machine].data.string,
428 srvi->event_name,
429 namebuff,
430 srvi->keys[Args].data.string);
431 } else {
432 swprintf(execbuff,bufflen,L"\"%s\" %s %s",
433 srvi->keys[Machine].data.string,
434 namebuff,
435 srvi->keys[Args].data.string);
436 }
437
438 memset (&start, 0, sizeof (start));
439 start.cb = sizeof (start);
440 start.dwFlags = STARTF_USESHOWWINDOW;
441 start.wShowWindow = SW_HIDE;
442
443 /* Console debugging implies no working StopAction */
444 if(srvi->keys[DebugType].data.value == DEBUG_TYPE_CONSOLE) {
445 COORD coord = {80,999};
446 if(console_allocated = AllocConsole())
447 SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE),coord);
448 else
449 log_warning(L"Unable to allocate debugging console!");
450 } else if(*(srvi->keys[StopAction].data.string) ||
451 srvi->keys[DebugType].data.value != DEBUG_TYPE_NO_DEBUG){
452 pipe_security.nLength = sizeof(pipe_security);
453 pipe_security.lpSecurityDescriptor = NULL;
454 pipe_security.bInheritHandle = TRUE;
455 if(!CreatePipe(&read_pipe,&write_pipe,&pipe_security,0)){
456 log_error(L"Could not create pipe for erlang service.");
457 if(my_environ)
458 free(my_environ);
459 free(execbuff);
460 free(errbuff);
461 return FALSE;
462 }
463 if(srvi->keys[DebugType].data.value != DEBUG_TYPE_NO_DEBUG){
464 wchar_t *filename;
465 if(*(srvi->keys[WorkDir].data.string)){
466 int filenamelen = (wcslen(srvi->keys[WorkDir].data.string) + 1 +
467 wcslen(service_name)+wcslen(L".debug")+1);
468 filename = malloc(filenamelen*sizeof(wchar_t));
469 swprintf(filename,filenamelen,L"%s\\%s.debug",
470 srvi->keys[WorkDir].data.string,
471 service_name);
472 } else {
473 int filenamelen = wcslen(service_name)+wcslen(L".debug")+1;
474 filename = malloc(filenamelen*sizeof(wchar_t));
475 swprintf(filename,filenamelen,L"%s.debug",service_name);
476 }
477 log_debug(filename);
478
479 if(srvi->keys[DebugType].data.value == DEBUG_TYPE_NEW){
480 wchar_t *tmpfn = get_next_debug_file(filename);
481 if(tmpfn){
482 free(filename);
483 filename = tmpfn;
484 } else {
485 log_warning(L"Number of debug files exceeds system defined "
486 L"limit, reverting to DebugType: reuse. ");
487 }
488 }
489
490
491 nul = CreateFileW(filename,
492 GENERIC_READ | GENERIC_WRITE,
493 FILE_SHARE_READ | FILE_SHARE_WRITE,
494 &pipe_security,
495 CREATE_ALWAYS,
496 FILE_ATTRIBUTE_NORMAL,
497 NULL);
498 free(filename);
499 } else { /* Not debugging */
500 nul = CreateFile("NUL",
501 GENERIC_READ | GENERIC_WRITE,
502 FILE_SHARE_READ | FILE_SHARE_WRITE,
503 &pipe_security,
504 OPEN_EXISTING,
505 FILE_ATTRIBUTE_NORMAL,
506 NULL);
507 }
508 if(nul == NULL){
509 log_error((srvi->keys[DebugType].data.value != DEBUG_TYPE_NO_DEBUG)
510 ? L"Could not create debug file. "
511 L"(Working directory not valid?)"
512 : L"Cold not open NUL!");
513 start.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
514 start.hStdError = GetStdHandle(STD_ERROR_HANDLE);
515 }
516 start.hStdOutput = nul;
517 start.hStdError = nul;
518 start.hStdInput = read_pipe;
519 start.dwFlags |= STARTF_USESTDHANDLES;
520 }
521
522 attr.nLength = sizeof(attr);
523 attr.lpSecurityDescriptor = NULL;
524 attr.bInheritHandle = TRUE;
525
526 new_acl(&save_acl);
527
528 {
529 BOOL bIsProcessInJob;
530 if (!IsProcessInJob(GetCurrentProcess(), NULL, &bIsProcessInJob)) {
531 log_error(L"IsProcessInJob failed");
532 return FALSE;
533 }
534 if (!bIsProcessInJob) {
535 HANDLE hJob = CreateJobObject(NULL, NULL);
536 JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = { 0 };
537 /*
538 * Causes all processes associated with the job to terminate when the
539 * last handle to the job is closed.
540 */
541 jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
542 SetInformationJobObject(hJob, JobObjectExtendedLimitInformation, &jeli, sizeof(jeli));
543 if (AssignProcessToJobObject(hJob, GetCurrentProcess()) == FALSE) {
544 log_error(L"Could not AssignProcessToJobObject");
545 return FALSE;
546 }
547 }
548 }
549
550 if(!CreateProcessW(NULL,
551 execbuff,
552 &attr,
553 NULL,
554 (read_pipe != NULL),
555 CREATE_UNICODE_ENVIRONMENT |
556 CREATE_DEFAULT_ERROR_MODE |
557 (srvi->keys[Priority].data.value),
558 my_environ,
559 (*(srvi->keys[WorkDir].data.string)) ?
560 srvi->keys[WorkDir].data.string : NULL,
561 &start,
562 &(srvi->info))){
563 swprintf(errbuff,bufflen+MAX_PATH,L"Could not start erlang service \"%s\""
564 L"with commandline [%s].",
565 service_name,
566 execbuff
567 );
568 log_error(errbuff);
569 if(read_pipe != NULL){
570 CloseHandle(read_pipe);
571 CloseHandle(write_pipe);
572 if(nul != NULL)
573 CloseHandle(nul);
574 }
575 if(console_allocated)
576 FreeConsole();
577 reset_acl(&save_acl);
578 if(my_environ)
579 free(my_environ);
580 free(execbuff);
581 free(errbuff);
582 return FALSE;
583 }
584 if(console_allocated)
585 FreeConsole();
586 #ifdef HARDDEBUG
587 swprintf(errbuff,bufflen+MAX_PATH,
588 L"Started %s with the following commandline: %s",
589 service_name,execbuff);
590 log_debug(errbuff);
591 #endif
592 if(read_pipe != NULL){
593 CloseHandle(read_pipe);
594 if(nul != NULL)
595 CloseHandle(nul);
596 srvi->erl_stdin = write_pipe;
597 }
598
599 reset_acl(&save_acl);
600 if(my_environ)
601 free(my_environ);
602 free(execbuff);
603 free(errbuff);
604 return TRUE;
605 }
606
create_erlang_event(wchar_t * event_name)607 static HANDLE create_erlang_event(wchar_t *event_name)
608 {
609 HANDLE e;
610 if ((e = OpenEventW(EVENT_ALL_ACCESS,FALSE,event_name)) == NULL) {
611 if ((e = CreateEventW(NULL, TRUE, FALSE, event_name)) == NULL) {
612 log_warning(L"Could not create or access erlang termination event");
613 }
614 } else {
615 if (!ResetEvent(e)) {
616 log_warning(L"Could not reset erlang termination event.");
617 }
618 }
619 return e;
620 }
621
stop_erlang(ServerInfo * srvi,int waithint,int * checkpoint)622 static BOOL stop_erlang(ServerInfo *srvi, int waithint,
623 int *checkpoint){
624 DWORD written = 0;
625 wchar_t *wc_action = srvi->keys[StopAction].data.string;
626 DWORD towrite = wcslen(wc_action);
627 char *toerl;
628 DWORD exitcode;
629 int i;
630 int kill;
631
632 if(towrite > 2 && srvi->erl_stdin != NULL){
633 towrite = WideCharToMultiByte(CP_UTF8, 0, wc_action, -1, NULL, 0, NULL, NULL);
634 toerl = malloc((1+towrite)*sizeof(char));
635 WideCharToMultiByte(CP_UTF8, 0, wc_action, -1, toerl, towrite, NULL, NULL);
636 strcat(toerl, "\n");
637 WriteFile(srvi->erl_stdin, toerl, towrite, &written,0);
638 free(toerl);
639 /* Give it 45 seconds to terminate */
640 for(i=0;i<45;++i){
641 if(WaitForSingleObject(srvi->info.hProcess, 1000) ==
642 WAIT_OBJECT_0){
643 GetExitCodeProcess(srvi->info.hProcess,&exitcode);
644 CloseHandle(srvi->info.hProcess);
645 CloseHandle(srvi->info.hThread);
646 return TRUE;
647 }
648 ++(*checkpoint);
649 set_stop_pending(waithint,*checkpoint);
650 }
651 log_warning(L"StopAction did not terminate erlang. Trying forced kill.");
652 }
653 log_debug(L"Terminating erlang...");
654 kill = 1;
655 if(eventKillErlang != NULL && SetEvent(eventKillErlang) != 0){
656 for(i=0;i<10;++i){
657 if(WaitForSingleObject(srvi->info.hProcess, 1000) == WAIT_OBJECT_0){
658 kill = 0;
659 break;
660 }
661 ++(*checkpoint);
662 set_stop_pending(waithint,*checkpoint);
663 }
664 } else {
665 #ifdef HARDDEBUG
666 {
667 wchar_t *mes;
668 FormatMessageW(
669 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
670 NULL,
671 GetLastError(),
672 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
673 (LPWSTR) &mes,
674 0,
675 NULL );
676 log_info(mes);
677 LocalFree(mes);
678 }
679 #endif
680 log_debug(L"Could not send control event to Erlang process");
681 }
682 if(kill){
683 log_warning(L"Using TerminateProcess to kill erlang.");
684 if(!TerminateProcess(srvi->info.hProcess,NO_ERROR))
685 log_error(L"TerminateProcess failed");
686 }
687 GetExitCodeProcess(srvi->info.hProcess,&exitcode);
688 CloseHandle(srvi->info.hProcess);
689 CloseHandle(srvi->info.hThread);
690 if (eventKillErlang != NULL) {
691 ResetEvent(eventKillErlang);
692 }
693 return TRUE;
694 }
695
enable_privilege(void)696 static BOOL enable_privilege(void) {
697 HANDLE ProcessHandle;
698 DWORD DesiredAccess = TOKEN_ADJUST_PRIVILEGES;
699 HANDLE TokenHandle;
700 TOKEN_PRIVILEGES Tpriv;
701 LUID luid;
702 ProcessHandle = GetCurrentProcess();
703 OpenProcessToken(ProcessHandle, DesiredAccess, &TokenHandle);
704 LookupPrivilegeValue(0,SE_SHUTDOWN_NAME,&luid);
705 Tpriv.PrivilegeCount = 1;
706 Tpriv.Privileges[0].Luid = luid;
707 Tpriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
708 return AdjustTokenPrivileges(TokenHandle,FALSE,&Tpriv,0,0,0);
709 }
710
pull_service_name(void)711 static BOOL pull_service_name(void){
712 SC_HANDLE scm;
713 DWORD sz = 1024;
714 static wchar_t service_name_buff[1024];
715 if((scm = OpenSCManager(NULL,
716 NULL,
717 GENERIC_READ))
718 == NULL){
719 return FALSE;
720 }
721 if(!GetServiceDisplayNameW(scm,real_service_name,service_name_buff,&sz))
722 return FALSE;
723 CloseServiceHandle(scm);
724 service_name = service_name_buff;
725 return TRUE;
726 }
727
728
service_main_loop(DWORD argc,wchar_t ** argv)729 static VOID WINAPI service_main_loop(DWORD argc, wchar_t **argv){
730 int waithint = 30000;
731 int checkpoint = 1;
732 RegEntry *keys;
733 RegEntry *save_keys;
734 ServerInfo srvi;
735 HANDLE harr[2];
736 FILETIME creationt,exitt,kernelt,usert;
737 LONGLONG creationl,exitl,diffl;
738 wchar_t event_name[MAX_PATH] = L"ErlSrv_";
739 wchar_t executable_name[MAX_PATH];
740 #ifdef DEBUG
741 wchar_t errorbuff[2048]; /* FIXME... */
742 #endif
743 int success_wait = NO_SUCCESS_WAIT;
744
745 real_service_name = argv[0];
746 if(!pull_service_name()){
747 log_error(L"Could not get Display name of erlang service.");
748 set_stopped(ERROR_CANTREAD);
749 return;
750 }
751
752 SetEnvironmentVariableW(SERVICE_ENV, service_name);
753
754 wcsncat(event_name, service_name, MAX_PATH - wcslen(event_name));
755 event_name[MAX_PATH - 1] = L'\0';
756
757 if(!GetModuleFileNameW(NULL, executable_name, MAX_PATH)){
758 log_error(L"Unable to retrieve module file name, " EXECUTABLE_ENV
759 L" will not be set.");
760 } else {
761 wchar_t quoted_exe_name[MAX_PATH+4];
762 swprintf(quoted_exe_name, MAX_PATH+4, L"\"%s\"", executable_name);
763 SetEnvironmentVariableW(EXECUTABLE_ENV, quoted_exe_name);
764 }
765
766 log_debug(L"Here we go, service_main_loop...");
767 currentState = SERVICE_START_PENDING;
768 InitializeCriticalSection(&crit);
769 eventStop = CreateEvent(NULL,FALSE,FALSE,NULL);
770 if ((eventKillErlang = create_erlang_event(event_name)) != NULL) {
771 srvi.event_name = event_name;
772 } else {
773 srvi.event_name = NULL;
774 }
775 statusHandle = RegisterServiceCtrlHandlerW(real_service_name, &handler);
776 if(!statusHandle)
777 return;
778 set_start_pending(waithint,checkpoint);
779 keys = get_keys(service_name);
780 if(!keys){
781 log_error(L"Could not get registry keys for erlang service.");
782 set_stopped(ERROR_CANTREAD);
783 return;
784 }
785 srvi.keys = keys;
786 srvi.erl_stdin = NULL;
787
788 ++checkpoint;
789 if(!start_a_service(&srvi)){
790 log_error(L"Could not start erlang machine");
791 set_stopped(ERROR_PROCESS_ABORTED);
792 if (eventKillErlang != NULL) {
793 CloseHandle(eventKillErlang);
794 }
795 free_keys(keys);
796 return;
797 }
798 set_start_pending(waithint,checkpoint);
799 set_running();
800 success_wait = INITIAL_SUCCESS_WAIT;
801 harr[0] = srvi.info.hProcess;
802 harr[1] = eventStop;
803 for(;;){
804 DWORD ret;
805 ret = WaitForMultipleObjects((DWORD) 2,
806 harr,
807 FALSE,
808 (success_wait == NO_SUCCESS_WAIT) ?
809 INFINITE :
810 SUCCESS_WAIT_TIME);
811 if(ret == WAIT_TIMEOUT){
812 /* Just do the "success reporting" and continue */
813 if(success_wait == INITIAL_SUCCESS_WAIT){
814 log_info(L"Erlang service started successfully.");
815 } else {
816 log_warning(L"Erlang service restarted");
817 }
818 success_wait = NO_SUCCESS_WAIT;
819 continue;
820 }
821 if(ret == WAIT_FAILED || (int)(ret-WAIT_OBJECT_0) >= 2){
822 set_stopped(WAIT_FAILED);
823 log_error(L"Internal error, could not wait for objects.");
824 if (eventKillErlang != NULL) {
825 CloseHandle(eventKillErlang);
826 }
827 free_keys(keys);
828 return;
829 }
830 ret -= WAIT_OBJECT_0;
831 if(((int) ret) == 1){
832 /* Stop service... */
833 checkpoint = 2; /* 1 is taken by the handler */
834 set_stop_pending(waithint,checkpoint);
835 if(stop_erlang(&srvi,waithint,&checkpoint)){
836 log_debug(L"Erlang machine is stopped");
837 CloseHandle(eventStop);
838 if (eventKillErlang != NULL) {
839 CloseHandle(eventKillErlang);
840 }
841 set_stopped(NO_ERROR);
842 if(srvi.erl_stdin)
843 CloseHandle(srvi.erl_stdin);
844 free_keys(keys);
845 return;
846 } else {
847 log_warning(L"Unable to stop erlang service.");
848 set_running();
849 continue;
850 }
851 }
852 /* Reload the registry keys, they may have changed. */
853 save_keys = keys;
854 keys = get_keys(service_name);
855 if(!keys){
856 log_error(L"Could not reload registry keys.");
857 keys = srvi.keys = save_keys;
858 } else {
859 #ifdef HARDDEBUG
860 swprintf(errorbuff,2048,L"Reloaded the registry keys because %s stopped.",
861 service_name);
862 log_debug(errorbuff);
863 #endif /* HARDDEBUG */
864 free_keys(save_keys);
865 srvi.keys = keys;
866 }
867 if(srvi.keys[OnFail].data.value == ON_FAIL_RESTART ||
868 srvi.keys[OnFail].data.value == ON_FAIL_RESTART_ALWAYS){
869 if(!GetProcessTimes(srvi.info.hProcess,&creationt,
870 &exitt,&kernelt,&usert)){
871 DWORD rcode = GetLastError();
872 log_error(L"Could not get process time of terminated process.");
873 CloseHandle(srvi.info.hProcess);
874 CloseHandle(srvi.info.hThread);
875 CloseHandle(eventStop);
876 if(srvi.erl_stdin)
877 CloseHandle(srvi.erl_stdin);
878 set_stopped(rcode);
879 if (eventKillErlang != NULL) {
880 CloseHandle(eventKillErlang);
881 }
882 free_keys(keys);
883 return;
884 }
885 CloseHandle(srvi.info.hProcess);
886 CloseHandle(srvi.info.hThread);
887 if(srvi.erl_stdin)
888 CloseHandle(srvi.erl_stdin);
889 srvi.erl_stdin = NULL;
890 memcpy(&creationl,&creationt,sizeof(FILETIME));
891 memcpy(&exitl,&exitt,sizeof(FILETIME));
892 diffl = exitl - creationl;
893 diffl /= 10000000;
894 #ifdef DEBUG
895 swprintf(errorbuff,2048,L"Process lived for %d seconds", (int) diffl);
896 log_debug(errorbuff);
897 #endif
898
899 if(diffl > CYCLIC_RESTART_LIMIT ||
900 srvi.keys[OnFail].data.value == ON_FAIL_RESTART_ALWAYS){
901 if(!start_a_service(&srvi)){
902 log_error(L"Unable to restart failed erlang service, aborting.");
903 CloseHandle(eventStop);
904 set_stopped(ERROR_PROCESS_ABORTED);
905 if (eventKillErlang != NULL) {
906 CloseHandle(eventKillErlang);
907 }
908 free_keys(keys);
909 return;
910 }
911 log_warning(L"Restarted erlang machine.");
912 if(diffl <= CYCLIC_RESTART_LIMIT)
913 log_warning(L"Possible cyclic restarting of erlang machine.");
914 success_wait = RESTART_SUCCESS_WAIT;
915 harr[0] = srvi.info.hProcess;
916 } else {
917 if(success_wait == INITIAL_SUCCESS_WAIT){
918 log_error(L"Erlang machine stopped instantly "
919 L"(distribution name conflict?). "
920 L"The service is not restarted, ignoring OnFail option.");
921 } else {
922 log_error(L"Erlang machine seems to die "
923 L"continously, not restarted.");
924 }
925 CloseHandle(eventStop);
926 set_stopped(ERROR_PROCESS_ABORTED);
927 if (eventKillErlang != NULL) {
928 CloseHandle(eventKillErlang);
929 }
930 free_keys(keys);
931 return;
932 }
933 } else if(srvi.keys[OnFail].data.value == ON_FAIL_REBOOT){
934 log_error(L"Rebooting because erlang machine stopped.");
935 enable_privilege();
936 if(!InitiateSystemShutdown("",NULL,0,TRUE,TRUE)){
937 log_error(L"Failed to reboot!");
938 #ifdef HARDDEBUG
939 {
940 wchar_t *mes;
941 FormatMessageW(
942 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
943 NULL,
944 GetLastError(),
945 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
946 (LPWSTR) &mes,
947 0,
948 NULL );
949 log_debug(mes);
950 LocalFree(mes);
951 }
952 #endif
953 CloseHandle(srvi.info.hProcess);
954 CloseHandle(eventStop);
955 if(srvi.erl_stdin != NULL)
956 CloseHandle(srvi.erl_stdin);
957 set_stopped(NO_ERROR);
958 if (eventKillErlang != NULL) {
959 CloseHandle(eventKillErlang);
960 }
961 free_keys(keys);
962 return;
963 }
964 } else {
965 DWORD ecode = NO_ERROR;
966 if(success_wait == NO_SUCCESS_WAIT){
967 log_warning(L"Erlang machine voluntarily stopped. "
968 L"The service is not restarted as OnFail "
969 L"is set to ignore.");
970 } else {
971 log_error(L"Erlang machine stopped instantly "
972 L"(distribution name conflict?). "
973 L"The service is not restarted as OnFail is set to ignore.");
974 ecode = ERROR_PROCESS_ABORTED;
975 }
976 CloseHandle(srvi.info.hProcess);
977 CloseHandle(eventStop);
978 if(srvi.erl_stdin != NULL)
979 CloseHandle(srvi.erl_stdin);
980 set_stopped(ecode);
981 if (eventKillErlang != NULL) {
982 CloseHandle(eventKillErlang);
983 }
984 free_keys(keys);
985 return;
986 }
987 }
988 }
989
service_main(int argc,wchar_t ** argv)990 int service_main(int argc, wchar_t **argv){
991 wchar_t dummy_name[] = L"";
992 SERVICE_TABLE_ENTRYW serviceTable[] =
993 {
994 { dummy_name,
995 (LPSERVICE_MAIN_FUNCTIONW) service_main_loop},
996 { NULL, NULL }
997 };
998 BOOL success;
999 success = StartServiceCtrlDispatcherW(serviceTable);
1000 if (!success)
1001 log_error(L"Could not initiate service");
1002 log_debug(L"service_main done its job");
1003 return 0;
1004 }
1005
1006