如何从 C# 中的 libc p/invoke getpwnam()?
How to p/invoke getpwnam() from libc in C#?
让我们从文档开始:
https://man7.org/linux/man-pages/man3/getpwnam.3.html
有了这个,我编写了以下 C# 代码:
using System;
using System.Runtime.InteropServices;
if (args.Length < 1) {
Console.Error.WriteLine("Provide user name.");
Environment.Exit(-1);
}
var name = args[0];
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) {
Syscall.getpwnam(name, out var passwd);
Console.WriteLine($"User = {name}, UID = {passwd.Uid}, GID = {passwd.Gid}");
passwd = GetPasswd(name);
Console.WriteLine($"User = {name}, UID = {passwd.Uid}, GID = {passwd.Gid}");
}
else {
Console.WriteLine("It supposed to be run on Linux.");
}
static Passwd GetPasswd(string name) {
var bufsize = 16384;
var buf = new byte[bufsize];
var passwd = new Passwd();
Syscall.getpwnam_r(name, passwd, buf, (uint)bufsize, out var result);
return result;
}
public struct Passwd {
public string Name;
public string Password;
public uint Uid;
public uint Gid;
public string Gecos;
public string Directory;
public string Shell;
}
static class Syscall {
[DllImport("libc", SetLastError = true)]
public static extern void getpwnam(string name, out Passwd passwd);
[DllImport("libc", SetLastError = true)]
public static extern void getpwnam_r(string name, Passwd passwd, byte[] buf, uint bufsize, out Passwd result);
}
没用。
这是我得到的:
User = service, UID = 0, GID = 0
Segmentation fault (core dumped)
我做错了什么?
我应该如何调用它才能得到实际的结构?我对返回的字符串不感兴趣。我只关心 Uid
和 Gid
值。
如链接文档所述 - 此函数接受一个参数 - 名称和 returns 指向包含数据的结构的指针。所以签名应该是:
[DllImport("libc", SetLastError = true)]
public static extern IntPtr getpwnam(string name);
然后:
// we have pointer here
var passwdPtr = Syscall.getpwnam(name);
// don't forget to check if pointer is not IntPtr.Zero.
// interpret data at pointer as structure
var passwd = Marshal.PtrToStructure<Passwd>(passwdPtr);
Console.WriteLine($"User = {passwd.Name}, UID = {passwd.Uid}, GID = {passwd.Gid}");
让我们从文档开始: https://man7.org/linux/man-pages/man3/getpwnam.3.html
有了这个,我编写了以下 C# 代码:
using System;
using System.Runtime.InteropServices;
if (args.Length < 1) {
Console.Error.WriteLine("Provide user name.");
Environment.Exit(-1);
}
var name = args[0];
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) {
Syscall.getpwnam(name, out var passwd);
Console.WriteLine($"User = {name}, UID = {passwd.Uid}, GID = {passwd.Gid}");
passwd = GetPasswd(name);
Console.WriteLine($"User = {name}, UID = {passwd.Uid}, GID = {passwd.Gid}");
}
else {
Console.WriteLine("It supposed to be run on Linux.");
}
static Passwd GetPasswd(string name) {
var bufsize = 16384;
var buf = new byte[bufsize];
var passwd = new Passwd();
Syscall.getpwnam_r(name, passwd, buf, (uint)bufsize, out var result);
return result;
}
public struct Passwd {
public string Name;
public string Password;
public uint Uid;
public uint Gid;
public string Gecos;
public string Directory;
public string Shell;
}
static class Syscall {
[DllImport("libc", SetLastError = true)]
public static extern void getpwnam(string name, out Passwd passwd);
[DllImport("libc", SetLastError = true)]
public static extern void getpwnam_r(string name, Passwd passwd, byte[] buf, uint bufsize, out Passwd result);
}
没用。
这是我得到的:
User = service, UID = 0, GID = 0
Segmentation fault (core dumped)
我做错了什么?
我应该如何调用它才能得到实际的结构?我对返回的字符串不感兴趣。我只关心 Uid
和 Gid
值。
如链接文档所述 - 此函数接受一个参数 - 名称和 returns 指向包含数据的结构的指针。所以签名应该是:
[DllImport("libc", SetLastError = true)]
public static extern IntPtr getpwnam(string name);
然后:
// we have pointer here
var passwdPtr = Syscall.getpwnam(name);
// don't forget to check if pointer is not IntPtr.Zero.
// interpret data at pointer as structure
var passwd = Marshal.PtrToStructure<Passwd>(passwdPtr);
Console.WriteLine($"User = {passwd.Name}, UID = {passwd.Uid}, GID = {passwd.Gid}");