From: Patrick Gaskin Date: Sun, 30 May 2021 19:28:30 +0000 (-0400) Subject: daemon: Add support for running as a service on win32 X-Git-Tag: v14.99.2~1 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=4f3ca10d9ec2db1d39b1da4bbc2c84ae5ade0aff;p=platform%2Fupstream%2Fpulseaudio.git daemon: Add support for running as a service on win32 * Minimal implementation of --system on win32. * Wrap main with a Windows Service on win32 (with a fallback to running it directly). * Update PA_SYSTEM_{RUNTIME,STATE,CONFIG}_PATH and HOME dynamically on Windows (overrides the build config, similar to the existing config path replacement logic). Part-of: --- diff --git a/src/daemon/main.c b/src/daemon/main.c index a424d9e..ec1ec3c 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -60,6 +60,10 @@ #include #endif +#ifdef HAVE_WINDOWS_H +#include +#endif + #include #include #include @@ -156,7 +160,44 @@ static void signal_callback(pa_mainloop_api* m, pa_signal_event *e, int sig, voi } } -#if defined(HAVE_PWD_H) && defined(HAVE_GRP_H) + +#if defined(OS_IS_WIN32) + +static int change_user(void) { + pa_log_info("Overriding system runtime/config base dir to '%s'.", pa_win32_get_system_appdata()); + + /* On other platforms, these paths are compiled into PulseAudio. This isn't + * suitable on Windows. Firstly, Windows doesn't follow the FHS or use Unix + * paths and the build system can't handle Windows-style paths properly. + * Secondly, the idiomatic location for a service's state and shared data is + * ProgramData, and the location of special folders is dynamic on Windows. + * Also, this method of handling paths is consistent with how they are + * handled on Windows in other parts of PA. Note that this is only needed + * in system-wide mode since paths in user instances are already handled + * properly. + */ + + char *run_path = pa_sprintf_malloc("%s" PA_PATH_SEP "run", pa_win32_get_system_appdata()); + char *lib_path = pa_sprintf_malloc("%s" PA_PATH_SEP "lib", pa_win32_get_system_appdata()); + + /* TODO: directory ACLs */ + + pa_set_env("HOME", run_path); + if (!getenv("PULSE_RUNTIME_PATH")) + pa_set_env("PULSE_RUNTIME_PATH", run_path); + if (!getenv("PULSE_CONFIG_PATH")) + pa_set_env("PULSE_CONFIG_PATH", lib_path); + if (!getenv("PULSE_STATE_PATH")) + pa_set_env("PULSE_STATE_PATH", lib_path); + + pa_xfree(run_path); + pa_xfree(lib_path); + + pa_log_info("Not changing user for system instance on Windows."); + return 0; +} + +#elif defined(HAVE_PWD_H) && defined(HAVE_GRP_H) static int change_user(void) { struct passwd *pw; @@ -377,7 +418,45 @@ fail: } #endif +#ifdef OS_IS_WIN32 +#define SVC_NAME "PulseAudio" +static bool is_svc = true; +static int argc; +static char **argv; +static int real_main(int s_argc, char *s_argv[]); +static SERVICE_STATUS_HANDLE svc_status; + +DWORD svc_callback(DWORD ctl, DWORD evt, LPVOID data, LPVOID userdata) { + pa_mainloop **m = userdata; + switch (ctl) { + case SERVICE_CONTROL_STOP: + case SERVICE_CONTROL_SHUTDOWN: + if (m) { + pa_log_info("Exiting."); + pa_mainloop_get_api(*m)->quit(pa_mainloop_get_api(*m), 0); + } + return NO_ERROR; + case SERVICE_CONTROL_INTERROGATE: + return NO_ERROR; + } + return ERROR_CALL_NOT_IMPLEMENTED; +} + +int main(int p_argc, char *p_argv[]) { + argc = p_argc; + argv = p_argv; + if (StartServiceCtrlDispatcherA((SERVICE_TABLE_ENTRYA[]){ + {SVC_NAME, (LPSERVICE_MAIN_FUNCTIONA) real_main}, + {0}, + })) return 0; + is_svc = false; + return real_main(0, NULL); +} + +static int real_main(int s_argc, char *s_argv[]) { +#else int main(int argc, char *argv[]) { +#endif pa_core *c = NULL; pa_strbuf *buf = NULL; pa_daemon_conf *conf = NULL; @@ -402,6 +481,23 @@ int main(int argc, char *argv[]) { bool start_server; #endif +#ifdef OS_IS_WIN32 + if (is_svc && !(svc_status = RegisterServiceCtrlHandlerExA(SVC_NAME, (LPHANDLER_FUNCTION_EX) svc_callback, &mainloop))) { + pa_log("Failed to register service control handler."); + goto finish; + } + + if (is_svc) { + SetServiceStatus(svc_status, &(SERVICE_STATUS){ + .dwServiceType = SERVICE_WIN32, + .dwCurrentState = SERVICE_START_PENDING, + .dwControlsAccepted = 0, + .dwWin32ExitCode = NO_ERROR, + .dwWaitHint = 3000, + }); + } +#endif + pa_log_set_ident("pulseaudio"); pa_log_set_level(PA_LOG_NOTICE); pa_log_set_flags(PA_LOG_COLORS|PA_LOG_PRINT_FILE|PA_LOG_PRINT_LEVEL, PA_LOG_RESET); @@ -1172,6 +1268,18 @@ int main(int argc, char *argv[]) { sd_notify(0, "READY=1"); #endif +#ifdef OS_IS_WIN32 + if (is_svc) { + SetServiceStatus(svc_status, &(SERVICE_STATUS){ + .dwServiceType = SERVICE_WIN32, + .dwCurrentState = SERVICE_RUNNING, + .dwControlsAccepted = SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN, + .dwWin32ExitCode = NO_ERROR, + .dwWaitHint = 0, + }); + } +#endif + retval = 0; if (pa_mainloop_run(mainloop, &retval) < 0) goto finish; @@ -1182,6 +1290,18 @@ int main(int argc, char *argv[]) { sd_notify(0, "STOPPING=1"); #endif +#ifdef OS_IS_WIN32 + if (is_svc) { + SetServiceStatus(svc_status, &(SERVICE_STATUS){ + .dwServiceType = SERVICE_WIN32, + .dwCurrentState = SERVICE_STOP_PENDING, + .dwControlsAccepted = 0, + .dwWin32ExitCode = NO_ERROR, + .dwWaitHint = 2000, + }); + } +#endif + finish: #ifdef HAVE_DBUS if (server_bus) @@ -1249,5 +1369,17 @@ finish: dbus_shutdown(); #endif +#ifdef OS_IS_WIN32 + if (is_svc) { + SetServiceStatus(svc_status, &(SERVICE_STATUS){ + .dwServiceType = SERVICE_WIN32, + .dwCurrentState = SERVICE_STOPPED, + .dwControlsAccepted = 0, + .dwWin32ExitCode = retval ? ERROR_PROCESS_ABORTED : NO_ERROR, + .dwWaitHint = 0, + }); + } +#endif + return retval; } diff --git a/src/pulse/context.c b/src/pulse/context.c index b0a8388..e535d04 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -1035,7 +1035,14 @@ int pa_context_connect( } /* The system wide instance via PF_LOCAL */ +#ifndef OS_IS_WIN32 c->server_list = pa_strlist_prepend(c->server_list, PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET); +#else + /* see change_user in src/daemon/main.c */ + char *run_path = pa_sprintf_malloc("%s" PA_PATH_SEP "run" PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET, pa_win32_get_system_appdata()); + c->server_list = pa_strlist_prepend(c->server_list, run_path); + pa_xfree(run_path); +#endif /* The user instance via PF_LOCAL */ c->server_list = prepend_per_user(c->server_list); diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c index 89e37d5..c383a61 100644 --- a/src/pulsecore/core-util.c +++ b/src/pulsecore/core-util.c @@ -83,6 +83,7 @@ #ifdef HAVE_WINDOWS_H #include +#include #endif #ifndef ENOTSUP @@ -171,6 +172,15 @@ char *pa_win32_get_toplevel(HANDLE handle) { return toplevel; } +char *pa_win32_get_system_appdata() { + static char appdata[MAX_PATH] = {0}; + + if (!*appdata && SHGetFolderPathAndSubDirA(NULL, CSIDL_COMMON_APPDATA|CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, "PulseAudio", appdata) != S_OK) + return NULL; + + return appdata; +} + #endif static void set_nonblock(int fd, bool nonblock) { diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h index ed123c7..a69d53b 100644 --- a/src/pulsecore/core-util.h +++ b/src/pulsecore/core-util.h @@ -310,6 +310,7 @@ bool pa_running_in_vm(void); #ifdef OS_IS_WIN32 char *pa_win32_get_toplevel(HANDLE handle); +char *pa_win32_get_system_appdata(); #endif size_t pa_page_size(void);