mirror of
				https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
				synced 2025-11-03 09:01:50 -05:00 
			
		
		
		
	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: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/549>
			
			
This commit is contained in:
		
							parent
							
								
									87b4d68978
								
							
						
					
					
						commit
						4f3ca10d9e
					
				
					 4 changed files with 151 additions and 1 deletions
				
			
		| 
						 | 
					@ -60,6 +60,10 @@
 | 
				
			||||||
#include <systemd/sd-daemon.h>
 | 
					#include <systemd/sd-daemon.h>
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef HAVE_WINDOWS_H
 | 
				
			||||||
 | 
					#include <windows.h>
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <pulse/client-conf.h>
 | 
					#include <pulse/client-conf.h>
 | 
				
			||||||
#include <pulse/mainloop.h>
 | 
					#include <pulse/mainloop.h>
 | 
				
			||||||
#include <pulse/mainloop-signal.h>
 | 
					#include <pulse/mainloop-signal.h>
 | 
				
			||||||
| 
						 | 
					@ -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) {
 | 
					static int change_user(void) {
 | 
				
			||||||
    struct passwd *pw;
 | 
					    struct passwd *pw;
 | 
				
			||||||
| 
						 | 
					@ -377,7 +418,45 @@ fail:
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#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[]) {
 | 
					int main(int argc, char *argv[]) {
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
    pa_core *c = NULL;
 | 
					    pa_core *c = NULL;
 | 
				
			||||||
    pa_strbuf *buf = NULL;
 | 
					    pa_strbuf *buf = NULL;
 | 
				
			||||||
    pa_daemon_conf *conf = NULL;
 | 
					    pa_daemon_conf *conf = NULL;
 | 
				
			||||||
| 
						 | 
					@ -402,6 +481,23 @@ int main(int argc, char *argv[]) {
 | 
				
			||||||
    bool start_server;
 | 
					    bool start_server;
 | 
				
			||||||
#endif
 | 
					#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_ident("pulseaudio");
 | 
				
			||||||
    pa_log_set_level(PA_LOG_NOTICE);
 | 
					    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);
 | 
					    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");
 | 
					    sd_notify(0, "READY=1");
 | 
				
			||||||
#endif
 | 
					#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;
 | 
					    retval = 0;
 | 
				
			||||||
    if (pa_mainloop_run(mainloop, &retval) < 0)
 | 
					    if (pa_mainloop_run(mainloop, &retval) < 0)
 | 
				
			||||||
        goto finish;
 | 
					        goto finish;
 | 
				
			||||||
| 
						 | 
					@ -1182,6 +1290,18 @@ int main(int argc, char *argv[]) {
 | 
				
			||||||
    sd_notify(0, "STOPPING=1");
 | 
					    sd_notify(0, "STOPPING=1");
 | 
				
			||||||
#endif
 | 
					#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:
 | 
					finish:
 | 
				
			||||||
#ifdef HAVE_DBUS
 | 
					#ifdef HAVE_DBUS
 | 
				
			||||||
    if (server_bus)
 | 
					    if (server_bus)
 | 
				
			||||||
| 
						 | 
					@ -1249,5 +1369,17 @@ finish:
 | 
				
			||||||
    dbus_shutdown();
 | 
					    dbus_shutdown();
 | 
				
			||||||
#endif
 | 
					#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;
 | 
					    return retval;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1035,7 +1035,14 @@ int pa_context_connect(
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /* The system wide instance via PF_LOCAL */
 | 
					        /* 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);
 | 
					        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 */
 | 
					        /* The user instance via PF_LOCAL */
 | 
				
			||||||
        c->server_list = prepend_per_user(c->server_list);
 | 
					        c->server_list = prepend_per_user(c->server_list);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -83,6 +83,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef HAVE_WINDOWS_H
 | 
					#ifdef HAVE_WINDOWS_H
 | 
				
			||||||
#include <windows.h>
 | 
					#include <windows.h>
 | 
				
			||||||
 | 
					#include <shlobj.h>
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef ENOTSUP
 | 
					#ifndef ENOTSUP
 | 
				
			||||||
| 
						 | 
					@ -171,6 +172,15 @@ char *pa_win32_get_toplevel(HANDLE handle) {
 | 
				
			||||||
    return toplevel;
 | 
					    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
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void set_nonblock(int fd, bool nonblock) {
 | 
					static void set_nonblock(int fd, bool nonblock) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -310,6 +310,7 @@ bool pa_running_in_vm(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef OS_IS_WIN32
 | 
					#ifdef OS_IS_WIN32
 | 
				
			||||||
char *pa_win32_get_toplevel(HANDLE handle);
 | 
					char *pa_win32_get_toplevel(HANDLE handle);
 | 
				
			||||||
 | 
					char *pa_win32_get_system_appdata();
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
size_t pa_page_size(void);
 | 
					size_t pa_page_size(void);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue