Skip to content

Migrate libpsl-native to managed/libc P/Invoke#27668

Draft
adityapatwardhan wants to merge 5 commits into
PowerShell:masterfrom
adityapatwardhan:ReplaceLibPSLNative
Draft

Migrate libpsl-native to managed/libc P/Invoke#27668
adityapatwardhan wants to merge 5 commits into
PowerShell:masterfrom
adityapatwardhan:ReplaceLibPSLNative

Conversation

@adityapatwardhan

Copy link
Copy Markdown
Member

Replace the low-risk libpsl-native functions (those not requiring stat/passwd struct marshalling) with pure managed code and direct LibraryImport("libc") calls, following the engine/Interop/Windows convention with a parallel engine/Interop/Unix folder.

Migrated:

  • GetErrorCategory -> managed errno->ErrorCategory table
  • CreateSymLink/CreateHardLink -> libc symlink/link
  • IsExecutable -> libc access(path, X_OK)
  • KillProcess -> libc kill(pid, SIGKILL)
  • WaitPid -> libc waitpid
  • GetCurrentThreadId -> libc gettid (Linux) / pthread_threadid_np (macOS)
  • SetDate -> libc settimeofday (via DateTimeOffset; drops UnixTm struct)
  • Syslog Open/SysLog/Close -> libc openlog/syslog/closelog

Interop.Unix is compiled only on non-Windows (per csproj), so the consuming bodies in CorePsPlatform are guarded with #if UNIX. The remaining libpsl-native imports (stat, passwd/group, GetUserFromPid, ForkAndExecProcess) are deferred to later phases.

Verified: Windows build and cross-compiled linux-x64 build both pass.

PR Summary

PR Context

PR Checklist

Replace the low-risk libpsl-native functions (those not requiring
stat/passwd struct marshalling) with pure managed code and direct
LibraryImport("libc") calls, following the engine/Interop/Windows
convention with a parallel engine/Interop/Unix folder.

Migrated:
- GetErrorCategory -> managed errno->ErrorCategory table
- CreateSymLink/CreateHardLink -> libc symlink/link
- IsExecutable -> libc access(path, X_OK)
- KillProcess -> libc kill(pid, SIGKILL)
- WaitPid -> libc waitpid
- GetCurrentThreadId -> libc gettid (Linux) / pthread_threadid_np (macOS)
- SetDate -> libc settimeofday (via DateTimeOffset; drops UnixTm struct)
- Syslog Open/SysLog/Close -> libc openlog/syslog/closelog

Interop.Unix is compiled only on non-Windows (per csproj), so the
consuming bodies in CorePsPlatform are guarded with #if UNIX. The
remaining libpsl-native imports (stat, passwd/group, GetUserFromPid,
ForkAndExecProcess) are deferred to later phases.

Verified: Windows build and cross-compiled linux-x64 build both pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

adityapatwardhan and others added 4 commits July 9, 2026 10:14
… managed libc

Replace remaining libpsl-native P/Invokes with direct libc calls:

- Stat.cs: architecture-independent Linux statx(2) and macOS 64-bit-inode
  stat/lstat, exposed as a normalized StatInfo. Wires GetCommonStat,
  GetCommonLStat, GetLinkCount, GetInodeData, IsSameFileSystemItem.
- UserGroup.cs: getpwuid_r/getgrgid_r name resolution (GetPwUid, GetGrGid).
- Process.cs: macOS proc_pidinfo helpers for GetPPid and GetUserFromPid.
- CorePsPlatform.cs: all remaining psLib imports removed; migrated methods
  guarded with #if UNIX and #else PlatformNotSupportedException stubs so the
  Windows build (which excludes engine/Interop/Unix) still compiles.

Compile-verified on Windows and cross-compiled linux-x64. Runtime testing on
Linux/macOS still required (struct offsets, macOS symbol dispatch).

Only ForkAndExecProcess (Phase 3) still uses libpsl-native.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Phase 1 syslog migration used POSIX syslog() on all Unix platforms per the
migration plan's simplicity recommendation. On macOS this broke Logging.Tests:
POSIX syslog() does not set the os_log subsystem/category, so 'log show' entries
never match the test's "category -eq $logId" filter and no items are returned.

Restore the native (libpsl-native nativesyslog.cpp) behavior on macOS by logging
through the unified logging system:

- os_log_create("com.microsoft.powershell", ident) sets subsystem + category.
- _os_log_impl(...) is invoked with a hand-built argument buffer equivalent to
  what the os_log compiler macro emits for a single "%{public}s" argument.
- Severity is mapped to os_log_type_t (fault/error/debug/default) matching the
  native switch. Linux continues to use POSIX syslog().

NOTE: os_log is normally a compiler-macro API; the _os_log_impl + DSO/format-string
path is compile-verified only (host is Windows). macOS CI (Logging.Tests.ps1) must
confirm 'log show' renders the eventMessage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…uture phase

POSIX syslog() is not a valid substitute for os_log on macOS: Host/Logging.Tests.ps1
reads entries via 'log show --style json' and filters on the os_log category (= logId)
and messageType "Default". syslog() sets no category, so no entries match.

A pure-managed os_log (os_log_create + hand-rolled _os_log_impl) compiles and sets the
category correctly, but 'log show' renders a compose failure because os_log requires the
format string to be a compile-time constant located in a Mach-O __TEXT,__os_log section;
a runtime/heap format string is not resolvable by logd. This is why mature FFI bindings
ship a small C shim.

Interim resolution:
- Linux: managed libc syslog (unchanged).
- macOS: route OpenLog/SysLog/CloseLog back to libpsl-native
  Native_OpenLog/Native_SysLog/Native_CloseLog.

A dedicated macOS os_log shim (compiled during the macOS build with a literal
"%{public}s" format) is documented as a future migration phase; it will let the
libpsl-native logging dependency be removed on macOS as well.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…Phase 3)

Replace the libpsl-native ForkAndExecProcess P/Invoke used by SSH based
remoting with a pure-managed implementation over libc posix_spawn(3).

- Add engine/Interop/Unix/Spawn.cs: Interop.Unix.SpawnProcess sets up
  close-on-exec pipes for the requested stdin/stdout/stderr redirections,
  builds posix_spawn file actions (adddup2, addchdir_np) and attributes,
  and spawns the child. posix_spawn is used instead of a hand-rolled
  fork/execve because running managed code between fork and exec is unsafe.
- Preserve SUPPRESS_PROCESS_SIGINT semantics via POSIX_SPAWN_SETSID so the
  SSH child runs in its own session and terminal Ctrl+C (SIGINT) does not
  propagate to it (flag value differs per OS: Linux 0x0080, macOS 0x0400).
- RunspaceConnectionInfo.StartSSHProcess now calls Interop.Unix.SpawnProcess;
  remove the now-dead CreateProcess, ForkAndExecProcess DllImport,
  AllocNullTerminatedArray, FreeArray and the SUPPRESS_PROCESS_SIGINT const.

This removes the last real [DllImport("libpsl-native")] P/Invoke; only the
macOS os_log syslog path remains (deferred to a future phase).

Compile-verified on Windows and cross-compiled linux-x64. Not runtime-tested;
SSH remoting startup, stream redirection, and Ctrl+C isolation should be
validated on Linux and macOS.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
private static partial int Close(int fd);

[LibraryImport("libc", EntryPoint = "posix_spawn", StringMarshalling = StringMarshalling.Utf8)]
private static unsafe partial int PosixSpawn(out int pid, string path, IntPtr fileActions, IntPtr attr, byte** argv, byte** envp);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now in .Net 11 we have StartDetached

Implementation is used PosixSpawn

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants