diff options
author | Jeremy Huddleston Sequoia <jeremyhu@apple.com> | 2023-01-18 10:38:41 -0800 |
---|---|---|
committer | Jeremy Huddleston Sequoia <jeremyhu@apple.com> | 2023-01-26 09:56:12 -0800 |
commit | 44d6c82ac82a78d904a6d47387ac363d9699b891 (patch) | |
tree | 77a654c2614a78cf2279a164db297bc02aef0296 | |
parent | 1317083fbc407dc9dbb04ba5b98187b75222a16f (diff) | |
download | xserver-44d6c82ac82a78d904a6d47387ac363d9699b891.tar.gz |
darwin: Implement DetermineClientCmd for macOS
Withoug a proper implementation of DetermineClientCmd, clients that
connect via an ssh tunnel are miscategorized as local. This results
in failures when we try to use SCM_RIGHTS (eg: in MIT-SHM).
Fixes: https://github.com/XQuartz/XQuartz/issues/314
Signed-off-by: Jeremy Huddleston Sequoia <jeremyhu@apple.com>
(cherry picked from commit 0ea9b595891f2f31915538192961f3404d9ca699)
-rw-r--r-- | os/client.c | 110 |
1 files changed, 109 insertions, 1 deletions
diff --git a/os/client.c b/os/client.c index 89a92d5b5..922172cc5 100644 --- a/os/client.c +++ b/os/client.c @@ -73,6 +73,12 @@ #include <limits.h> #endif +#ifdef __APPLE__ +#include <dispatch/dispatch.h> +#include <errno.h> +#include <sys/sysctl.h> +#endif + /** * Try to determine a PID for a client from its connection * information. This should be called only once when new client has @@ -130,9 +136,11 @@ DetermineClientPid(struct _Client * client) void DetermineClientCmd(pid_t pid, const char **cmdname, const char **cmdargs) { +#if !defined(__APPLE__) char path[PATH_MAX + 1]; int totsize = 0; int fd = 0; +#endif if (cmdname) *cmdname = NULL; @@ -142,7 +150,107 @@ DetermineClientCmd(pid_t pid, const char **cmdname, const char **cmdargs) if (pid == -1) return; -#if defined(__OpenBSD__) +#if defined (__APPLE__) + { + static dispatch_once_t once; + static int argmax; + dispatch_once(&once, ^{ + int mib[2]; + size_t len; + + mib[0] = CTL_KERN; + mib[1] = KERN_ARGMAX; + + len = sizeof(argmax); + if (sysctl(mib, 2, &argmax, &len, NULL, 0) == -1) { + ErrorF("Unable to dynamically determine kern.argmax, using ARG_MAX (%d)\n", ARG_MAX); + argmax = ARG_MAX; + } + }); + + int mib[3]; + size_t len = argmax; + int32_t argc = -1; + + char * const procargs = malloc(len); + if (!procargs) { + ErrorF("Failed to allocate memory (%lu bytes) for KERN_PROCARGS2 result for pid %d: %s\n", len, pid, strerror(errno)); + return; + } + + mib[0] = CTL_KERN; + mib[1] = KERN_PROCARGS2; + mib[2] = pid; + + if (sysctl(mib, 3, procargs, &len, NULL, 0) == -1) { + ErrorF("Failed to determine KERN_PROCARGS2 for pid %d: %s\n", pid, strerror(errno)); + free(procargs); + return; + } + + if (len < sizeof(argc) || len > argmax) { + ErrorF("Erroneous length returned when querying KERN_PROCARGS2 for pid %d: %zu\n", pid, len); + free(procargs); + return; + } + + /* Ensure we have a failsafe NUL termination just in case the last entry + * was not actually NUL terminated. + */ + procargs[len-1] = '\0'; + + /* Setup our iterator */ + char *is = procargs; + + /* The first element in the buffer is argc as a 32bit int. When using + * the older KERN_PROCARGS, this is omitted, and one needs to guess + * (usually by checking for an `=` character) when we start seeing + * envvars instead of arguments. + */ + argc = *(int32_t *)is; + is += sizeof(argc); + + /* The very next string is the executable path. Skip over it since + * this function wants to return argv[0] and argv[1...n]. + */ + is += strlen(is) + 1; + + /* Skip over extra NUL characters to get to the start of argv[0] */ + for (; (is < &procargs[len]) && !(*is); is++); + + if (! (is < &procargs[len])) { + ErrorF("Arguments were not returned when querying KERN_PROCARGS2 for pid %d: %zu\n", pid, len); + free(procargs); + return; + } + + if (cmdname) { + *cmdname = strdup(is); + } + + /* Jump over argv[0] and point to argv[1] */ + is += strlen(is) + 1; + + if (cmdargs && is < &procargs[len]) { + char *args = is; + + /* Remove the NUL terminators except the last one */ + for (int i = 1; i < argc - 1; i++) { + /* Advance to the NUL terminator */ + is += strlen(is); + + /* Change the NUL to a space, ensuring we don't accidentally remove the terminal NUL */ + if (is < &procargs[len-1]) { + *is = ' '; + } + } + + *cmdargs = strdup(args); + } + + free(procargs); + } +#elif defined(__OpenBSD__) /* on OpenBSD use kvm_getargv() */ { kvm_t *kd; |