# HG changeset patch # User Paper # Date 1700433038 18000 # Node ID e44b7c428d7caadd9b4cab860c74db581f760e76 # Parent 79a2a24453fac9ad46582bd76ada3aa7a12e4db5 dep/animia: add libkvm method (UNTESTED) diff -r 79a2a24453fa -r e44b7c428d7c dep/animia/CMakeLists.txt --- a/dep/animia/CMakeLists.txt Sun Nov 19 05:36:41 2023 -0500 +++ b/dep/animia/CMakeLists.txt Sun Nov 19 17:30:38 2023 -0500 @@ -54,16 +54,15 @@ src/util/win32.cc ) else() # NOT WIN32 AND NOT APPLE + find_library(LIBUTIL_LIBRARY util) + find_library(LIBKVM_LIBRARY kvm) + if(LINUX) list(APPEND DEFINES LINUX) list(APPEND SRC_FILES src/fd/proc.cc ) - endif() # LINUX - - # FreeBSD - find_library(LIBUTIL_LIBRARY util) - if(LIBUTIL_LIBRARY) + elseif(LIBUTIL_LIBRARY) # FreeBSD's libutil get_filename_component(LIBUTIL_DIR ${LIBUTIL_LIBRARY} DIRECTORY) include(CheckLibraryExists) @@ -74,7 +73,18 @@ list(APPEND DEFINES LIBUTIL) list(APPEND SRC_FILES src/fd/libutil.cc) endif() # LIBUTIL_GOOD - endif() # LIBUTIL_LIBRARY + elseif(LIBKVM_LIBRARY) # BSD libkvm + get_filename_component(LIBKVM_DIR ${LIBKVM_LIBRARY} DIRECTORY) + + include(CheckLibraryExists) + check_library_exists(kvm kvm_getprocs ${LIBKVM_DIR} LIBKVM_GOOD) + + if(LIBKVM_GOOD) + list(APPEND LIBRARIES ${LIBKVM_LIBRARY}) + list(APPEND DEFINES LIBKVM) + list(APPEND SRC_FILES src/fd/libkvm.cc) + endif() # LIBUTIL_GOOD + endif() # LINUX # X11 find_package(X11 COMPONENTS X11) diff -r 79a2a24453fa -r e44b7c428d7c dep/animia/src/fd/kvm.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dep/animia/src/fd/kvm.cc Sun Nov 19 17:30:38 2023 -0500 @@ -0,0 +1,134 @@ +/* kvm.cc: provides support for libkvm in multiple BSDs +** +** this is really the only way to get a thing that works on +** OpenBSD AND NetBSD. +** +** Much of this file is taken from the fstat source code in +** NetBSD. +*/ + +#include + +namespace animia::internal::kvm { + +static bool GetFilename(kvm_t* kvm, struct vnode *vp, std::string& name) { + struct vnode vn; + if (!kvm_read(kvm, vp, &vn, sizeof(*vn))) + return 0; + + struct filestat fst; + const char* type = vfilestat(vn, &fst); + if (type == dead) + return false; + + for (DEVS* d = devs; d != NULL; d = d->next) { + if (d->fsid == fst->fsid && d->ino == fst->fileid) { + name = d->name; + break; + } + } + + return true; +} + +static bool GetFilePath(kvm_t* kvm, fdfile_t* fp, std::string& path) { + struct file file; + fdfile_t fdfile; + + if (!kvm_read(kvm, fp, &fdfile, sizeof(fdfile))) + return false; + + if (!fdfile.ff_file) + return false; + + if (!kvm_read(fdfile.ff_file, &file, sizeof(file))) + return false; + + if (file.f_type != DTYPE_VNODE) + return false; + + return GetFilename(kvm, file.f_data, path); +} + +static bool OpenFiles(kvm_t* kvm, struct kinfo_proc* p, open_file_proc_t open_file_proc) { + if (p->proc->p_fd == 0 || p->proc->p_cwdi == 0) + return false; + + struct filedesc filed; + if (!kvm_read(kvm, p->proc->p_fd, &filed, sizeof(filed))) + return false; + + if (filed.fd_lastfile == -1) + return false; + + struct cwdinfo cwdi; + if (!kvm_read(kvm, p->proc->p_cwdi, &cwdi, sizeof(cwdi))) + return false; + + struct fdtab dt; + if (!kvm_read(kvm, filed.fd_dt, &dt, sizeof(dt))) + return false; + + /* check for corrupted files? */ + if ((unsigned)filed.fd_lastfile >= dt.dt_nfiles || filed.fd_freefile > filed.fd_lastfile + 1) + return false; + + /* open files */ + std::unique_ptr ofiles = nullptr; + { + ofiles.reset(malloc((filed.fd_lastfile + 1) * sizeof(fdfile_t*))); + if (!ofiles.get()) + return false; + } + + if (!kvm_read(kvm, &filed.fd_dt->dt_ff, ofiles.get(), filed.fd_lastfile + 1 * (sizeof(fdfile_t*)))) + return false; + + for (int i = 0; i <= filed.fd_lastfile; i++) { + if (!ofiles[i]) + continue; + std::string name; + GetFilePath(kvm, ofiles[i], name); + if (!open_file_proc(p->proc->p_pid, name)) + return false; + } + + return true; +} + +bool EnumerateOpenProcesses(process_proc_t process_proc) { + char errbuf[_POSIX2_LINE_MAX]; + kvm_t* kernel = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); + if (!kernel) + return false; + + int entries = 0; + struct kinfo_proc* kinfo = kvm_getprocs(kernel, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc), &nentries); + if (!kinfo) + return false; + + for (int i = 0; i < entries; i++) + if (!process_proc({kinfo[i].p_pid, kinfo[i].p_comm})) + return false; + + return true; +} + +bool EnumerateOpenFiles(std::set& pids, open_file_proc_t open_file_proc) { + kvm_t* kernel = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); + if (!kernel) + return false; + + for (const auto& pid : pids) { + int cnt; + struct kinfo_proc* kinfo = kvm_getprocs(kernel, KERN_PROC_PID, pid, &cnt); + if (!kinfo) + return false; + + for (int i = 0; i < cnt; i++) { + OpenFiles(kernel, kinfo, open_file_proc); + } + } +} + +} diff -r 79a2a24453fa -r e44b7c428d7c dep/animia/src/fd/win32.cc --- a/dep/animia/src/fd/win32.cc Sun Nov 19 05:36:41 2023 -0500 +++ b/dep/animia/src/fd/win32.cc Sun Nov 19 17:30:38 2023 -0500 @@ -1,8 +1,3 @@ -/* -** win32.cpp -** - provides support for Windows clients -** -*/ #include "animia/fd/win32.h" #include "animia/util/win32.h" #include "animia.h" @@ -24,12 +19,18 @@ #include /* This file is noticably more complex than Unix and Linux, and that's because - there is no "simple" way to get the paths of a file. In fact, this thing requires - you to use *internal functions* that can't even be linked to, hence why we have to - use GetProcAddress and such. What a mess. */ + * there is no "simple" way to get the paths of a file. In fact, this thing requires + * you to use *internal functions* that can't even be linked to, hence why we have to + * use GetProcAddress and such. What a mess. + * + * Speaking of which, because this file uses internal functions of the OS, it is not + * guaranteed to work far into the future. However, it has worked since NT 6.0 (Vista) + * at least, so it's unlikely to be changed much ever. +*/ /* SystemExtendedHandleInformation is only available in NT 5.1+ (XP and higher) and provides information for - 32-bit PIDs, unlike SystemHandleInformation */ + * 32-bit PIDs, unlike SystemHandleInformation +*/ constexpr SYSTEM_INFORMATION_CLASS SystemExtendedHandleInformation = static_cast(0x40); /* more constants not in winternl.h */ diff -r 79a2a24453fa -r e44b7c428d7c dep/animia/src/fd/xnu.cc --- a/dep/animia/src/fd/xnu.cc Sun Nov 19 05:36:41 2023 -0500 +++ b/dep/animia/src/fd/xnu.cc Sun Nov 19 17:30:38 2023 -0500 @@ -1,11 +1,5 @@ -/* -** fd/xnu.cc -** - provides support for XNU (part of Darwin) -*/ #include "animia/fd/xnu.h" -#ifdef HAVE_COREFOUNDATION -# include "animia/util/osx.h" -#endif +#include "animia/util/osx.h" #include "animia.h" #include diff -r 79a2a24453fa -r e44b7c428d7c dep/animia/src/util/osx.cc --- a/dep/animia/src/util/osx.cc Sun Nov 19 05:36:41 2023 -0500 +++ b/dep/animia/src/util/osx.cc Sun Nov 19 17:30:38 2023 -0500 @@ -1,5 +1,8 @@ #include "animia/util/osx.h" +#include +#include + #ifdef HAVE_COREFOUNDATION #include #endif @@ -7,12 +10,20 @@ namespace animia::internal::osx::util { #ifdef HAVE_COREFOUNDATION -/* All of these LaunchServices things use *internal functions* that are subject -** to change. Granted, it's not very likely that these will change very much -** because I'm fairly sure Apple uses them lots in their own internal code. +/* I don't want to have to call CFRelease */ +template +struct CFDeleter { + using pointer = T; + void operator()(pointer p) { CFRelease(p); } +} + +template +typedef CFReference = std::unique_ptr; + +/* all of these LaunchServices things use *internal functions* that are subject + * to change. Granted, it's not very likely that these will change very much + * because I'm fairly sure Apple uses them lots in their own internal code. */ - -/* from RDProcess */ typedef CFTypeRef (*LSASNCreateWithPidSpec)(CFAllocatorRef, pid_t); typedef CFDictionaryRef (*LSCopyApplicationInformationSpec)(int, CFTypeRef, CFArrayRef); @@ -21,6 +32,7 @@ /* retrieved from LaunchServicesSPI.h in WebKit */ static constexpr int kLSDefaultSessionID = -2; + static const CFStringRef kLaunchServicesBundleID = CFSTR("com.apple.LaunchServices"); /* retrieved dynamically */ @@ -56,33 +68,26 @@ if (!GetLaunchServicesPrivateSymbols()) return false; - CFTypeRef asn = LSASNCreateWithPid(kCFAllocatorDefault, pid); - - CFArrayRef request_array = CFArrayCreate(NULL, (const void **)kLSDisplayNameKey, 1, NULL); + CFReference asn = LSASNCreateWithPid(kCFAllocatorDefault, pid); - CFDictionaryRef dictionary = LSCopyApplicationInformation(kLaunchServicesMagicConstant, asn, request_array); + CFReference request_array = CFArrayCreate(NULL, (const void **)kLSDisplayNameKey, 1, NULL); - CFRelease(request_array); - if (!dictionary) + CFReference dictionary = LSCopyApplicationInformation(kLaunchServicesMagicConstant, asn, request_array.get()); + if (!dictionary.get()) return false; - CFStringRef str; - if (!CFDictionaryGetValueIfPresent(dictionary, kLSDisplayNameKey, (CFTypeRef*)&str) || !str) { - CFRelease(dictionary); - return false; - } - CFRetain(str); - - CFRelease(dictionary); - - result.reserve(CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), kCFStringEncodingUTF8) + 1); - - if (!CFStringGetCString(str, &result.front(), result.length(), result.length())) { - CFRelease(str); - return false; + CFReference str; + { + CFStringRef rstr; + if (!CFDictionaryGetValueIfPresent(dictionary, kLSDisplayNameKey, (CFTypeRef*)&rstr) || !rstr) + return false; + str.reset(rstr); } - CFRelease(str); + result.reserve(CFStringGetMaximumSizeForEncoding(CFStringGetLength(str.get()), kCFStringEncodingUTF8) + 1); + + if (!CFStringGetCString(str.get(), &result.front(), result.length(), result.length())) + return false; return true; }