From f0ed87278620217d0b881407e04bdbc2966212a1 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Wed, 13 Oct 2021 14:39:52 +0100 Subject: [PATCH] Allow multiple service instances on Windows. --- ChangeLog.txt | 2 + README-windows.txt | 21 +++++++++- src/mosquitto.c | 6 +-- src/mosquitto_broker_internal.h | 6 +-- src/service.c | 72 ++++++++++++++++++++++++++------- 5 files changed, 86 insertions(+), 21 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index ee243c30..6afa03f4 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -82,6 +82,8 @@ Broker: client connects with the same client id. Closes #2340. - Add support for sending the SIGRTMIN signal to trigger log rotation. Closes #2337. +- Allow multiple instances of mosquitto to run as services on Windows. See + README-windows.txt. Client library: - Add MOSQ_OPT_DISABLE_SOCKETPAIR to allow the disabling of the socketpair diff --git a/README-windows.txt b/README-windows.txt index 2bc91c1c..fac92819 100644 --- a/README-windows.txt +++ b/README-windows.txt @@ -21,7 +21,8 @@ start/stop it from the control panel as well as running it as a normal executable. When running as a service, the configuration file used is mosquitto.conf in the -directory that you installed to. +directory defined by the %MOSQUITTO_DIR% environment variable. This will be set +to the directory that you installed to by default. If you want to install/uninstall mosquitto as a Windows service run from the command line as follows: @@ -29,6 +30,24 @@ command line as follows: C:\Program Files\mosquitto\mosquitto install C:\Program Files\mosquitto\mosquitto uninstall +It is possible to install and run multiple instances of a Mosquitto service, as +of version 2.1. To do this, copy the mosquitto executable to a new *name* and +run the service install as above. The service will load a configuration file +mosquitto.conf from the directory defined by the environment variable +"_DIR". For this reason it is suggested to keep the executable +name consisting of alphanumeric and '_' characters. Any other character will be +replaced with '_'. + +For example, if you copy mosquitto.exe to eclipse_mosquitto.exe, you would run +these commands to install/uninstall: + +C:\Program Files\mosquitto\eclipse_mosquitto install +C:\Program Files\mosquitto\eclipse_mosquitto uninstall + +And the service would try to load the config file at %ECLIPSE_MOSQUITTO_DIR%/mosquitto.conf + +The new service will appear in the service list as "Mosquitto Broker (eclipse_mosquitto.exe)". + Logging ------- diff --git a/src/mosquitto.c b/src/mosquitto.c index ecab4587..cd065a83 100644 --- a/src/mosquitto.c +++ b/src/mosquitto.c @@ -225,13 +225,13 @@ int main(int argc, char *argv[]) #if defined(WIN32) || defined(__CYGWIN__) if(argc == 2){ if(!strcmp(argv[1], "run")){ - service_run(); + service_run(argv[0]); return 0; }else if(!strcmp(argv[1], "install")){ - service_install(); + service_install(argv[0]); return 0; }else if(!strcmp(argv[1], "uninstall")){ - service_uninstall(); + service_uninstall(argv[0]); return 0; } } diff --git a/src/mosquitto_broker_internal.h b/src/mosquitto_broker_internal.h index cc2c2afa..8de74d02 100644 --- a/src/mosquitto_broker_internal.h +++ b/src/mosquitto_broker_internal.h @@ -902,9 +902,9 @@ void signal__flag_check(void); * Window service and signal related functions * ============================================================ */ #if defined(WIN32) || defined(__CYGWIN__) -void service_install(void); -void service_uninstall(void); -void service_run(void); +void service_install(char *name); +void service_uninstall(char *name); +void service_run(char *name); DWORD WINAPI SigThreadProc(void* data); #endif diff --git a/src/service.c b/src/service.c index 91a66dd1..31cb6d2b 100644 --- a/src/service.c +++ b/src/service.c @@ -20,8 +20,8 @@ Contributors: #include "config.h" +#include "mosquitto_broker_internal.h" #include - #include "memory_mosq.h" extern int g_run; @@ -29,6 +29,21 @@ SERVICE_STATUS_HANDLE service_handle = 0; static SERVICE_STATUS service_status; int main(int argc, char *argv[]); +static char* fix_name(char* name) +{ + size_t len; + + if (strrchr(name, '\\')) { + name = strrchr(name, '\\') + 1; + } + len = strlen(name); + if(!strcasecmp(&name[len-4], ".exe")){ + name[len-4] = '\0'; + } + + return name; +} + static void print_error(void) { char *buf = NULL; @@ -69,23 +84,39 @@ void __stdcall service_main(DWORD dwArgc, LPTSTR *lpszArgv) int argc = 1; char conf_path[MAX_PATH + 20]; int rc; + char *name; + char env_name[MAX_PATH]; UNUSED(dwArgc); - UNUSED(lpszArgv); - service_handle = RegisterServiceCtrlHandler("mosquitto", service_handler); + name = fix_name(lpszArgv[0]); + snprintf(env_name, sizeof(env_name), "%s_DIR", name); + for(int i=0; i='A' && env_name[i]<='Z') { + /* Keep upper case letter */ + }else if(env_name[i]>='0' && env_name[i]<='9'){ + /* Keep number */ + }else if(env_name[i]>='a' && env_name[i]<='z'){ + /* Convert lower case to upper case */ + env_name[i] -= 32; + }else{ + env_name[i] = '_'; + } + } + + service_handle = RegisterServiceCtrlHandler(name, service_handler); if(service_handle){ memset(conf_path, 0, sizeof(conf_path)); - rc = GetEnvironmentVariable("MOSQUITTO_DIR", conf_path, MAX_PATH); + rc = GetEnvironmentVariable(env_name, conf_path, MAX_PATH); if(!rc || rc == MAX_PATH){ service_status.dwCurrentState = SERVICE_STOPPED; SetServiceStatus(service_handle, &service_status); return; } - strcat(conf_path, "/mosquitto.conf"); + strcat(conf_path, "\\mosquitto.conf"); argv = mosquitto__malloc(sizeof(char *)*3); - argv[0] = "mosquitto"; + argv[0] = name; argv[1] = "-c"; argv[2] = conf_path; argc = 3; @@ -105,11 +136,12 @@ void __stdcall service_main(DWORD dwArgc, LPTSTR *lpszArgv) } } -void service_install(void) +void service_install(char* name) { SC_HANDLE sc_manager, svc_handle; char service_string[MAX_PATH + 20]; char exe_path[MAX_PATH + 1]; + char display_name[MAX_PATH+1]; SERVICE_DESCRIPTION svc_desc; memset(exe_path, 0, sizeof(exe_path)); @@ -119,14 +151,23 @@ void service_install(void) } snprintf(service_string, sizeof(service_string), "\"%s\" run", exe_path); + name = fix_name(name); + sc_manager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE); if(sc_manager){ - svc_handle = CreateService(sc_manager, "mosquitto", "Mosquitto Broker", + if (!strcmp(name, "mosquitto")) { + snprintf(display_name, sizeof(display_name), "Mosquitto Broker"); + }else{ + snprintf(display_name, sizeof(display_name), "Mosquitto Broker (%s.exe)", name); + } + + svc_handle = CreateService(sc_manager, name, display_name, SERVICE_START | SERVICE_STOP | SERVICE_CHANGE_CONFIG, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, service_string, NULL, NULL, NULL, NULL, NULL); if(svc_handle){ + memset(&svc_desc, 0, sizeof(svc_desc)); svc_desc.lpDescription = "Eclipse Mosquitto MQTT v5/v3.1.1 broker"; ChangeServiceConfig2(svc_handle, SERVICE_CONFIG_DESCRIPTION, &svc_desc); CloseServiceHandle(svc_handle); @@ -139,14 +180,16 @@ void service_install(void) } } -void service_uninstall(void) +void service_uninstall(char *name) { SC_HANDLE sc_manager, svc_handle; SERVICE_STATUS status; + name = fix_name(name); + sc_manager = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT); if(sc_manager){ - svc_handle = OpenService(sc_manager, "mosquitto", SERVICE_QUERY_STATUS | DELETE); + svc_handle = OpenService(sc_manager, name, SERVICE_QUERY_STATUS | DELETE); if(svc_handle){ if(QueryServiceStatus(svc_handle, &status)){ if(status.dwCurrentState == SERVICE_STOPPED){ @@ -163,14 +206,15 @@ void service_uninstall(void) } } -void service_run(void) +void service_run(char *name) { SERVICE_TABLE_ENTRY ste[] = { - { "mosquitto", service_main }, + { name, service_main }, { NULL, NULL } }; - + name = fix_name(name); + ste[0].lpServiceName = name; StartServiceCtrlDispatcher(ste); } -#endif +#endif \ No newline at end of file