[pulseaudio-commits] [Git][pulseaudio/pulseaudio][master] 2 commits: daemon: Add support for running as a service on win32

PulseAudio Marge Bot (@pulseaudio-merge-bot) gitlab at gitlab.freedesktop.org
Wed Jun 16 09:20:40 UTC 2021



PulseAudio Marge Bot pushed to branch master at PulseAudio / pulseaudio


Commits:
4f3ca10d by Patrick Gaskin at 2021-06-16T09:17:27+00: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>

- - - - -
ce962563 by Patrick Gaskin at 2021-06-16T09:17:27+00:00
win32: Add DACLs for directories created by system daemon

Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/549>

- - - - -


4 changed files:

- src/daemon/main.c
- src/pulse/context.c
- src/pulsecore/core-util.c
- src/pulsecore/core-util.h


Changes:

=====================================
src/daemon/main.c
=====================================
@@ -60,6 +60,12 @@
 #include <systemd/sd-daemon.h>
 #endif
 
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#include <aclapi.h>
+#include <sddl.h>
+#endif
+
 #include <pulse/client-conf.h>
 #include <pulse/mainloop.h>
 #include <pulse/mainloop-signal.h>
@@ -156,7 +162,95 @@ 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());
+
+    /* https://docs.microsoft.com/en-us/windows/win32/secauthz/ace-strings */
+    /* https://docs.microsoft.com/en-us/windows/win32/secauthz/modifying-the-acls-of-an-object-in-c-- */
+    /* https://docs.microsoft.com/en-us/windows/win32/api/sddl/nf-sddl-convertstringsecuritydescriptortosecuritydescriptora */
+    {
+        mkdir(run_path);
+        PSECURITY_DESCRIPTOR sd;
+        if (ConvertStringSecurityDescriptorToSecurityDescriptorA(
+            "D:PAI"                   /* DACL, disable inheritance from parent, enable propagation to children */
+            "(A;OICI;FA;;;SY)"        /* give system full access */
+            "(A;OICI;FA;;;CO)"        /* give owner full access */
+            "(A;OICI;FA;;;BA)"        /* give administrators full access */
+            "(A;OICI;0x1200a9;;;WD)", /* give everyone read/write/execute access */
+            SDDL_REVISION_1, &sd, NULL
+        )) {
+            PACL acl;
+            BOOL acl_present, acl_default;
+            if (GetSecurityDescriptorDacl(sd, &acl_present, &acl, &acl_default)) {
+                if (SetNamedSecurityInfo(run_path, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION, NULL, NULL, acl, NULL) != ERROR_SUCCESS) {
+                    pa_log_warn("Failed to set DACL for runtime dir: failed to apply DACL: error %lu.", GetLastError());
+                }
+                LocalFree(acl);
+            } else {
+                pa_log_warn("Failed to set DACL for runtime dir: failed to get security descriptor DACL: error %lu.", GetLastError());
+            }
+        } else {
+            pa_log_warn("Failed to set DACL for runtime dir: failed to parse security descriptor: error %lu.", GetLastError());
+        }
+    }
+    {
+        mkdir(lib_path);
+        PSECURITY_DESCRIPTOR sd;
+        if (ConvertStringSecurityDescriptorToSecurityDescriptorA(
+            "D:PAI"             /* DACL, disable inheritance from parent, enable propagation to children */
+            "(A;OICI;FA;;;SY)"  /* give system full access */
+            "(A;OICI;FA;;;CO)"  /* give owner full access */
+            "(A;OICI;FA;;;BA)", /* give administrators full access */
+            SDDL_REVISION_1, &sd, NULL
+        )) {
+            PACL acl;
+            BOOL acl_present, acl_default;
+            if (GetSecurityDescriptorDacl(sd, &acl_present, &acl, &acl_default)) {
+                if (SetNamedSecurityInfo(lib_path, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION, NULL, NULL, acl, NULL) != ERROR_SUCCESS) {
+                    pa_log_warn("Failed to set DACL for lib dir: failed to apply DACL: error %lu.", GetLastError());
+                }
+                LocalFree(acl);
+            } else {
+                pa_log_warn("Failed to set DACL for lib dir: failed to get security descriptor DACL: error %lu.", GetLastError());
+            }
+        } else {
+            pa_log_warn("Failed to set DACL for lib dir: failed to parse security descriptor: error %lu.", GetLastError());
+        }
+    }
+
+    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 +471,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 +534,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 +1321,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 +1343,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 +1422,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;
 }


=====================================
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);


=====================================
src/pulsecore/core-util.c
=====================================
@@ -83,6 +83,7 @@
 
 #ifdef HAVE_WINDOWS_H
 #include <windows.h>
+#include <shlobj.h>
 #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) {


=====================================
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);



View it on GitLab: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/compare/87b4d68978fc144b15a0b6746ee8126869f85f1c...ce962563eba7ee18ad4641f700d097adbea6b6d7

-- 
View it on GitLab: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/compare/87b4d68978fc144b15a0b6746ee8126869f85f1c...ce962563eba7ee18ad4641f700d097adbea6b6d7
You're receiving this email because of your account on gitlab.freedesktop.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.freedesktop.org/archives/pulseaudio-commits/attachments/20210616/02294cac/attachment-0001.htm>


More information about the pulseaudio-commits mailing list