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