aboutsummaryrefslogtreecommitdiffstats
path: root/erts/etc/win32/erlsrv
diff options
context:
space:
mode:
Diffstat (limited to 'erts/etc/win32/erlsrv')
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_global.h37
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_interactive.c1163
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_interactive.h24
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_logmess.mc33
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_main.c44
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_registry.c404
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_registry.h76
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_service.c966
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_service.h32
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_util.c154
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_util.h50
11 files changed, 2983 insertions, 0 deletions
diff --git a/erts/etc/win32/erlsrv/erlsrv_global.h b/erts/etc/win32/erlsrv/erlsrv_global.h
new file mode 100644
index 0000000000..d3922dc1e3
--- /dev/null
+++ b/erts/etc/win32/erlsrv/erlsrv_global.h
@@ -0,0 +1,37 @@
+/*
+ * %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%
+ */
+#ifndef _ERLSRV_GLOBAL_H
+#define _ERLSRV_GLOBAL_H
+
+#define APP_NAME "ErlSrv"
+
+#define ERLANG_MACHINE "erl.exe"
+
+#define SERVICE_ENV "ERLSRV_SERVICE_NAME"
+#define EXECUTABLE_ENV "ERLSRV_EXECUTABLE"
+#define DEBUG_ENV "ERLSRV_DEBUG"
+
+#ifdef _DEBUG
+#define HARDDEBUG 1
+#define DEBUG 1
+#else
+#define NDEBUG 1
+#endif
+
+#endif /* _ERLSRV_GLOBAL_H */
diff --git a/erts/etc/win32/erlsrv/erlsrv_interactive.c b/erts/etc/win32/erlsrv/erlsrv_interactive.c
new file mode 100644
index 0000000000..13e029b364
--- /dev/null
+++ b/erts/etc/win32/erlsrv/erlsrv_interactive.c
@@ -0,0 +1,1163 @@
+/*
+ * %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 <assert.h>
+#include "erlsrv_global.h"
+#include "erlsrv_registry.h"
+#include "erlsrv_interactive.h"
+#include "erlsrv_util.h" /* service_name */
+
+#define DBG fprintf(stderr,"argv[0]:%s line %d\n",argv[0],__LINE__)
+
+/* Really HAS to correcpond to the enum in erlsrv_registry.h */
+static char *arg_tab[] = {
+ "stopaction", "st",
+ "onfail", "on",
+ "machine", "m",
+ "env", "e",
+ "workdir", "w",
+ "priority", "p",
+ "sname", "sn",
+ "name", "n",
+ "args", "ar",
+ "debugtype", "d",
+ "internalservicename","i",
+ "comment","c",
+ NULL, NULL
+};
+
+static char *generate_real_service_name(char *display_name){
+ SYSTEMTIME systime;
+ FILETIME ftime;
+ char *buff = malloc(strlen(display_name)+
+ (8*2)+1);
+ char *tmp = _strdup(display_name);
+ int i;
+ /* 2 Hex chars for each byte in a DWORD */
+ GetSystemTime(&systime);
+ SystemTimeToFileTime(&systime,&ftime);
+ /* Remove trailing version info to avoid user confusion */
+ for(i = (strlen(tmp)-1);i > 0; --i)
+ if(tmp[i] == '_'){
+ tmp[i] = '\0';
+ break;
+ }
+ sprintf(buff,"%s%08x%08x",tmp,ftime.dwHighDateTime,
+ ftime.dwLowDateTime);
+ free(tmp);
+ return buff;
+}
+
+static int lookup_arg(char *arg){
+ int i;
+ if(*arg != '-' && *arg != '/')
+ return -1;
+ for(i=0; arg_tab[i] != NULL; i += 2){
+ if(!_strnicmp(arg_tab[i],arg+1,strlen(arg+1)) &&
+ !_strnicmp(arg_tab[i+1],arg+1,strlen(arg_tab[i+1])))
+ return (i / 2);
+ }
+ return -1;
+}
+
+
+
+char *edit_env(char *edit, char *oldenv){
+ char **arg;
+ char *value;
+ char *name = strdup(edit);
+ int i;
+ char *tmp;
+ arg = env_to_arg(oldenv);
+ value = strchr(name,'=');
+ if(value){
+ *(value++) = '\0';
+ if(*value == '\0')
+ value = NULL;
+ }
+ for(i=0;arg[i] != NULL; ++i){
+ tmp = strchr(arg[i],'=');
+ if(((int) strlen(name)) == (tmp - arg[i]) &&
+ !_strnicmp(name,arg[i], tmp - arg[i]))
+ break;
+ }
+ if(arg[i] != NULL){
+ free(arg[i]);
+ if(value){
+ arg[i] = strdup(edit);
+ } else {
+ do {
+ arg[i] = arg[i+1];
+ ++i;
+ } while(arg[i] != NULL);
+ }
+ } else if(value){ /* add to arg, which is always allocated
+ to hold one extra environment variable*/
+ arg[i] = strdup(edit);
+ arg[i+1] = NULL;
+ }
+ free(name);
+ return arg_to_env(arg);
+}
+
+int last_error = 0;
+
+void print_last_error(void){
+ char *mes;
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ (last_error) ? last_error : GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &mes,
+ 0,
+ NULL );
+ fprintf(stderr,"Error: %s",mes);
+ LocalFree(mes);
+}
+
+static BOOL install_service(void){
+ SC_HANDLE scm;
+ SC_HANDLE service;
+ char filename[MAX_PATH + 3];
+ DWORD fnsiz=MAX_PATH;
+ char dependant[] = { 'L','a','n','m','a','n',
+ 'W','o','r','k','s','t',
+ 'a','t','i','o','n','\0','\0'};
+
+ if(!(fnsiz = GetModuleFileName(NULL, filename, fnsiz)))
+ return FALSE;
+ if(strchr(filename,' ')){
+ memmove(filename+1,filename,fnsiz);
+ filename[0] ='\"'; /* " */
+ filename[fnsiz+1] = '\"'; /* " */
+ filename[fnsiz+2] = '\0';
+ }
+ if((scm = OpenSCManager(NULL,
+ NULL,
+ SC_MANAGER_CONNECT |
+ SC_MANAGER_CREATE_SERVICE))
+ == NULL){
+ last_error = GetLastError();
+ return FALSE;
+ }
+ service = CreateService(scm,
+ real_service_name,
+ service_name,
+ SERVICE_ALL_ACCESS &
+ ~(SERVICE_PAUSE_CONTINUE),
+ SERVICE_WIN32_OWN_PROCESS,
+ SERVICE_AUTO_START,
+ SERVICE_ERROR_NORMAL,
+ filename,
+ NULL,
+ NULL,
+ dependant,
+ NULL,
+ NULL);
+ if(service == NULL){
+ CloseServiceHandle(scm);
+ last_error = GetLastError();
+ return FALSE;
+ }
+ CloseServiceHandle(service);
+ CloseServiceHandle(scm);
+ return TRUE;
+}
+
+static BOOL remove_service(void){
+ SC_HANDLE scm;
+ SC_HANDLE service;
+ if((scm = OpenSCManager(NULL,
+ NULL,
+ GENERIC_WRITE))
+ == NULL)
+ return FALSE;
+ service = OpenService(scm,
+ real_service_name,
+ SERVICE_ALL_ACCESS);
+ if(service == NULL){
+ CloseServiceHandle(scm);
+ return FALSE;
+ }
+ if(!DeleteService(service)){
+ last_error = GetLastError();
+ return FALSE;
+ }
+ CloseServiceHandle(service);
+ CloseServiceHandle(scm);
+ return TRUE;
+}
+
+static BOOL open_service_control(SC_HANDLE *scm, SC_HANDLE *service){
+ if((*scm = OpenSCManager(NULL,
+ NULL,
+ SC_MANAGER_ALL_ACCESS))
+ == NULL)
+ return FALSE;
+ *service = OpenService(*scm,
+ real_service_name,
+ SERVICE_ALL_ACCESS);
+ if(service == NULL){
+ CloseServiceHandle(*scm);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static BOOL open_service_config(SC_HANDLE *scm, SC_HANDLE *service){
+ if((*scm = OpenSCManager(NULL,
+ NULL,
+ /*GENERIC_WRITE | GENERIC_EXECUTE*/
+ SC_MANAGER_ALL_ACCESS))
+ == NULL){
+ last_error = GetLastError();
+ return FALSE;
+ }
+ *service = OpenService(*scm,
+ real_service_name,
+ /*GENERIC_WRITE*/
+ SERVICE_ALL_ACCESS);
+ if(service == NULL){
+ last_error = GetLastError();
+ CloseServiceHandle(*scm);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static BOOL set_service_comment(char *comment) {
+ SC_HANDLE scm;
+ SC_HANDLE service;
+ SERVICE_DESCRIPTION sd;
+ BOOL ret = TRUE;
+ sd.lpDescription = comment;
+ if (!open_service_config(&scm,&service)) {
+ return FALSE;
+ }
+ if (!ChangeServiceConfig2(service,SERVICE_CONFIG_DESCRIPTION,&sd)) {
+ last_error = GetLastError();
+ ret = FALSE;
+ }
+ CloseServiceHandle(service);
+ CloseServiceHandle(scm);
+ return ret;
+}
+
+static BOOL wait_service_trans(DWORD initial, DWORD passes, DWORD goal,
+ int timeout)
+{
+ SC_HANDLE scm;
+ SC_HANDLE service;
+ int moved = 0;
+ BOOL ret;
+ int i;
+ SERVICE_STATUS stat;
+
+ if(! open_service_config(&scm,&service))
+ return FALSE;
+ for(i = 0; i < timeout; ++i){
+ if(!QueryServiceStatus(service,&stat)){
+ last_error = GetLastError();
+ ret = FALSE;
+ goto out;
+ }
+ if(stat.dwCurrentState == initial){
+ if(moved){
+ ret = FALSE;
+
+ /*
+ * The exitcode is usually strange when we tried to stop and failed,
+ * to report a timeout is more appropriate.
+ */
+ if(goal == SERVICE_STOPPED)
+ last_error = ERROR_SERVICE_REQUEST_TIMEOUT;
+ else
+ last_error = stat.dwWin32ExitCode;
+ goto out;
+ }
+ } else if(stat.dwCurrentState == passes){
+ moved = 1;
+ } else if(stat.dwCurrentState == goal){
+ ret = TRUE;
+ goto out;
+ }
+ Sleep(1000);
+ }
+ ret = FALSE;
+ last_error = ERROR_SERVICE_REQUEST_TIMEOUT;
+out:
+ CloseServiceHandle(scm);
+ CloseServiceHandle(service);
+ return ret;
+}
+
+static BOOL stop_service(void){
+ SC_HANDLE scm;
+ SC_HANDLE service;
+ BOOL ret;
+ SERVICE_STATUS ss;
+
+ if(!open_service_control(&scm,&service)){
+#ifdef HARDDEBUG
+ fprintf(stderr,"Failed to open service.\n");
+#endif
+ return FALSE;
+ }
+ ret = ControlService(service,SERVICE_CONTROL_STOP,&ss);
+ if(!ret){
+ last_error = GetLastError();
+ }
+ CloseServiceHandle(service);
+ CloseServiceHandle(scm);
+#ifdef HARDDEBUG
+ if(!ret)
+ {
+ fprintf(stderr,"Failed to control service.\n");
+ print_last_error();
+ }
+#endif
+ return ret;
+}
+
+static BOOL start_service(void){
+ SC_HANDLE scm;
+ SC_HANDLE service;
+ BOOL ret;
+
+ if(!open_service_control(&scm,&service))
+ return FALSE;
+
+ ret = StartService(service,0,NULL);
+ if(!ret){
+ last_error = GetLastError();
+ }
+ CloseServiceHandle(service);
+ CloseServiceHandle(scm);
+ return ret;
+}
+
+static BOOL disable_service(void){
+ SC_HANDLE scm;
+ SC_HANDLE service;
+ BOOL ret;
+
+ if(!open_service_config(&scm,&service))
+ return FALSE;
+
+ ret = ChangeServiceConfig(service,
+ SERVICE_NO_CHANGE,
+ SERVICE_DISABLED,
+ SERVICE_NO_CHANGE,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ if(!ret){
+ last_error = GetLastError();
+ }
+
+ CloseServiceHandle(service);
+ CloseServiceHandle(scm);
+ return ret;
+}
+
+static BOOL enable_service(void){
+ SC_HANDLE scm;
+ SC_HANDLE service;
+ BOOL ret;
+
+if(!open_service_config(&scm,&service))
+ return FALSE;
+
+ ret = ChangeServiceConfig(service,
+ SERVICE_NO_CHANGE,
+ SERVICE_AUTO_START,
+ SERVICE_NO_CHANGE,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ if(!ret){
+ last_error = GetLastError();
+ }
+ CloseServiceHandle(service);
+ CloseServiceHandle(scm);
+ return ret;
+}
+
+static BOOL set_interactive(BOOL interactive){
+ SC_HANDLE scm;
+ SC_HANDLE service;
+ BOOL ret;
+
+ if(!open_service_config(&scm,&service))
+ return FALSE;
+
+ ret = ChangeServiceConfig(service,
+ SERVICE_WIN32_OWN_PROCESS | ((interactive) ?
+ SERVICE_INTERACTIVE_PROCESS : 0),
+ SERVICE_NO_CHANGE,
+ SERVICE_NO_CHANGE,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ if(!ret){
+ last_error = GetLastError();
+ }
+ CloseServiceHandle(service);
+ CloseServiceHandle(scm);
+ return ret;
+}
+
+
+RegEntry *old_entries = NULL;
+
+BOOL fetch_current(RegEntry *new){
+ int i;
+
+ if(!(old_entries = get_keys(service_name)))
+ return FALSE;
+ for(i=0;i<num_reg_entries;++i)
+ new[i] = old_entries[i];
+ return TRUE;
+}
+
+void cleanup_old(){
+ if(old_entries != NULL)
+ free_keys(old_entries);
+}
+
+BOOL fill_in_defaults(RegEntry *new){
+ char filename[MAX_PATH];
+ char *ptr;
+
+
+ if(!GetModuleFileName(NULL, filename, MAX_PATH))
+ return FALSE;
+ for(ptr = filename + strlen(filename) - 1;
+ ptr > filename && *ptr != '\\';
+ --ptr)
+ ;
+ if(*ptr == '\\')
+ ++ptr;
+ *ptr = '\0';
+
+ ptr = malloc(strlen(filename)+strlen(ERLANG_MACHINE)+1);
+ strcpy(ptr,filename);
+ strcat(ptr,ERLANG_MACHINE);
+
+ new[StopAction].data.bytes = "";
+ new[OnFail].data.value = ON_FAIL_IGNORE;
+ new[Machine].data.bytes = ptr;
+ new[Machine].data.expand.unexpanded = ptr;
+ new[Env].data.bytes = "\0";
+ new[WorkDir].data.bytes = new[WorkDir].data.expand.unexpanded =
+ "";
+ new[Priority].data.value = NORMAL_PRIORITY_CLASS;
+ new[SName].data.bytes = service_name;
+ new[Name].data.bytes = "";
+ new[Args].data.bytes = new[Args].data.expand.unexpanded = "";
+ new[DebugType].data.value = DEBUG_TYPE_NO_DEBUG;
+ new[InternalServiceName].data.bytes = real_service_name;
+ new[Comment].data.bytes = "";
+ return TRUE;
+}
+
+int do_usage(char *arg0){
+ printf("Usage:\n");
+ printf("%s {set | add} <servicename>\n"
+ "\t[-st[opaction] [<erlang shell command>]]\n"
+ "\t[-on[fail] [{reboot | restart | restart_always}]]\n"
+ "\t[-m[achine] [<erl-command>]]\n"
+ "\t[-e[nv] [<variable>[=<value>]]]\n"
+ "\t[-w[orkdir] [<directory>]]\n"
+ "\t[-p[riority] [{low|high|realtime}]]\n"
+ "\t[{-sn[ame] | -n[ame]} [<nodename>]]\n"
+ "\t[-d[ebugtype] [{new|reuse|console}]]\n"
+ "\t[-ar[gs] [<limited erl arguments>]]\n\n"
+ "%s {start | stop | disable | enable} <servicename>\n\n"
+ "%s remove <servicename>\n\n"
+ "%s rename <servicename> <servicename>\n\n"
+ "%s list [<servicename>]\n\n"
+ "%s help\n\n",
+ arg0,arg0,arg0,arg0,arg0,arg0);
+ printf("Manipulates Erlang system services on Windows NT.\n\n");
+ printf("When no parameter to an option is specified, the option\n"
+ "is reset to it's default value. To set an empty argument\n"
+ "list, give option -args as last option on command line "
+ "with\n"
+ "no arguments.\n\n");
+ printf("Se Erlang documentation for full description.\n");
+ return 0;
+}
+
+int do_manage(int argc,char **argv){
+ char *action = argv[1];
+ RegEntry *current = empty_reg_tab();
+
+ if(argc < 3){
+ fprintf(stderr,"%s: No servicename given!\n",argv[0]);
+ do_usage(argv[0]);
+ return 1;
+ }
+ service_name = argv[2];
+ if(!fetch_current(current)){
+ fprintf(stderr,"%s: The service %s is not an erlsrv controlled service.\n",
+ argv[0],service_name);
+ return 1;
+ }
+ real_service_name = _strdup(current[InternalServiceName].data.bytes);
+ free_keys(current);
+
+ if(!_stricmp(action,"start")){
+ if(!start_service()){
+ fprintf(stderr,"%s: Failed to start service %s.\n",
+ argv[0],service_name);
+ print_last_error();
+ return 1;
+ } else {
+ if(!wait_service_trans(SERVICE_STOPPED, SERVICE_START_PENDING,
+ SERVICE_RUNNING, 60)){
+ fprintf(stderr,"%s: Failed to start service %s.\n",
+ argv[0],service_name);
+ print_last_error();
+ return 1;
+ }
+ printf("%s: Service %s started.\n",
+ argv[0],service_name);
+ return 0;
+ }
+ }
+ if(!_stricmp(action,"stop")){
+ if(!stop_service()){
+ fprintf(stderr,"%s: Failed to stop service %s.\n",
+ argv[0],service_name);
+ print_last_error();
+ return 1;
+ } else {
+ if(!wait_service_trans(SERVICE_RUNNING, SERVICE_STOP_PENDING,
+ SERVICE_STOPPED, 60)){
+ fprintf(stderr,"%s: Failed to stop service %s.\n",
+ argv[0],service_name);
+ print_last_error();
+ return 1;
+ }
+ printf("%s: Service %s stopped.\n",
+ argv[0],service_name);
+ return 0;
+ }
+ }
+ if(!_stricmp(action,"disable")){
+#if 0
+ if(stop_service()){
+ printf("%s: Service %s stopped.\n",
+ argv[0],service_name);
+ }
+#endif
+ if(!disable_service()){
+ fprintf(stderr,"%s: Failed to disable service %s.\n",
+ argv[0],service_name);
+ print_last_error();
+ return 1;
+ } else {
+ printf("%s: Service %s disabled.\n",
+ argv[0],service_name);
+ return 0;
+ }
+ }
+ if(!_stricmp(action,"enable")){
+ if(!enable_service()){
+ fprintf(stderr,"%s: Failed to enable service %s.\n",
+ argv[0],service_name);
+ print_last_error();
+ return 1;
+ } else {
+ printf("%s: Service %s enabled.\n",
+ argv[0],service_name);
+ return 0;
+ }
+ }
+ fprintf(stderr,"%s: Unrecignized argument %s.\n",
+ argv[0],action);
+ return 1;
+}
+
+int do_add_or_set(int argc, char **argv){
+ RegEntry *new_entries;
+ RegEntry *default_entries;
+ int add = 0;
+ int i;
+ int current;
+ int set_comment = 0;
+ new_entries = empty_reg_tab();
+ default_entries = empty_reg_tab();
+ if(argc < 3){
+ fprintf(stderr,"%s: No servicename given!\n",argv[0]);
+ do_usage(argv[0]);
+ return 1;
+ }
+ service_name = argv[2];
+ if(!_stricmp(argv[1],"add")){
+ if(fetch_current(default_entries)){
+ fprintf(stderr,"%s: A service with the name %s already "
+ "exists.\n",
+ argv[0],service_name);
+ return 1;
+ }
+ real_service_name = generate_real_service_name(service_name);
+ if(!fill_in_defaults(new_entries)){
+ fprintf(stderr,"%s: Internal error.\n", argv[0]);
+ return 1;
+ }
+ add = 1;
+ } else {
+ if(!fetch_current(new_entries)){
+ fprintf(stderr,"%s: No service with the name %s exists.\n",
+ argv[0], service_name);
+ return 1;
+ }
+ real_service_name = new_entries[InternalServiceName].data.bytes;
+ }
+
+ if(!fill_in_defaults(default_entries)){
+ fprintf(stderr,"%s: Internal error.\n", argv[0]);
+ return 1;
+ }
+
+ /* make sure env is malloced... */
+ new_entries[Env].data.bytes = envdup(new_entries[Env].data.bytes);
+
+ for(i = 3; i < argc; ++i){
+ switch((current = lookup_arg(argv[i]))){
+ case Comment:
+ set_comment = 1;
+ case Machine:
+ case WorkDir:
+ case Args:
+ if(i+1 >= argc){
+ new_entries[current].data.bytes =
+ default_entries[current].data.bytes;
+ new_entries[current].data.expand.unexpanded =
+ default_entries[current].data.expand.unexpanded;
+ } else {
+ new_entries[current].data.expand.unexpanded =
+ new_entries[current].data.bytes = argv[i+1];
+ ++i;
+ }
+ break;
+ case SName:
+ new_entries[Name].data.bytes = "";
+ case StopAction:
+ case Name:
+ if(i+1 >= argc ||
+ *argv[i+1] == '-' || *argv[i+1] == '/'){
+ new_entries[current].data.bytes =
+ default_entries[current].data.bytes;
+ } else {
+ new_entries[current].data.bytes = argv[i+1];
+ ++i;
+ }
+ break;
+ case OnFail:
+ if(i+1 >= argc ||
+ *argv[i+1] == '-' || *argv[i+1] == '/'){
+ new_entries[current].data.value =
+ default_entries[current].data.value;
+ } else {
+ if(!_stricmp(argv[i+1],"reboot"))
+ new_entries[current].data.value = ON_FAIL_REBOOT;
+ else if(!_stricmp(argv[i+1],"restart"))
+ new_entries[current].data.value = ON_FAIL_RESTART;
+ else if(!_stricmp(argv[i+1],"restart_always"))
+ new_entries[current].data.value = ON_FAIL_RESTART_ALWAYS;
+ else {
+ fprintf(stderr,"%s: Unrecognized keyword value %s.\n",
+ argv[0],argv[i+1]);
+ return 1;
+ }
+ ++i;
+ }
+ break;
+ case DebugType:
+ if(i+1 >= argc ||
+ *argv[i+1] == '-' || *argv[i+1] == '/'){
+ new_entries[current].data.value =
+ default_entries[current].data.value;
+ } else {
+ if(!_stricmp(argv[i+1],"new"))
+ new_entries[current].data.value = DEBUG_TYPE_NEW;
+ else if(!_stricmp(argv[i+1],"reuse"))
+ new_entries[current].data.value = DEBUG_TYPE_REUSE;
+ else if(!_stricmp(argv[i+1],"console"))
+ new_entries[current].data.value = DEBUG_TYPE_CONSOLE;
+ else {
+ fprintf(stderr,"%s: Unrecognized keyword value %s.\n",
+ argv[0],argv[i+1]);
+ return 1;
+ }
+ ++i;
+ }
+ break;
+ case Priority:
+ if(i+1 >= argc ||
+ *argv[i+1] == '-' || *argv[i+1] == '/'){
+ new_entries[current].data.value =
+ default_entries[current].data.value;
+ } else {
+ if(!_stricmp(argv[i+1],"high"))
+ new_entries[current].data.value = HIGH_PRIORITY_CLASS;
+ else if(!_stricmp(argv[i+1],"low"))
+ new_entries[current].data.value = IDLE_PRIORITY_CLASS;
+ else if(!_stricmp(argv[i+1],"realtime"))
+ new_entries[current].data.value = REALTIME_PRIORITY_CLASS;
+ else {
+ fprintf(stderr,"%s: Unrecognized keyword value %s.\n",
+ argv[0],argv[i+1]);
+ return 1;
+ }
+ ++i;
+ }
+ break;
+
+ case Env:
+ if(i+1 >= argc ||
+ *argv[i+1] == '-' || *argv[i+1] == '/'){
+ fprintf(stderr,"%s: %s requires a parameter.\n",
+ argv[0],argv[i]);
+ return 1;
+ }
+ new_entries[current].data.bytes =
+ edit_env(argv[i+1],
+ new_entries[current].data.bytes);
+ ++i;
+ break;
+ case InternalServiceName:
+ if (!add) {
+ fprintf(stderr,"%s: %s only allowed when adding a new service.\n",
+ argv[0],argv[i]);
+ return 1;
+ }
+ if(i+1 >= argc){
+ fprintf(stderr,"%s: %s requires a parameter.\n",
+ argv[0],argv[i]);
+ return 1;
+ }
+ new_entries[InternalServiceName].data.expand.unexpanded =
+ new_entries[InternalServiceName].data.bytes = argv[i+1];
+ ++i;
+ /* Discard old, should maybe be fred' but we'll exit anyway */
+ real_service_name = new_entries[InternalServiceName].data.bytes;
+ break;
+ default:
+ fprintf(stderr,"%s: Unrecognized option %s.\n", argv[0],
+ argv[i]);
+ return 1;
+ }
+ }
+ if(*new_entries[SName].data.bytes &&
+ *new_entries[Name].data.bytes){
+#if 0
+ fprintf(stderr,"%s: Both -sname and -name specified.\n",
+ argv[0]);
+ return 1;
+#else
+ new_entries[SName].data.bytes = "";
+#endif
+ }
+ if(add && !(*new_entries[SName].data.bytes) &&
+ !(*new_entries[Name].data.bytes)){
+ fprintf(stderr,"%s: Neither -sname nor -name specified.\n",
+ argv[0]);
+ return 1;
+ }
+ if(add && !install_service()){
+ fprintf(stderr,"%s: Unable to register service with service manager.\n",
+ argv[0], service_name);
+ print_last_error();
+ return 1;
+ }
+ if(!set_interactive(new_entries[DebugType].data.value ==
+ DEBUG_TYPE_CONSOLE)){
+ fprintf(stderr,"%s: Warning, could not set correct interactive mode.\n",
+ argv[0], service_name);
+ print_last_error();
+ /* Not severe or??? */
+ }
+ /* Update registry */
+ register_logkeys();
+ set_keys(service_name, new_entries);
+ /* Update service comment if needed */
+ if(set_comment) {
+ if (!set_service_comment(new_entries[Comment].data.bytes)) {
+ fprintf(stderr,"%s: Warning, could not set correct "
+ "service description (comment)",
+ argv[0], service_name);
+ print_last_error();
+ }
+ }
+
+ /* As I do this, I should also clean up the new entries, which is
+ somewhat harder as I really dont know what is and what is not
+ malloced, but we'll exit anyway, so... */
+ cleanup_old();
+ if(add)
+ printf("%s: Service %s added to system.\n",
+ argv[0], service_name);
+ else
+ printf("%s: Service %s updated.\n",
+ argv[0], service_name);
+ return 0;
+}
+int do_rename(int argc, char **argv){
+ RegEntry *current = empty_reg_tab();
+ RegEntry *dummy = empty_reg_tab();
+ SC_HANDLE scm;
+ SC_HANDLE service;
+ if(argc < 3){
+ fprintf(stderr,"%s: No old servicename given!\n",argv[0]);
+ do_usage(argv[0]);
+ return 1;
+ }
+ if(argc < 4){
+ fprintf(stderr,"%s: No new servicename given!\n",argv[0]);
+ do_usage(argv[0]);
+ return 1;
+ }
+ service_name = argv[3];
+ if(fetch_current(dummy)){
+ fprintf(stderr,"%s: A service with the name %s already "
+ "exists.\n",
+ argv[0],service_name);
+ return 1;
+ }
+ service_name = argv[2];
+
+ if(!fetch_current(current)){
+ fprintf(stderr,"%s: Error, old service name %s does not exist.\n",
+ argv[0],service_name);
+ return 1;
+ }
+ real_service_name = _strdup(current[InternalServiceName].data.bytes);
+ if(!open_service_config(&scm,&service)){
+ fprintf(stderr,"%s: Error, unable to communicate with service control"
+ " manager.\n",
+ argv[0]);
+ print_last_error();
+ return 1;
+ }
+ if(!ChangeServiceConfig(service,
+ SERVICE_NO_CHANGE,
+ SERVICE_NO_CHANGE,
+ SERVICE_NO_CHANGE,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ argv[3])){
+ fprintf(stderr,"%s: Error, unable to communicate with service control"
+ " manager.\n",
+ argv[0]);
+ print_last_error();
+ CloseServiceHandle(scm);
+ CloseServiceHandle(service);
+ return 1;
+ }
+ CloseServiceHandle(scm);
+ CloseServiceHandle(service);
+
+ if(remove_keys(service_name) != 0)
+ fprintf(stderr,"%s: Warning, old service parameter keys could not "
+ "be removed, continuing.\n", argv[0]);
+ /* Update registry */
+ register_logkeys();
+ set_keys(argv[3], current);
+
+ printf("%s: Service %s renamed to %s.\n",
+ argv[0], service_name, argv[3]);
+ return 0;
+}
+
+int do_remove(int argc, char **argv){
+ RegEntry *current = empty_reg_tab();
+ int rem_res;
+ BOOL found;
+
+ if(argc < 3){
+ fprintf(stderr,"%s: No servicename given!\n",argv[0]);
+ do_usage(argv[0]);
+ return 1;
+ }
+ service_name = argv[2];
+ found = fetch_current(current);
+ if(found){
+ real_service_name = _strdup(current[InternalServiceName].data.bytes);
+ } else {
+ real_service_name = _strdup(service_name);
+ }
+ if(found)
+ free_keys(current);
+ if(stop_service() && !wait_service_trans(SERVICE_RUNNING,
+ SERVICE_STOP_PENDING,
+ SERVICE_STOPPED, 60)){
+ fprintf(stderr,"%s: Failed to stop running service %s.\n",
+ argv[0],service_name);
+ print_last_error();
+ return 1;
+ }
+ if(!remove_service()){
+ fprintf(stderr,"%s: Unable to remove service (not enough "
+ "privileges?)\n",argv[0]);
+ print_last_error();
+ return 1;
+ }
+
+ if((rem_res = remove_keys(service_name)) > 0){
+ fprintf(stderr,"%s: Warning, service parameter keys belonged to old "
+ "erlsrv version.\n", argv[0]);
+ /* Backward compatibility... */
+ } else if(rem_res < 0) {
+ fprintf(stderr,"%s: Error, service parameter keys nonexistent.\n",
+ argv[0]);
+ return 1;
+ }
+ printf("%s: Service %s removed from system.\n",
+ argv[0], service_name);
+ return 0;
+}
+
+BOOL list_one(char *servicename, RegEntry *keys, BOOL longlist){
+ char *onfail;
+ char *prio;
+ char *debugtype;
+ switch(keys[OnFail].data.value){
+ case ON_FAIL_RESTART:
+ onfail = "restart";
+ break;
+ case ON_FAIL_RESTART_ALWAYS:
+ onfail = "restart_always";
+ break;
+ case ON_FAIL_REBOOT:
+ onfail = "reboot";
+ break;
+ default:
+ onfail = "ignore";
+ }
+ switch(keys[DebugType].data.value){
+ case DEBUG_TYPE_NEW:
+ debugtype = "new";
+ break;
+ case DEBUG_TYPE_REUSE:
+ debugtype = "reuse";
+ break;
+ case DEBUG_TYPE_CONSOLE:
+ debugtype = "console";
+ break;
+ default:
+ debugtype = "none";
+ }
+ switch(keys[Priority].data.value){
+ case HIGH_PRIORITY_CLASS:
+ prio = "high";
+ break;
+ case IDLE_PRIORITY_CLASS:
+ prio = "low";
+ break;
+ case REALTIME_PRIORITY_CLASS:
+ prio = "realtime";
+ break;
+ case NORMAL_PRIORITY_CLASS:
+ prio = "default";
+ break;
+ default:
+ prio = "unknown/faulty";
+ }
+
+
+ if(longlist){
+ char *env = envdup(keys[Env].data.bytes);
+ char **arg = env_to_arg(env);
+ char **pek = arg;
+ printf("Service name: %s\n",
+ servicename);
+ printf("StopAction: %s\n",
+ keys[StopAction].data.bytes);
+ printf("OnFail: %s\n",onfail);
+ printf("Machine: %s\n",
+ keys[Machine].data.expand.unexpanded);
+ printf("WorkDir: %s\n",
+ keys[WorkDir].data.expand.unexpanded);
+ if(*keys[SName].data.bytes)
+ printf("SName: %s\n",
+ keys[SName].data.bytes);
+ else
+ printf("Name: %s\n",
+ keys[Name].data.bytes);
+ printf("Priority: %s\n",prio);
+ printf("DebugType: %s\n",debugtype);
+ printf("Args: %s\n",
+ keys[Args].data.expand.unexpanded);
+ printf("InternalServiceName: %s\n",
+ keys[InternalServiceName].data.bytes);
+ printf("Comment: %s\n",
+ keys[Comment].data.bytes);
+ printf("Env:\n");
+ while(*pek){
+ printf("\t%s\n",*pek);
+ ++pek;
+ }
+ /* env is easier to free...*/
+ env = arg_to_env(arg);
+ free(env);
+ } else {
+ printf("%s\t%s\t%s\t%s\t%s\n",
+ servicename,
+ (*keys[Name].data.bytes) ?
+ keys[Name].data.bytes :
+ keys[SName].data.bytes,
+ prio,
+ onfail,
+ keys[Args].data.expand.unexpanded);
+ }
+ return TRUE;
+}
+
+
+int do_list(int argc, char **argv){
+ if(argc < 3){
+ RegEntryDesc *all_keys = get_all_keys();
+ if(!all_keys){
+ fprintf(stderr,"%s: No services found in registry.\n",
+ argv[0]);
+ return 0;
+ }
+ printf("Service\t(S)Name\tPrio\tOnFail\tArgs\n");
+ while(all_keys->servicename){
+ list_one(all_keys->servicename,all_keys->entries,FALSE);
+ ++all_keys;
+ }
+ return 0;
+ } else {
+ RegEntry *keys;
+ service_name = argv[2];
+ keys = get_keys(service_name);
+ if(!keys){
+ fprintf(stderr,"%s: Could not retrieve any "
+ "registered data for %s.\n",argv[0],service_name);
+ return 1;
+ }
+ list_one(service_name, keys, TRUE);
+ }
+ return 0;
+}
+
+#define READ_CHUNK 100
+#define ARGV_CHUNK 20
+
+char *safe_get_line(void){
+ int lsize = READ_CHUNK;
+ char *line = malloc(READ_CHUNK);
+ int pos = 0;
+ int ch;
+
+ while((ch = getchar()) != EOF && ch != '\n'){
+ if(pos + 1 >= lsize){
+ line = realloc(line,(lsize += READ_CHUNK));
+ assert(line);
+ }
+ line[pos++] = ch;
+ }
+ if(ch == EOF || !pos){
+ free(line);
+ return NULL;
+ }
+ line[pos] = '\0';
+ return line;
+}
+
+
+void read_arguments(int *pargc, char ***pargv){
+ int argc = 0;
+ int asize = ARGV_CHUNK;
+ char **argv = malloc(ARGV_CHUNK*sizeof(char *));
+ char *tmp;
+
+ argv[0] = (*pargv)[0];
+ argc = 1;
+ while((tmp = safe_get_line()) != NULL){
+ if(argc + 1 >= asize){
+ argv = realloc(argv,(asize += ARGV_CHUNK)*sizeof(char *));
+ assert(argv);
+ }
+ argv[argc++] = tmp;
+ }
+ argv[argc] = NULL;
+ *pargc = argc;
+ *pargv = argv;
+}
+
+
+int interactive_main(int argc, char **argv){
+ char *action = argv[1];
+
+ if(!_stricmp(action,"readargs")){
+ read_arguments(&argc,&argv);
+ action = argv[1];
+ }
+ if(!_stricmp(action,"set") || !_stricmp(action,"add"))
+ return do_add_or_set(argc,argv);
+ if(!_stricmp(action,"rename"))
+ return do_rename(argc,argv);
+ if(!_stricmp(action,"remove"))
+ return do_remove(argc,argv);
+ if(!_stricmp(action,"list"))
+ return do_list(argc,argv);
+ if(!_stricmp(action,"start") ||
+ !_stricmp(action,"stop") ||
+ !_stricmp(action,"enable") ||
+ !_stricmp(action,"disable"))
+ return do_manage(argc,argv);
+ if(_stricmp(action,"?") &&
+ _stricmp(action,"/?") &&
+ _stricmp(action,"-?") &&
+ *action != 'h' &&
+ *action != 'H')
+ fprintf(stderr,"%s: action %s not implemented.\n",argv[0],action);
+ do_usage(argv[0]);
+ return 1;
+}
+
diff --git a/erts/etc/win32/erlsrv/erlsrv_interactive.h b/erts/etc/win32/erlsrv/erlsrv_interactive.h
new file mode 100644
index 0000000000..deacf81899
--- /dev/null
+++ b/erts/etc/win32/erlsrv/erlsrv_interactive.h
@@ -0,0 +1,24 @@
+/*
+ * %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%
+ */
+#ifndef _ERLSRV_INTERACTIVE_H
+#define _ERLSRV_INTERACTIVE_H
+
+int interactive_main(int argc, char **argv);
+
+#endif /* _ERLSRV_INTERACTIVE_H */
diff --git a/erts/etc/win32/erlsrv/erlsrv_logmess.mc b/erts/etc/win32/erlsrv/erlsrv_logmess.mc
new file mode 100644
index 0000000000..354ac14c9f
--- /dev/null
+++ b/erts/etc/win32/erlsrv/erlsrv_logmess.mc
@@ -0,0 +1,33 @@
+;/*MessageIDTypedef=WORD*/
+;
+;/*MessageID=0x1*/
+;/*SymbolicName=CAT_GENERIC*/
+;/*Language=English*/
+;/*Generic Category*/
+;/*.*/
+;
+MessageIDTypedef=DWORD
+
+MessageID=0x10
+Severity=Warning
+Facility=Application
+SymbolicName=MSG_WARNING
+Language=English
+%1: %2
+.
+MessageID=0x11
+Severity=Error
+Facility=Application
+SymbolicName=MSG_ERROR
+Language=English
+%1: %2
+.
+MessageID=0x12
+Severity=Informational
+Facility=Application
+SymbolicName=MSG_INFO
+Language=English
+%1: %2
+.
+
+
diff --git a/erts/etc/win32/erlsrv/erlsrv_main.c b/erts/etc/win32/erlsrv/erlsrv_main.c
new file mode 100644
index 0000000000..920a4a1827
--- /dev/null
+++ b/erts/etc/win32/erlsrv/erlsrv_main.c
@@ -0,0 +1,44 @@
+/*
+ * %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_interactive.h"
+#include "erlsrv_service.h"
+
+int main(int argc, char **argv){
+ if(argc > 1)
+ return interactive_main(argc,argv);
+ else
+ return service_main(argc,argv);
+}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/erts/etc/win32/erlsrv/erlsrv_registry.c b/erts/etc/win32/erlsrv/erlsrv_registry.c
new file mode 100644
index 0000000000..c1aa9f2b67
--- /dev/null
+++ b/erts/etc/win32/erlsrv/erlsrv_registry.c
@@ -0,0 +1,404 @@
+/*
+ * %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"
+
+#define LOG_TYPE "System"
+#define LOG_ROOT \
+"SYSTEM\\CurrentControlSet\\Services\\EventLog\\" LOG_TYPE "\\"
+#define LOG_APP_KEY APP_NAME
+
+
+#define BASE_KEY HKEY_LOCAL_MACHINE
+#define PRODUCT_NAME APP_NAME
+#define OLD_PRODUCT_VERSION "1.0"
+#define PRODUCT_VERSION "1.1"
+#define PROG_KEY "SOFTWARE\\Ericsson\\Erlang\\" PRODUCT_NAME "\\" PRODUCT_VERSION
+#define OLD_PROG_KEY "SOFTWARE\\Ericsson\\Erlang\\" PRODUCT_NAME "\\" OLD_PRODUCT_VERSION
+
+#define MAX_KEY_LEN MAX_PATH
+
+static const char * const noString = "\0";
+
+#define MAX_MANDATORY_REG_ENTRY 10 /* InternalServiceName == reg_entries[10] */
+static RegEntry reg_entries[] = {
+ {"StopAction",REG_SZ,NULL},
+ {"OnFail",REG_DWORD,NULL},
+ {"Machine",REG_EXPAND_SZ,NULL},
+ {"Env", REG_MULTI_SZ,NULL},
+ {"WorkDir", REG_EXPAND_SZ,NULL},
+ {"Priority",REG_DWORD,NULL},
+ {"SName",REG_SZ,NULL},
+ {"Name",REG_SZ,NULL},
+ {"Args",REG_EXPAND_SZ,NULL},
+ {"DebugType",REG_DWORD,NULL},
+ {"InternalServiceName",REG_SZ,NULL},
+ /* Non mandatory follows */
+ {"Comment",REG_SZ,NULL}
+};
+
+
+int num_reg_entries = sizeof(reg_entries)/sizeof(RegEntry);
+
+RegEntry *empty_reg_tab(void){
+ RegEntry *ret = malloc(num_reg_entries * sizeof(RegEntry));
+ memcpy(ret,reg_entries,num_reg_entries * sizeof(RegEntry));
+ return ret;
+}
+
+void free_keys(RegEntry *keys){
+ int i;
+
+ for(i=0;i<num_reg_entries && keys[i].name != NULL;++i){
+ if((keys[i].type == REG_SZ || keys[i].type == REG_EXPAND_SZ ||
+ keys[i].type == REG_MULTI_SZ) &&
+ keys[i].data.bytes != noString){
+ free(keys[i].data.bytes);
+ if(keys[i].type == REG_EXPAND_SZ &&
+ keys[i].data.expand.unexpanded != noString)
+ free(keys[i].data.expand.unexpanded);
+ }
+ }
+ free(keys);
+}
+
+void free_all_keys(RegEntryDesc *descs){
+ RegEntryDesc *tmp = descs;
+ for(;tmp->servicename != NULL; ++tmp){
+ free_keys(tmp->entries);
+ free(tmp->servicename);
+ }
+ free(descs);
+}
+
+RegEntry *get_keys(char *servicename){
+ RegEntry *res = NULL;
+ HKEY prog_key;
+ int key_opened = 0;
+ int i;
+ DWORD ret;
+ char *copy;
+ char *tmpbuf;
+ DWORD tmpbuflen;
+
+ char key_to_open[MAX_KEY_LEN];
+
+ DWORD val_type;
+ char *val_data = malloc(MAX_KEY_LEN);
+ DWORD val_datalen;
+ DWORD val_datasiz = MAX_KEY_LEN;
+
+ if(strlen(PROG_KEY) + strlen(servicename) + 2 > MAX_KEY_LEN)
+ goto error;
+ sprintf(key_to_open,"%s\\%s",PROG_KEY,servicename);
+
+ if(RegOpenKeyEx(BASE_KEY,
+ key_to_open,
+ 0,
+ KEY_QUERY_VALUE,
+ &prog_key) != ERROR_SUCCESS)
+ goto error;
+ key_opened = 1;
+
+ res = malloc(num_reg_entries*sizeof(RegEntry));
+ for(i=0;i<num_reg_entries;++i)
+ res[i].name = NULL;
+
+ for(i=0;i<num_reg_entries;++i){
+ for(;;){
+ val_datalen = val_datasiz;
+ ret = RegQueryValueEx(prog_key,
+ reg_entries[i].name,
+ NULL,
+ &val_type,
+ (BYTE *) val_data,
+ &val_datalen);
+ if(ret == ERROR_SUCCESS){
+ if(reg_entries[i].type == val_type)
+ break;
+ else
+ goto error;
+ } else if(ret == ERROR_MORE_DATA){
+ val_data = realloc(val_data,val_datasiz = val_datalen);
+ } else if (i > MAX_MANDATORY_REG_ENTRY && ret == ERROR_FILE_NOT_FOUND) {
+ /* Non mandatory entries, look at the type... */
+ switch (reg_entries[i].type){
+ case REG_EXPAND_SZ:
+ case REG_SZ:
+ case REG_MULTI_SZ:
+ val_datalen = 0;
+ break;
+ case REG_DWORD:
+ {
+ DWORD dummy = 0;
+ memcpy(val_data,&dummy,(val_datalen = sizeof(DWORD)));
+ }
+ break;
+ default:
+ goto error;
+ }
+ break; /* for(;;) */
+ } else {
+ goto error;
+ }
+ }
+ res[i] = reg_entries[i];
+ copy = NULL;
+ switch(reg_entries[i].type){
+ case REG_EXPAND_SZ:
+ if(!val_datalen || val_data[0] == '\0'){
+ copy = (char *) noString;
+ res[i].data.expand.unexpanded = (char *) noString;
+ } else {
+ tmpbuf = malloc(MAX_KEY_LEN);
+ tmpbuflen = (DWORD) MAX_KEY_LEN;
+ for(;;){
+ ret = ExpandEnvironmentStrings(val_data,tmpbuf,tmpbuflen);
+ if(!ret){
+ free(tmpbuf);
+ goto error;
+ }else if(ret > tmpbuflen){
+ tmpbuf=realloc(tmpbuf,tmpbuflen=ret);
+ } else {
+ copy = strdup(tmpbuf);
+ free(tmpbuf);
+ break;
+ }
+ }
+ res[i].data.expand.unexpanded = strdup(val_data);
+ }
+ case REG_MULTI_SZ:
+ case REG_SZ:
+ if(!copy){
+ if(!val_datalen ||
+ ((val_datalen == 1 && val_data[0] == '\0') ||
+ (val_datalen == 2 && val_data[0] == '\0' &&
+ val_data[1] == '\0'))){
+ copy = (char *) noString;
+ } else {
+ copy = malloc(val_datalen);
+ memcpy(copy,val_data,val_datalen);
+ }
+ }
+ res[i].data.bytes = copy;
+ break;
+ case REG_DWORD:
+ memcpy(&res[i].data.value,val_data,sizeof(DWORD));
+ break;
+ default:
+ goto error;
+ }
+ }
+ RegCloseKey(prog_key);
+ free(val_data);
+ return res;
+error:
+ free(val_data);
+ if(res != NULL)
+ free_keys(res);
+ if(key_opened)
+ RegCloseKey(prog_key);
+ return NULL;
+}
+
+int set_keys(char *servicename, RegEntry *keys){
+ HKEY prog_key;
+ int key_opened = 0;
+ int i;
+ char key_to_open[MAX_KEY_LEN];
+ DWORD disposition;
+
+ if(strlen(PROG_KEY) + strlen(servicename) + 2 > MAX_KEY_LEN)
+ goto error;
+ sprintf(key_to_open,"%s\\%s",PROG_KEY,servicename);
+
+ if(RegOpenKeyEx(BASE_KEY,
+ key_to_open,
+ 0,
+ KEY_SET_VALUE,
+ &prog_key) != ERROR_SUCCESS){
+ if(RegCreateKeyEx(BASE_KEY,
+ key_to_open,
+ 0,
+ NULL,
+ REG_OPTION_NON_VOLATILE,
+ KEY_SET_VALUE,
+ NULL,
+ &prog_key,
+ &disposition) != ERROR_SUCCESS)
+ goto error;
+ }
+ key_opened = 1;
+
+
+ for(i=0;i<num_reg_entries;++i){
+ void *ptr;
+ DWORD siz;
+ int j;
+ switch(keys[i].type){
+ case REG_SZ:
+ ptr = keys[i].data.bytes;
+ siz = strlen(ptr)+1;
+ break;
+ case REG_EXPAND_SZ:
+ ptr = keys[i].data.expand.unexpanded;
+ siz = strlen(ptr)+1;
+ break;
+ case REG_MULTI_SZ:
+ ptr = keys[i].data.bytes;
+ for(j=0;!(((char *)ptr)[j] == '\0' &&
+ ((char *)ptr)[j+1] == '\0');++j)
+ ;
+ siz=(DWORD)j+2;
+ break;
+ case REG_DWORD:
+ ptr = &keys[i].data.value;
+ siz = sizeof(DWORD);
+ break;
+ default:
+ goto error;
+ }
+#ifdef HARDDEBUG
+ fprintf(stderr,"%s %s:%d\n",keys[i].name,
+ (keys[i].type == REG_DWORD) ? "(dword)" : ptr,siz);
+#endif
+ if(RegSetValueEx(prog_key,
+ keys[i].name,
+ 0,
+ keys[i].type,
+ ptr,
+ siz) != ERROR_SUCCESS)
+ goto error;
+ }
+ RegCloseKey(prog_key);
+ return 0;
+error:
+ if(key_opened)
+ RegCloseKey(prog_key);
+ return 1;
+}
+
+static int do_remove_keys(char *servicename, const char *prog_key_name){
+ HKEY prog_key;
+ if(RegOpenKeyEx(BASE_KEY,
+ prog_key_name,
+ 0,
+ KEY_ALL_ACCESS,
+ &prog_key) != ERROR_SUCCESS)
+ return -1;
+ if(RegDeleteKey(prog_key,servicename) != ERROR_SUCCESS){
+ RegCloseKey(prog_key);
+ return -1;
+ }
+ RegCloseKey(prog_key);
+ return 0;
+}
+
+int remove_keys(char *servicename){
+ int ret;
+
+ if((ret = do_remove_keys(servicename, PROG_KEY)) < 0){
+ if(!do_remove_keys(servicename, OLD_PROG_KEY))
+ return 1;
+ else
+ return -1;
+ }
+ return ret;
+}
+
+
+RegEntryDesc *get_all_keys(void){
+ RegEntryDesc *res = malloc(10*sizeof(RegEntryDesc));
+ int res_siz = 10;
+ int ndx = 0;
+ HKEY prog_key;
+ int key_opened = 0;
+ DWORD enum_index;
+ char name[MAX_KEY_LEN];
+ DWORD namelen;
+ char class[MAX_KEY_LEN];
+ DWORD classlen;
+ FILETIME ft;
+
+ res[ndx].servicename = NULL;
+ if(RegOpenKeyEx(BASE_KEY, PROG_KEY, 0,
+ KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS,
+ &prog_key) != ERROR_SUCCESS)
+ goto error;
+ key_opened = 1;
+ for(enum_index = 0, namelen = MAX_KEY_LEN, classlen = MAX_KEY_LEN;
+ ERROR_SUCCESS == RegEnumKeyEx(prog_key,
+ enum_index,
+ name,
+ &namelen,
+ NULL,
+ class,
+ &classlen,
+ &ft);
+ ++enum_index, namelen = MAX_KEY_LEN, classlen = MAX_KEY_LEN){
+ if(ndx >= res_siz - 1)
+ res = realloc(res, (res_siz += 10)*sizeof(RegEntryDesc));
+ if(!(res[ndx].entries = get_keys(name)))
+ goto error;
+ res[ndx].servicename = strdup(name);
+ res[++ndx].servicename = NULL;
+ }
+ RegCloseKey(prog_key);
+ return res;
+error:
+ if(key_opened)
+ RegCloseKey(prog_key);
+ free_all_keys(res);
+ return NULL;
+}
+
+int register_logkeys(void){
+ HKEY key;
+ DWORD disposition;
+ DWORD types = EVENTLOG_ERROR_TYPE |
+ EVENTLOG_WARNING_TYPE |
+ EVENTLOG_INFORMATION_TYPE;
+ DWORD catcount=1;
+ char filename[2048];
+ DWORD fnsiz=2048;
+
+ if(RegCreateKeyEx(HKEY_LOCAL_MACHINE,
+ LOG_ROOT LOG_APP_KEY, 0,
+ NULL, REG_OPTION_NON_VOLATILE,
+ KEY_SET_VALUE, NULL,
+ &key, &disposition) != ERROR_SUCCESS)
+ return -1;
+ if(!GetModuleFileName(NULL, filename, fnsiz))
+ return -1;
+ if(RegSetValueEx(key, "EventMessageFile",
+ 0, REG_EXPAND_SZ, (LPBYTE) filename,
+ strlen(filename)+1) != ERROR_SUCCESS)
+ return -1;
+ if(RegSetValueEx(key, "TypesSupported",
+ 0, REG_DWORD, (LPBYTE) &types,
+ sizeof(DWORD)) != ERROR_SUCCESS)
+ return -1;
+ return 0;
+}
+
diff --git a/erts/etc/win32/erlsrv/erlsrv_registry.h b/erts/etc/win32/erlsrv/erlsrv_registry.h
new file mode 100644
index 0000000000..fbccc5416a
--- /dev/null
+++ b/erts/etc/win32/erlsrv/erlsrv_registry.h
@@ -0,0 +1,76 @@
+/*
+ * %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%
+ */
+#ifndef _ERLSRV_REGISTRY_H
+#define _ERLSRV_REGISTRY_H
+
+typedef struct _reg_entry {
+ char *name;
+ DWORD type;
+ union {
+ char *bytes;
+ DWORD value;
+ struct {
+ char *bytes;
+ char *unexpanded;
+ } expand;
+ } data;
+} RegEntry;
+
+typedef struct _reg_entry_desc {
+ char *servicename;
+ RegEntry *entries;
+} RegEntryDesc;
+
+enum {
+ StopAction,
+ OnFail,
+ Machine,
+ Env,
+ WorkDir,
+ Priority,
+ SName,
+ Name,
+ Args,
+ DebugType,
+ InternalServiceName,
+ Comment
+};
+
+#define ON_FAIL_IGNORE 0
+#define ON_FAIL_RESTART 1
+#define ON_FAIL_REBOOT 2
+#define ON_FAIL_RESTART_ALWAYS 3
+
+#define DEBUG_TYPE_NO_DEBUG 0
+#define DEBUG_TYPE_NEW 1
+#define DEBUG_TYPE_REUSE 2
+#define DEBUG_TYPE_CONSOLE 3
+
+extern int num_reg_entries;
+
+RegEntry *empty_reg_tab(void);
+void free_keys(RegEntry *keys);
+void free_all_keys(RegEntryDesc *descs);
+RegEntry *get_keys(char *servicename);
+int set_keys(char *servicename, RegEntry *keys);
+RegEntryDesc *get_all_keys(void);
+int remove_keys(char *servicename);
+int register_logkeys(void);
+#endif /* _ERLSRV_REGISTRY_H */
+
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;
+}
+
diff --git a/erts/etc/win32/erlsrv/erlsrv_service.h b/erts/etc/win32/erlsrv/erlsrv_service.h
new file mode 100644
index 0000000000..3eab275836
--- /dev/null
+++ b/erts/etc/win32/erlsrv/erlsrv_service.h
@@ -0,0 +1,32 @@
+/*
+ * %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%
+ */
+#ifndef _ERLSRV_SERVICE_H
+#define _ERLSRV_SERVICE_H
+
+#define CYCLIC_RESTART_LIMIT 10 /* Seconds */
+#define SUCCESS_WAIT_TIME (10*1000) /* Wait 5 s before reporting a service
+ as really started */
+#define NO_SUCCESS_WAIT 0
+#define INITIAL_SUCCESS_WAIT 1
+#define RESTART_SUCCESS_WAIT 2
+
+
+int service_main(int argc, char **argv);
+
+#endif /* _ERLSRV_SERVICE_H */
diff --git a/erts/etc/win32/erlsrv/erlsrv_util.c b/erts/etc/win32/erlsrv/erlsrv_util.c
new file mode 100644
index 0000000000..da3c6f5ef7
--- /dev/null
+++ b/erts/etc/win32/erlsrv/erlsrv_util.c
@@ -0,0 +1,154 @@
+/*
+ * %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_util.h"
+#include "erlsrv_logmess.h"
+
+char *service_name = "";
+char *real_service_name = "";
+
+void log_warning(char *mess){
+ HANDLE logh;
+ char *strings[] = {service_name, mess , NULL};
+
+ if(!(logh = RegisterEventSource(NULL,APP_NAME)))
+ return;
+ ReportEvent(logh, EVENTLOG_WARNING_TYPE, 0, MSG_WARNING,
+ NULL, 2, 0, strings, NULL);
+ DeregisterEventSource(logh);
+}
+
+void log_error(char *mess){
+ HANDLE logh;
+ char *strings[] = {service_name, mess , NULL};
+
+ if(!(logh = RegisterEventSource(NULL,APP_NAME)))
+ return;
+ ReportEvent(logh, EVENTLOG_ERROR_TYPE, 0, MSG_ERROR,
+ NULL, 2, 0, strings, NULL);
+ DeregisterEventSource(logh);
+}
+
+void log_info(char *mess){
+ HANDLE logh;
+ char *strings[] = {service_name, mess , NULL};
+
+ if(!(logh = RegisterEventSource(NULL,APP_NAME)))
+ return;
+ ReportEvent(logh, EVENTLOG_INFORMATION_TYPE, 0, MSG_INFO,
+ NULL, 2, 0, strings, NULL);
+ DeregisterEventSource(logh);
+}
+
+#ifndef NDEBUG
+void log_debug(char *mess){
+ char *buff=malloc(strlen(mess)+100);
+ sprintf(buff,"DEBUG! %s",mess);
+ log_info(buff);
+ free(buff);
+}
+#endif
+
+char *envdup(char *env){
+ char *tmp;
+ int len;
+ for(tmp = env; *tmp != '\0'; tmp += strlen(tmp)+1)
+ ;
+ len = (tmp - env) + 1;
+ if(len == 1)
+ ++len;
+ tmp = malloc(len);
+ memcpy(tmp,env,len);
+ return tmp;
+}
+
+char **env_to_arg(char *env){
+ char **ret;
+ char *tmp;
+ int i;
+ int num_strings = 0;
+ for(tmp = env; *tmp != '\0'; tmp += strlen(tmp)+1)
+ ++num_strings;
+ /* malloc enough to insert ONE string */
+ ret = malloc(sizeof(char *) * (num_strings + 2));
+ i = 0;
+ for(tmp = env; *tmp != '\0'; tmp += strlen(tmp)+1){
+ ret[i++] = strdup(tmp);
+ }
+ ret[i] = NULL;
+ free(env);
+ return ret;
+}
+
+static int compare(const void *a, const void *b){
+ char *s1 = *((char **) a);
+ char *s2 = *((char **) b);
+ char *e1 = strchr(s1,'=');
+ char *e2 = strchr(s2,'=');
+ int ret;
+ int len;
+
+ if(!e1)
+ e1 = s1 + strlen(s1);
+ if(!e2)
+ e2 = s2 + strlen(s2);
+
+ if((e1 - s1) > (e2 - s2))
+ len = (e2 - s2);
+ else
+ len = (e1 - s1);
+
+ ret = _strnicmp(s1,s2,len);
+ if(ret == 0)
+ return ((e1 - s1) - (e2 - s2));
+ else
+ return ret;
+}
+
+char *arg_to_env(char **arg){
+ char *block;
+ char *pek;
+ int i;
+ int totlen = 1; /* extra '\0' */
+
+ for(i=0;arg[i] != NULL;++i)
+ totlen += strlen(arg[i])+1;
+ /* sort the environment vector */
+ qsort(arg,i,sizeof(char *),&compare);
+ if(totlen == 1){
+ block = malloc(2);
+ block[0] = block[1] = '\0';
+ } else {
+ block = malloc(totlen);
+ pek = block;
+ for(i=0; arg[i] != NULL; ++i){
+ strcpy(pek, arg[i]);
+ free(arg[i]);
+ pek += strlen(pek)+1;
+ }
+ *pek = '\0';
+ }
+ free(arg);
+ return block;
+}
diff --git a/erts/etc/win32/erlsrv/erlsrv_util.h b/erts/etc/win32/erlsrv/erlsrv_util.h
new file mode 100644
index 0000000000..b98a6cd3ef
--- /dev/null
+++ b/erts/etc/win32/erlsrv/erlsrv_util.h
@@ -0,0 +1,50 @@
+/*
+ * %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%
+ */
+#ifndef _ERLSRV_UTIL_H
+#define _ERLSRV_UTIL_H
+
+extern char *service_name;
+extern char *real_service_name;
+void log_warning(char *mess);
+void log_error(char *mess);
+void log_info(char *mess);
+
+char *envdup(char *env);
+/*
+** Call before env_to_arg to get a 'freeable' environment block.
+*/
+
+char *arg_to_env(char **arg);
+/*
+** Frees the argument list before returning!
+*/
+
+char **env_to_arg(char *env);
+/*
+** Frees the environment block before returning!
+*/
+
+
+#ifndef NDEBUG
+void log_debug(char *mess);
+#else
+#define log_debug(mess) /* Debug removed */
+#endif
+
+#endif /* _ERLSRV_UTIL_H */