aboutsummaryrefslogtreecommitdiffstats
path: root/erts/etc/win32/erlsrv/erlsrv_interactive.c
diff options
context:
space:
mode:
Diffstat (limited to 'erts/etc/win32/erlsrv/erlsrv_interactive.c')
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_interactive.c1163
1 files changed, 1163 insertions, 0 deletions
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;
+}
+