aboutsummaryrefslogtreecommitdiffstats
path: root/erts/etc/win32/erlsrv/erlsrv_service.c
diff options
context:
space:
mode:
Diffstat (limited to 'erts/etc/win32/erlsrv/erlsrv_service.c')
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_service.c966
1 files changed, 966 insertions, 0 deletions
diff --git a/erts/etc/win32/erlsrv/erlsrv_service.c b/erts/etc/win32/erlsrv/erlsrv_service.c
new file mode 100644
index 0000000000..a58ee862c5
--- /dev/null
+++ b/erts/etc/win32/erlsrv/erlsrv_service.c
@@ -0,0 +1,966 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+#include <windows.h>
+#include <winsvc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "erlsrv_global.h"
+#include "erlsrv_registry.h"
+#include "erlsrv_util.h"
+#include "erlsrv_service.h"
+
+static HANDLE eventStop;
+
+static HANDLE eventKillErlang;
+
+static CRITICAL_SECTION crit;
+
+static SERVICE_STATUS_HANDLE statusHandle;
+
+static DWORD currentState;
+
+static void fill_status(SERVICE_STATUS *status){
+ status->dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+ status->dwCurrentState = 0;
+ status->dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
+ status->dwWin32ExitCode = NO_ERROR;
+ status->dwServiceSpecificExitCode = 0;
+ status->dwCheckPoint = 0;
+ status->dwWaitHint = 0;
+}
+
+static BOOL set_start_pending(int waithint, int checkpoint){
+ SERVICE_STATUS stat;
+ fill_status(&stat);
+ EnterCriticalSection(&crit);
+ currentState = stat.dwCurrentState = SERVICE_START_PENDING;
+ LeaveCriticalSection(&crit);
+ stat.dwControlsAccepted = 0;
+ stat.dwCheckPoint = checkpoint;
+ stat.dwWaitHint = waithint;
+ return SetServiceStatus(statusHandle, &stat);
+}
+
+static BOOL set_stop_pending(int waithint, int checkpoint){
+ SERVICE_STATUS stat;
+ fill_status(&stat);
+ EnterCriticalSection(&crit);
+ currentState = stat.dwCurrentState = SERVICE_STOP_PENDING;
+ LeaveCriticalSection(&crit);
+ stat.dwControlsAccepted = 0;
+ stat.dwCheckPoint = checkpoint;
+ stat.dwWaitHint = waithint;
+ return SetServiceStatus(statusHandle, &stat);
+}
+
+static BOOL set_running(){
+ SERVICE_STATUS stat;
+ fill_status(&stat);
+ EnterCriticalSection(&crit);
+ currentState = stat.dwCurrentState = SERVICE_RUNNING;
+ LeaveCriticalSection(&crit);
+ return SetServiceStatus(statusHandle, &stat);
+}
+
+static BOOL set_stopped(int error){
+ SERVICE_STATUS stat;
+ fill_status(&stat);
+ EnterCriticalSection(&crit);
+ currentState = stat.dwCurrentState = SERVICE_STOPPED;
+ LeaveCriticalSection(&crit);
+ stat.dwWin32ExitCode = error;
+ return SetServiceStatus(statusHandle, &stat);
+}
+
+static BOOL reset_current(){
+ SERVICE_STATUS stat;
+ fill_status(&stat);
+ EnterCriticalSection(&crit);
+ stat.dwCurrentState = currentState;
+ LeaveCriticalSection(&crit);
+ return SetServiceStatus(statusHandle, &stat);
+}
+
+static VOID WINAPI handler(DWORD control){
+ char buffer[1024];
+ sprintf(buffer,"handler called with control = %d.",(int) control);
+ log_debug(buffer);
+ switch(control){
+ case SERVICE_CONTROL_STOP:
+ set_stop_pending(30000,1);
+ SetEvent(eventStop);
+ return;
+ case SERVICE_CONTROL_SHUTDOWN:
+ return;
+ default:
+ reset_current();
+ break;
+ }
+ return;
+}
+
+typedef struct _server_info {
+ RegEntry *keys;
+ PROCESS_INFORMATION info;
+ HANDLE erl_stdin;
+ char *event_name;
+} ServerInfo;
+
+
+typedef struct {
+ BOOL initialized;
+ TOKEN_DEFAULT_DACL *defdacl;
+ PACL newacl;
+ PSID adminsid;
+} SaveAclStruct;
+
+
+static BOOL reset_acl(SaveAclStruct *save_acl){
+ HANDLE tokenh;
+
+ if(!save_acl->initialized)
+ return FALSE;
+ if(!OpenProcessToken(GetCurrentProcess(),
+ TOKEN_READ|TOKEN_WRITE,&tokenh)){
+ log_warning("Failed to open access token.");
+ return FALSE;
+ }
+ save_acl->initialized = FALSE;
+ if(!SetTokenInformation(tokenh,
+ TokenDefaultDacl,
+ save_acl->defdacl,
+ sizeof(TOKEN_DEFAULT_DACL))){
+ log_warning("Failed to get default ACL from token.");
+ CloseHandle(tokenh);
+ LocalFree(save_acl->defdacl);
+ LocalFree(save_acl->newacl);
+ FreeSid(save_acl->adminsid);
+ return FALSE;
+ }
+ CloseHandle(tokenh);
+ LocalFree(save_acl->defdacl);
+ LocalFree(save_acl->newacl);
+ FreeSid(save_acl->adminsid);
+ return TRUE;
+}
+
+
+static BOOL new_acl(SaveAclStruct *save_acl){
+ HANDLE tokenh;
+ TOKEN_DEFAULT_DACL newdacl;
+ DWORD required;
+ PACL oldacl;
+ PACL newacl;
+ int i;
+ ACL_SIZE_INFORMATION si;
+ size_t newsize;
+ PSID extra_sid;
+ SID_IDENTIFIER_AUTHORITY nt_auth = SECURITY_NT_AUTHORITY;
+ TOKEN_DEFAULT_DACL dummy;
+
+ save_acl->initialized = FALSE;
+ if(!OpenProcessToken(GetCurrentProcess(),
+ TOKEN_READ|TOKEN_WRITE,&tokenh)){
+ log_warning("Failed to open access token.");
+ return FALSE;
+ }
+ save_acl->defdacl = &dummy;
+ required = sizeof(TOKEN_DEFAULT_DACL);
+ GetTokenInformation(tokenh,
+ TokenDefaultDacl,
+ &(save_acl->defdacl),
+ sizeof(TOKEN_DEFAULT_DACL),
+ &required);
+ if(required == 0){
+ log_warning("Failed to get any ACL info from token.");
+ CloseHandle(tokenh);
+ return FALSE;
+ }
+ save_acl->defdacl = LocalAlloc(LPTR,required);
+ if(!GetTokenInformation(tokenh,
+ TokenDefaultDacl,
+ save_acl->defdacl,
+ required,
+ &required)){
+#ifdef HARDDEBUG
+ {
+ char *mes;
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &mes,
+ 0,
+ NULL );
+ log_info(mes);
+ LocalFree(mes);
+ }
+#endif
+ log_warning("Failed to get default ACL from token.");
+ CloseHandle(tokenh);
+ return FALSE;
+ }
+
+ oldacl = save_acl->defdacl->DefaultDacl;
+ if(!GetAclInformation(oldacl, &si, sizeof(si),
+ AclSizeInformation)){
+ log_warning("Failed to get size information for ACL");
+ CloseHandle(tokenh);
+ return FALSE;
+ }
+
+ if(!AllocateAndInitializeSid(&nt_auth,
+ 2,
+ SECURITY_BUILTIN_DOMAIN_RID,
+ DOMAIN_ALIAS_RID_ADMINS,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ &extra_sid)){
+ log_warning("Failed to initialize administrator SID.");
+ CloseHandle(tokenh);
+ return FALSE;
+ }
+
+ newsize = si.AclBytesInUse + sizeof(ACL) +
+ sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(extra_sid);
+
+ newacl = LocalAlloc(LPTR,newsize);
+
+ if(!InitializeAcl(newacl, newsize, ACL_REVISION)){
+ log_warning("Failed to initialize new ACL.");
+ LocalFree(newacl);
+ FreeSid(extra_sid);
+ CloseHandle(tokenh);
+ return FALSE;
+ }
+
+ for(i=0;i<((int)si.AceCount);++i){
+ ACE_HEADER *ace_header;
+ if (!GetAce (oldacl, i, &ace_header)){
+ log_warning("Failed to get ACE from old ACL.");
+ LocalFree(newacl);
+ FreeSid(extra_sid);
+ CloseHandle(tokenh);
+ return FALSE;
+ }
+ if(!AddAce(newacl,ACL_REVISION,0xffffffff,ace_header,
+ ace_header->AceSize)){
+ log_warning("Failed to set ACE in new ACL.");
+ LocalFree(newacl);
+ FreeSid(extra_sid);
+ CloseHandle(tokenh);
+ return FALSE;
+ }
+ }
+ if(!AddAccessAllowedAce(newacl,
+ ACL_REVISION2,
+ PROCESS_ALL_ACCESS,
+ extra_sid)){
+ log_warning("Failed to add system ACE to new ACL.");
+ LocalFree(newacl);
+ FreeSid(extra_sid);
+ return FALSE;
+ }
+
+ newdacl.DefaultDacl = newacl;
+ if(!SetTokenInformation(tokenh,
+ TokenDefaultDacl,
+ &newdacl,
+ sizeof(newdacl))){
+ log_warning("Failed to set token information");
+ LocalFree(newacl);
+ FreeSid(extra_sid);
+ CloseHandle(tokenh);
+ return FALSE;
+ }
+ save_acl->initialized = TRUE;
+ save_acl->newacl = newacl;
+ save_acl->adminsid = extra_sid;
+ CloseHandle(tokenh);
+
+ return TRUE;
+}
+
+static char **find_arg(char **arg, char *str){
+ char *tmp;
+ int len;
+
+ str = strdup(str);
+ if((tmp = strchr(str,'=')) == NULL)
+ goto fail;
+ tmp++;
+ *tmp = '\0';
+ len = tmp - str;
+ while(*arg != NULL){
+ if(!_strnicmp(*arg,str,len)){
+ free(str);
+ return arg;
+ }
+ ++arg;
+ }
+fail:
+ free(str);
+ return NULL;
+}
+
+static char **merge_environment(char *current, char *add){
+ char **c_arg = env_to_arg(envdup(current));
+ char **a_arg = env_to_arg(envdup(add));
+ char **new;
+ char **tmp;
+ int i,j;
+
+ for(i=0;c_arg[i] != NULL;++i)
+ ;
+ for(j=0;a_arg[j] != NULL;++j)
+ ;
+
+ new = malloc(sizeof(char *)*(i + j + 3));
+
+ for(i = 0; c_arg[i] != NULL; ++i)
+ new[i] = strdup(c_arg[i]);
+
+ new[i] = NULL;
+
+ for(j = 0; a_arg[j] != NULL; ++j){
+ if((tmp = find_arg(new,a_arg[j])) != NULL){
+ free(*tmp);
+ *tmp = strdup(a_arg[j]);
+ } else {
+ new[i++] = strdup(a_arg[j]);
+ new[i] = NULL;
+ }
+ }
+ free(arg_to_env(c_arg));
+ free(arg_to_env(a_arg));
+ return new;
+}
+
+
+static char *get_next_debug_file(char *prefix){
+ char *buffer = malloc(strlen(prefix)+12);
+ int i;
+ for(i=1;i<100;++i){
+ sprintf(buffer,"%s.%d",prefix,i);
+ if(GetFileAttributes(buffer) == 0xFFFFFFFF)
+ return buffer;
+ }
+ return NULL;
+}
+
+
+
+static BOOL start_a_service(ServerInfo *srvi){
+ STARTUPINFO start;
+ char execbuff[MAX_PATH*4]; /* FIXME: Can get overflow! */
+ char namebuff[MAX_PATH];
+ char errbuff[MAX_PATH*4]; /* hmmm.... */
+ HANDLE write_pipe = NULL, read_pipe = NULL;
+ SECURITY_ATTRIBUTES pipe_security;
+ SECURITY_ATTRIBUTES attr;
+ HANDLE nul;
+ SaveAclStruct save_acl;
+ char *my_environ;
+ BOOL console_allocated = FALSE;
+
+ if(!(*(srvi->keys[Env].data.bytes))){
+ my_environ = NULL;
+ } else {
+ char *tmp;
+ char **merged = merge_environment((tmp = GetEnvironmentStrings()),
+ srvi->keys[Env].data.bytes);
+ FreeEnvironmentStrings(tmp);
+ my_environ = arg_to_env(merged);
+ }
+
+ if(!*(srvi->keys[Machine].data.bytes) ||
+ (!*(srvi->keys[SName].data.bytes) &&
+ !*(srvi->keys[Name].data.bytes))){
+ log_error("Not enough parameters for erlang service.");
+ if(my_environ)
+ free(my_environ);
+ return FALSE;
+ }
+
+ if(*(srvi->keys[SName].data.bytes))
+ sprintf(namebuff,"-nohup -sname %s",srvi->keys[SName].data.bytes);
+ else
+ sprintf(namebuff,"-nohup -name %s",srvi->keys[Name].data.bytes);
+
+ if(srvi->keys[DebugType].data.value == DEBUG_TYPE_CONSOLE)
+ strcat(namebuff," -keep_window");
+
+ if (srvi->event_name != NULL) {
+ sprintf(execbuff,"\"%s\" -service_event %s %s %s",
+ srvi->keys[Machine].data.bytes,
+ srvi->event_name,
+ namebuff,
+ srvi->keys[Args].data.bytes);
+ } else {
+ sprintf(execbuff,"\"%s\" %s %s",
+ srvi->keys[Machine].data.bytes,
+ namebuff,
+ srvi->keys[Args].data.bytes);
+ }
+
+ memset (&start, 0, sizeof (start));
+ start.cb = sizeof (start);
+ start.dwFlags = STARTF_USESHOWWINDOW;
+ start.wShowWindow = SW_HIDE;
+
+ /* Console debugging implies no working StopAction */
+ if(srvi->keys[DebugType].data.value == DEBUG_TYPE_CONSOLE) {
+ COORD coord = {80,999};
+ if(console_allocated = AllocConsole())
+ SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE),coord);
+ else
+ log_warning("Unable to allocate debugging console!");
+ } else if(*(srvi->keys[StopAction].data.bytes) ||
+ srvi->keys[DebugType].data.value != DEBUG_TYPE_NO_DEBUG){
+ pipe_security.nLength = sizeof(pipe_security);
+ pipe_security.lpSecurityDescriptor = NULL;
+ pipe_security.bInheritHandle = TRUE;
+ if(!CreatePipe(&read_pipe,&write_pipe,&pipe_security,0)){
+ log_error("Could not create pipe for erlang service.");
+ if(my_environ)
+ free(my_environ);
+ return FALSE;
+ }
+ if(srvi->keys[DebugType].data.value != DEBUG_TYPE_NO_DEBUG){
+ char *filename;
+ if(*(srvi->keys[WorkDir].data.bytes)){
+ filename = malloc(strlen(srvi->keys[WorkDir].data.bytes) + 1 +
+ strlen(service_name)+strlen(".debug")+1);
+ sprintf(filename,"%s\\%s.debug",
+ srvi->keys[WorkDir].data.bytes,
+ service_name);
+ } else {
+ filename = malloc(strlen(service_name)+strlen(".debug")+1);
+ sprintf(filename,"%s.debug",service_name);
+ }
+ log_debug(filename);
+
+ if(srvi->keys[DebugType].data.value == DEBUG_TYPE_NEW){
+ char *tmpfn = get_next_debug_file(filename);
+ if(tmpfn){
+ free(filename);
+ filename = tmpfn;
+ } else {
+ log_warning("Number of debug files exceeds system defined "
+ "limit, reverting to DebugType: reuse. ");
+ }
+ }
+
+
+ nul = CreateFile(filename,
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ &pipe_security,
+ CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ free(filename);
+ } else { /* Not debugging */
+ nul = CreateFile("NUL",
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ &pipe_security,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ }
+ if(nul == NULL){
+ log_error((srvi->keys[DebugType].data.value != DEBUG_TYPE_NO_DEBUG)
+ ? "Could not create debug file. "
+ "(Working directory not valid?)"
+ : "Cold not open NUL!");
+ start.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+ start.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+ }
+ start.hStdOutput = nul;
+ start.hStdError = nul;
+ start.hStdInput = read_pipe;
+ start.dwFlags |= STARTF_USESTDHANDLES;
+ }
+
+ attr.nLength = sizeof(attr);
+ attr.lpSecurityDescriptor = NULL;
+ attr.bInheritHandle = TRUE;
+
+ new_acl(&save_acl);
+
+ if(!CreateProcess(NULL,
+ execbuff,
+ &attr,
+ NULL,
+ (read_pipe != NULL),
+ CREATE_DEFAULT_ERROR_MODE |
+ (srvi->keys[Priority].data.value),
+ my_environ,
+ (*(srvi->keys[WorkDir].data.bytes)) ?
+ srvi->keys[WorkDir].data.bytes : NULL,
+ &start,
+ &(srvi->info))){
+ sprintf(errbuff,"Could not start erlang service"
+ "with commandline \"%s\".",
+ service_name,
+ execbuff
+ );
+ log_error(errbuff);
+ if(read_pipe != NULL){
+ CloseHandle(read_pipe);
+ CloseHandle(write_pipe);
+ if(nul != NULL)
+ CloseHandle(nul);
+ }
+ if(console_allocated)
+ FreeConsole();
+ reset_acl(&save_acl);
+ if(my_environ)
+ free(my_environ);
+ return FALSE;
+ }
+ if(console_allocated)
+ FreeConsole();
+#ifdef HARDDEBUG
+ sprintf(errbuff,
+ "Started %s with the following commandline: "
+ "%s",service_name,execbuff);
+ log_debug(errbuff);
+#endif
+ if(read_pipe != NULL){
+ CloseHandle(read_pipe);
+ if(nul != NULL)
+ CloseHandle(nul);
+ srvi->erl_stdin = write_pipe;
+ }
+
+ reset_acl(&save_acl);
+ if(my_environ)
+ free(my_environ);
+ return TRUE;
+}
+
+static HANDLE create_erlang_event(char *event_name)
+{
+ HANDLE e;
+ if ((e = OpenEvent(EVENT_ALL_ACCESS,FALSE,event_name)) == NULL) {
+ if ((e = CreateEvent(NULL, TRUE, FALSE, event_name)) == NULL) {
+ log_warning("Could not create or access erlang termination event");
+ }
+ } else {
+ if (!ResetEvent(e)) {
+ log_warning("Could not reset erlang termination event.");
+ }
+ }
+ return e;
+}
+
+static BOOL stop_erlang(ServerInfo *srvi, int waithint,
+ int *checkpoint){
+ DWORD written = 0;
+ char *action = srvi->keys[StopAction].data.bytes;
+ DWORD towrite = strlen(action)+1;
+ char *toerl;
+ DWORD exitcode;
+ int i;
+ int kill;
+
+ if(towrite > 2 && srvi->erl_stdin != NULL){
+ toerl = malloc(towrite+1);
+ strcpy(toerl,action);
+ strcat(toerl,"\n");
+ WriteFile(srvi->erl_stdin, toerl, towrite, &written,0);
+ free(toerl);
+ /* Give it 45 seconds to terminate */
+ for(i=0;i<45;++i){
+ if(WaitForSingleObject(srvi->info.hProcess, 1000) ==
+ WAIT_OBJECT_0){
+ GetExitCodeProcess(srvi->info.hProcess,&exitcode);
+ CloseHandle(srvi->info.hProcess);
+ CloseHandle(srvi->info.hThread);
+ return TRUE;
+ }
+ ++(*checkpoint);
+ set_stop_pending(waithint,*checkpoint);
+ }
+ log_warning("StopAction did not terminate erlang. Trying forced kill.");
+ }
+ log_debug("Terminating erlang...");
+ kill = 1;
+ if(eventKillErlang != NULL && SetEvent(eventKillErlang) != 0){
+ for(i=0;i<10;++i){
+ if(WaitForSingleObject(srvi->info.hProcess, 1000) == WAIT_OBJECT_0){
+ kill = 0;
+ break;
+ }
+ ++(*checkpoint);
+ set_stop_pending(waithint,*checkpoint);
+ }
+ } else {
+#ifdef HARDDEBUG
+ {
+ char *mes;
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &mes,
+ 0,
+ NULL );
+ log_info(mes);
+ LocalFree(mes);
+ }
+#endif
+ log_debug("Could not send control event to Erlang process");
+ }
+ if(kill){
+ log_warning("Using TerminateProcess to kill erlang.");
+ if(!TerminateProcess(srvi->info.hProcess,NO_ERROR))
+ log_error("TerminateProcess failed");
+ }
+ GetExitCodeProcess(srvi->info.hProcess,&exitcode);
+ CloseHandle(srvi->info.hProcess);
+ CloseHandle(srvi->info.hThread);
+ if (eventKillErlang != NULL) {
+ ResetEvent(eventKillErlang);
+ }
+ return TRUE;
+}
+
+static BOOL enable_privilege(void) {
+ HANDLE ProcessHandle;
+ DWORD DesiredAccess = TOKEN_ADJUST_PRIVILEGES;
+ HANDLE TokenHandle;
+ TOKEN_PRIVILEGES Tpriv;
+ LUID luid;
+ ProcessHandle = GetCurrentProcess();
+ OpenProcessToken(ProcessHandle, DesiredAccess, &TokenHandle);
+ LookupPrivilegeValue(0,SE_SHUTDOWN_NAME,&luid);
+ Tpriv.PrivilegeCount = 1;
+ Tpriv.Privileges[0].Luid = luid;
+ Tpriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ return AdjustTokenPrivileges(TokenHandle,FALSE,&Tpriv,0,0,0);
+}
+
+static BOOL pull_service_name(void){
+ SC_HANDLE scm;
+ DWORD sz = 1024;
+ static char service_name_buff[1024];
+ if((scm = OpenSCManager(NULL,
+ NULL,
+ GENERIC_READ))
+ == NULL){
+ return FALSE;
+ }
+ if(!GetServiceDisplayName(scm,real_service_name,service_name_buff,&sz))
+ return FALSE;
+ CloseServiceHandle(scm);
+ service_name = service_name_buff;
+ return TRUE;
+}
+
+
+static VOID WINAPI service_main_loop(DWORD argc, char **argv){
+ int waithint = 30000;
+ int checkpoint = 1;
+ RegEntry *keys;
+ RegEntry *save_keys;
+ ServerInfo srvi;
+ HANDLE harr[2];
+ FILETIME creationt,exitt,kernelt,usert;
+ LONGLONG creationl,exitl,diffl;
+ char event_name[MAX_PATH] = "ErlSrv_";
+ char executable_name[MAX_PATH];
+#ifdef DEBUG
+ char errorbuff[2048]; /* FIXME... */
+#endif
+ int success_wait = NO_SUCCESS_WAIT;
+
+ real_service_name = argv[0];
+ if(!pull_service_name()){
+ log_error("Could not get Display name of erlang service.");
+ set_stopped(ERROR_CANTREAD);
+ return;
+ }
+
+ SetEnvironmentVariable((LPCTSTR) SERVICE_ENV, (LPCTSTR) service_name);
+
+ strncat(event_name, service_name, MAX_PATH - strlen(event_name));
+ event_name[MAX_PATH - 1] = '\0';
+
+ if(!GetModuleFileName(NULL, executable_name, MAX_PATH)){
+ log_error("Unable to retrieve module file name, " EXECUTABLE_ENV
+ " will not be set.");
+ } else {
+ char quoted_exe_name[MAX_PATH+4];
+ sprintf(quoted_exe_name, "\"%s\"", executable_name);
+ SetEnvironmentVariable((LPCTSTR) EXECUTABLE_ENV,
+ (LPCTSTR) quoted_exe_name);
+ }
+
+ log_debug("Here we go, service_main_loop...");
+ currentState = SERVICE_START_PENDING;
+ InitializeCriticalSection(&crit);
+ eventStop = CreateEvent(NULL,FALSE,FALSE,NULL);
+ if ((eventKillErlang = create_erlang_event(event_name)) != NULL) {
+ srvi.event_name = event_name;
+ } else {
+ srvi.event_name = NULL;
+ }
+ statusHandle = RegisterServiceCtrlHandler(real_service_name, &handler);
+ if(!statusHandle)
+ return;
+ set_start_pending(waithint,checkpoint);
+ keys = get_keys(service_name);
+ if(!keys){
+ log_error("Could not get registry keys for erlang service.");
+ set_stopped(ERROR_CANTREAD);
+ return;
+ }
+ srvi.keys = keys;
+ srvi.erl_stdin = NULL;
+
+ ++checkpoint;
+ if(!start_a_service(&srvi)){
+ log_error("Could not start erlang machine");
+ set_stopped(ERROR_PROCESS_ABORTED);
+ if (eventKillErlang != NULL) {
+ CloseHandle(eventKillErlang);
+ }
+ free_keys(keys);
+ return;
+ }
+ set_start_pending(waithint,checkpoint);
+ set_running();
+ success_wait = INITIAL_SUCCESS_WAIT;
+ harr[0] = srvi.info.hProcess;
+ harr[1] = eventStop;
+ for(;;){
+ DWORD ret;
+ ret = WaitForMultipleObjects((DWORD) 2,
+ harr,
+ FALSE,
+ (success_wait == NO_SUCCESS_WAIT) ?
+ INFINITE :
+ SUCCESS_WAIT_TIME);
+ if(ret == WAIT_TIMEOUT){
+ /* Just do the "success reporting" and continue */
+ if(success_wait == INITIAL_SUCCESS_WAIT){
+ log_info("Erlang service started successfully.");
+ } else {
+ log_warning("Erlang service restarted");
+ }
+ success_wait = NO_SUCCESS_WAIT;
+ continue;
+ }
+ if(ret == WAIT_FAILED || (int)(ret-WAIT_OBJECT_0) >= 2){
+ set_stopped(WAIT_FAILED);
+ log_error("Internal error, could not wait for objects.");
+ if (eventKillErlang != NULL) {
+ CloseHandle(eventKillErlang);
+ }
+ free_keys(keys);
+ return;
+ }
+ ret -= WAIT_OBJECT_0;
+ if(((int) ret) == 1){
+ /* Stop service... */
+ checkpoint = 2; /* 1 is taken by the handler */
+ set_stop_pending(waithint,checkpoint);
+ if(stop_erlang(&srvi,waithint,&checkpoint)){
+ log_debug("Erlang machine is stopped");
+ CloseHandle(eventStop);
+ if (eventKillErlang != NULL) {
+ CloseHandle(eventKillErlang);
+ }
+ set_stopped(NO_ERROR);
+ if(srvi.erl_stdin)
+ CloseHandle(srvi.erl_stdin);
+ free_keys(keys);
+ return;
+ } else {
+ log_warning("Unable to stop erlang service.");
+ set_running();
+ continue;
+ }
+ }
+ /* Reload the registry keys, they may have changed. */
+ save_keys = keys;
+ keys = get_keys(service_name);
+ if(!keys){
+ log_error("Could not reload registry keys.");
+ keys = srvi.keys = save_keys;
+ } else {
+#ifdef HARDDEBUG
+ sprintf(errorbuff,"Reloaded the registry keys because %s stopped.",
+ service_name);
+ log_debug(errorbuff);
+#endif /* HARDDEBUG */
+ free_keys(save_keys);
+ srvi.keys = keys;
+ }
+ if(srvi.keys[OnFail].data.value == ON_FAIL_RESTART ||
+ srvi.keys[OnFail].data.value == ON_FAIL_RESTART_ALWAYS){
+ if(!GetProcessTimes(srvi.info.hProcess,&creationt,
+ &exitt,&kernelt,&usert)){
+ DWORD rcode = GetLastError();
+ log_error("Could not get process time of terminated process.");
+ CloseHandle(srvi.info.hProcess);
+ CloseHandle(srvi.info.hThread);
+ CloseHandle(eventStop);
+ if(srvi.erl_stdin)
+ CloseHandle(srvi.erl_stdin);
+ set_stopped(rcode);
+ if (eventKillErlang != NULL) {
+ CloseHandle(eventKillErlang);
+ }
+ free_keys(keys);
+ return;
+ }
+ CloseHandle(srvi.info.hProcess);
+ CloseHandle(srvi.info.hThread);
+ if(srvi.erl_stdin)
+ CloseHandle(srvi.erl_stdin);
+ srvi.erl_stdin = NULL;
+ memcpy(&creationl,&creationt,sizeof(FILETIME));
+ memcpy(&exitl,&exitt,sizeof(FILETIME));
+ diffl = exitl - creationl;
+ diffl /= 10000000;
+#ifdef DEBUG
+ sprintf(errorbuff,"Process lived for %d seconds", (int) diffl);
+ log_debug(errorbuff);
+#endif
+
+ if(diffl > CYCLIC_RESTART_LIMIT ||
+ srvi.keys[OnFail].data.value == ON_FAIL_RESTART_ALWAYS){
+ if(!start_a_service(&srvi)){
+ log_error("Unable to restart failed erlang service, "
+ "aborting.");
+ CloseHandle(eventStop);
+ set_stopped(ERROR_PROCESS_ABORTED);
+ if (eventKillErlang != NULL) {
+ CloseHandle(eventKillErlang);
+ }
+ free_keys(keys);
+ return;
+ }
+ log_warning("Restarted erlang machine.");
+ if(diffl <= CYCLIC_RESTART_LIMIT)
+ log_warning("Possible cyclic restarting of erlang machine.");
+ success_wait = RESTART_SUCCESS_WAIT;
+ harr[0] = srvi.info.hProcess;
+ } else {
+ if(success_wait == INITIAL_SUCCESS_WAIT){
+ log_error("Erlang machine stopped instantly "
+ "(distribution name conflict?). "
+ "The service is not restarted, ignoring OnFail option.");
+ } else {
+ log_error("Erlang machine seems to die "
+ "continously, not restarted.");
+ }
+ CloseHandle(eventStop);
+ set_stopped(ERROR_PROCESS_ABORTED);
+ if (eventKillErlang != NULL) {
+ CloseHandle(eventKillErlang);
+ }
+ free_keys(keys);
+ return;
+ }
+ } else if(srvi.keys[OnFail].data.value == ON_FAIL_REBOOT){
+ log_error("Rebooting because erlang machine stopped.");
+ enable_privilege();
+ if(!InitiateSystemShutdown("",NULL,0,TRUE,TRUE)){
+ log_error("Failed to reboot!");
+#ifdef HARDDEBUG
+ {
+ char *mes;
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &mes,
+ 0,
+ NULL );
+ log_debug(mes);
+ LocalFree(mes);
+ }
+#endif
+ CloseHandle(srvi.info.hProcess);
+ CloseHandle(eventStop);
+ if(srvi.erl_stdin != NULL)
+ CloseHandle(srvi.erl_stdin);
+ set_stopped(NO_ERROR);
+ if (eventKillErlang != NULL) {
+ CloseHandle(eventKillErlang);
+ }
+ free_keys(keys);
+ return;
+ }
+ } else {
+ DWORD ecode = NO_ERROR;
+ if(success_wait == NO_SUCCESS_WAIT){
+ log_warning("Erlang machine volountarily stopped. "
+ "The service is not restarted as OnFail "
+ "is set to ignore.");
+ } else {
+ log_error("Erlang machine stopped instantly "
+ "(distribution name conflict?). "
+ "The service is not restarted as OnFail is set to ignore.");
+ ecode = ERROR_PROCESS_ABORTED;
+ }
+ CloseHandle(srvi.info.hProcess);
+ CloseHandle(eventStop);
+ if(srvi.erl_stdin != NULL)
+ CloseHandle(srvi.erl_stdin);
+ set_stopped(ecode);
+ if (eventKillErlang != NULL) {
+ CloseHandle(eventKillErlang);
+ }
+ free_keys(keys);
+ return;
+ }
+ }
+}
+
+int service_main(int argc, char **argv){
+ char dummy_name[] = "";
+ SERVICE_TABLE_ENTRY serviceTable[] =
+ {
+ { dummy_name,
+ (LPSERVICE_MAIN_FUNCTION) service_main_loop},
+ { NULL, NULL }
+ };
+ BOOL success;
+ success =
+ StartServiceCtrlDispatcher(serviceTable);
+ if (!success)
+ log_error("Could not initiate service");
+ log_debug("service_main done its job");
+ return 0;
+}
+