How to get UID and GID on Linux using p/invoke? - c#

I need some simple file operations on a Linux machine, for a service installer.
The code is .NET 5.0.
My current version uses Process.Start() to execute shell commands to change the owner of files and directories and set permissions.
This is quite slow (I use asynchronous methods), especially compared to Windows equivalents.
I see libc accessible to call from .NET has methods chmod and chown, however it wants uid and gid parameters. My application doesn't know the ids, at least without using shell for it.
So far I got something like this:
const string LIBC = "libc";
[DllImport(LIBC, SetLastError = true)]
private static extern int chmod(string path, uint mode);
[DllImport(LIBC, SetLastError = true)]
private static extern int chown(string path, int owner, int group);
So... how to get those 2 ints required?
UPDATE
Why anyone see this question (especially considering its title) as duplicate of question about similar, yet different things.
I know how to change owner and permissions of Linux files in many ways. The easiest way is to use Linux shell. The quickest and easiest way is to use Mono.Posix.NETStandard library, that call libc internally.
My specific question is HOW IT IS MADE? HOW DOES IT WORK?
To be even more specific:
Here's the Linux manual page for getpwnam():
https://man7.org/linux/man-pages/man3/getpwnam.3.html
How to just call it from C# using p/invoke? I see in many examples, that when they replace char* with string it somehow magically works. I created a struct like this:
public struct PASSWD {
public string pw_name; /* username */
public string pw_passwd; /* user password */
public int pw_uid; /* user ID */
public int pw_gid; /* group ID */
public string pw_gecos; /* user information */
public string pw_dir; /* home directory */
public string pw_shell; /* shell program */
};
...and tried to use it as out parameter for the signature.
I get no error, but it just doesn't work. The struct I get is empty.
So again, we are using Platform Invoke, in C#, we are calling libc and we want to get results from a structure. As far as I googled - it's not google-able. There is only Mono source code, that uses external module that implements what I need. I suspect they made it for performance reasons, also - using some special tools, because in comments stands that the code is generated.
My question is again, how, using Linux manual page definition create appropriate method signature for C# to be able to extract those 2 integers from getpwnam().
I also was curious if something like that might already exist in .NET itself, but I guess it doesn't.

So, I got rusty with p/invoke. My issue was I forgot, that when native function returns a pointer to a structure, there is no automatic conversion, I have to leave pointer in signature, so:
[DllImport(LIBC, SetLastError = true)]
internal static extern IntPtr getgrnam(string name);
[DllImport(LIBC, SetLastError = true)]
internal static extern IntPtr getpwnam(string name);
internal struct Group {
public string Name;
public string Password;
public uint Gid;
public IntPtr Members;
}
internal struct Passwd {
public string Name;
public string Password;
public uint Uid;
public uint Gid;
public string GECOS;
public string Directory;
public string Shell;
}
Lets create fully managed .NET style types:
public sealed class GroupInfo {
public string Name { get; }
public uint Id { get; }
public string[] Members { get; }
internal GroupInfo(Syscall.Group group) {
Name = group.Name;
Id = group.Gid;
Members = GetMembers(group.Members).ToArray();
}
private static IEnumerable<string> GetMembers(IntPtr members) {
IntPtr p;
for (int i = 0; (p = Marshal.ReadIntPtr(members, i * IntPtr.Size)) != IntPtr.Zero; i++)
yield return Marshal.PtrToStringAnsi(p)!;
}
}
public class UserInfo {
public string Name { get; }
public uint Uid { get; }
public uint Gid { get; }
public string? Directory { get; }
public string? Shell { get; }
internal UserInfo(Syscall.Passwd passwd) {
Name = passwd.Name;
Uid = passwd.Uid;
Gid = passwd.Gid;
Directory = passwd.Directory;
Shell = passwd.Shell;
}
}
And it can be used like this:
public static UserInfo? GetUserInfo(string name) {
var result = Syscall.getpwnam(name);
if (result != IntPtr.Zero) return new UserInfo(Marshal.PtrToStructure<Syscall.Passwd>(result));
return null;
}
public static GroupInfo? GetGroupInfo(string name) {
var result = Syscall.getgrnam(name);
if (result != IntPtr.Zero) return new GroupInfo(Marshal.PtrToStructure<Syscall.Group>(result));
return null;
}

Related

How can I use SHFileOperation in C#?

I'm trying to move a folder from directory to another directory , but I'm getting an exception of access to path is denied , so I found a c++ SHFileOperation method that can be used in this case, the problem is that I didn't have any idea how to use it:
public void MoveFolder(string oldPath,string newPath)
{
SHFILEOPSTRUCT hFILEOPSTRUCT = new SHFILEOPSTRUCT();
hFILEOPSTRUCT.pFrom = oldPath;
hFILEOPSTRUCT.pTo = newPath;
int res = SHFileOperation(ref hFILEOPSTRUCT);
}
[DllImport("shell32.dll", CharSet = CharSet.Auto, SetLastError = true,
ThrowOnUnmappableChar = true)]
[Obsolete]
static extern int SHFileOperation(ref SHFILEOPSTRUCT lpFileOp);
Struct definition of SHFILEOPSTRUCT:
[Obsolete("Use this element with caution and only if you know what you are doing. It's only meant to be used internally by MahApps.Metro and Fluent.Ribbon. Breaking changes might occur anytime without prior warning.")]
public struct SHFILEOPSTRUCT
{
public IntPtr hwnd;
public FO wFunc;
public string pFrom;
public string pTo;
[CLSCompliant(false)]
public FOF fFlags;
public int fAnyOperationsAborted;
public IntPtr hNameMappings;
public string lpszProgressTitle;
}
}
This code throw an exception
Cannot marshal field 'fAnyOperationsAborted' of type 'ControlzEx.Standard.SHFILEOPSTRUCT': Invalid managed/unmanaged type combination (Int32/UInt32 must be paired with I4, U4, or Error).
How can I make this works?

How to allow only certain parameters in method in c#

is there a way to only allow certain values as parameters in a method. Not at runtime, I mean it already shows an error when coding. For example:
I have this method:
public bool addPoints(Guid userId, uint amount)
{
...
}
It will be called multiple times in the code. But, I only want people to pass certain values. Those values are defined somewhere e.q. a class:
public class LeaderboardPoints
{
public const uint CREATE = 30;
public const uint REPLICATE = 15;
public const uint REMIX = 15;
public const uint COMMENT = 15;
}
Is there a way, that I can force the argument to be one of those props? or is there maybe another way to make sure that the right amount is passed?
Thanks!!
An enum is one option, but enums are just named integers - you can get them wrong and pass any integer (it is just more awkward).
Another way to make it much harder to pass in something wrong is to encapsulate the values in a type, using a non-public constructor to make it impossible (without reflection at least) to create invalid options. For example:
public bool AddPoints(Guid userId, Points points)
{
// something += points.Value
}
//...
public readonly struct Points
{
public uint Value {get;}
private Points(uint value) => Value = value;
public static Points Create {get;} = new Points(30);
public static Points Replicate {get;} = new Points(15);
public static Points Remix {get;} = new Points(15);
public static Points Comment {get;} = new Points(15);
}
// ...
something.AddPoints(userId, Points.Create);
You could also optionally give things names, if it helps debugging.
As others have commented, you can define and use an enum type for this:
public enum LeaderBoardAction : uint
{
CREATE = 30,
REPLICATE = 15,
REMIX = 15,
COMMENT = 15
}
Then use Enum.IsDefined() to ensure no one passes arbitrary values in place of a defined enum label:
public bool AddPoints(Guid userId, LeaderBoardAction action)
{
if(!Enum.IsDefined(typeof(LeaderBoardAction), action))
throw new ArgumentException("Expected a valid leader board action", nameof(action));
// ...
}
or is there maybe another way to make sure that the right amount is passed?
Another approach is to take away the parameter and instead give named methods that they can call; you have just 4 options so it's certainly feasible to:
private bool AddPoints(Guid userId, uint amount)
{
...
}
public bool AddCreatePoints(Guid userId) => AddPoints(userId, LeaderboardPoints.CREATE);
public bool AddReplicatePoints(Guid userId) => AddPoints(userId, LeaderboardPoints.REPLICATE);
public bool AddRemixPoints(Guid userId) => AddPoints(userId, LeaderboardPoints.REMIX);
public bool AddCommentPoints(Guid userId) => AddPoints(userId, LeaderboardPoints.COMMENT);
As with any such things there's always that drag of how much you need to change when adding more options; for my money I would just use an enum and trust developers not to put crazy values in

How to create global variable in c++ that can access multiple files

I used following code to provide a global variable.
namespace STR.Pref
{
public static class Pref
{
public static Lang PrimaryLang { get; set; } = Lang.Sinhala;
public static bool InsTrans { get; set; } = true;
public static HotKey Key { get; set; } = new HotKey();
}
}
So I can use following code to assign value to that global variable (any file).(without instantiating)
private static void SetValue(Pref_tempObj tempObj)
{
Pref.Pref.Key = tempObj.Key;
Pref.Pref.InsTrans = tempObj.InsTrans;
Pref.Pref.PrimaryLang = tempObj.PrimaryLang;
}
The program works perfectly,But recently I was interested in c++ so I made a decision to write that program in c++/clr with the small update. I'm bit new to c++ and I don't understand how can I accomplish this using c++.(whether using pointer or something else)
Ok, I really not recommend that but you can do following this instructions.
On a Global.h file (I mean a header will be include on every .cpp)
extern MyType MyTypeVar;
Declare on a .cpp
MyType MyTypeVar
And on every .cpp that you will include the Global.h have access to MyTypeVar.
See:
When to use extern in C++
Extern
What is the function of extern

Accessing Objective-C structure from Monotouch while binding to iOS static library

I have some 3rd Party iOS Static Library which has .h files containing const struct:
struct SomeStruct
{
__unsafe_unretained NSString * const FirstName;
__unsafe_unretained NSString * const SecondName;
__unsafe_unretained NSString * const ThirdName;
};
extern const struct SomeStruct someName;
I bound this static library to MonoTouch, but I did not understand how to replicate this in C# and access the string value from that structure.
In MonoTouch iOS Binding Project
public struct SomeStruct
{
public string FirstName;
public string SecondName;
public string ThirdName;
};
[Static]
public interface SomeInterface
{
[Field ("SomeStruct", "__Internal")]
IntPtr someNameStr { get; }
}
I included the dll generated by iOS binding Project in my demo application
To access the structure
public static SomeStruct Name {
get {
if (SomeInterface.someNameStr != IntPtr.Zero) {
return (SomeStruct)Marshal.PtrToStructure<SomeStruct> (SomeInterface.someNameStr);
}
return new SomeStruct ();
}
}
Here I am able to get the proper IntPtr. But I am unable to convert it into Structure and access the members in the structure.
SomeInterface.Name.FirstName;
So, could someone please help me in doing it.
Thanks in advance.
You want to use the methods in Dlfcn to get the address of the someName address. Use dlopen to open the library and dlsym to fetch the address.
Then you can marshal the result using Marshal.PtrToStructure but you must make sure that the values are IntPtr, like this:
public struct SomeStruct
{
IntPtr _FirstName;
IntPtr _SecondName;
IntPtr _ThirdName;
};
To get the strings, then you do:
public string FirstName {
get {
return (string) (new NSString (_FirstName));
}
}

Calling un-managed code with pointer

I have a C# project that makes calls out to an unmanaged C++ dll. The wrapper and most of the calls are working OK, so I know that I have the basic structure of how everything ties together OK, but there is one specific call that is giving me fits. The API call requires a pointer to a structure, which contains a list of configuration data.
Here is the call:
m_status = m_XXXXBox.SetConfig(m_channelId, ref SCONFIG_LIST);
Where SCONFIG_LIST is the structure containing the data...
The issue specifically relates to SCONFIG_LIST
Here is the documentation directly from the spec for this API :
Points to the structure SCONFIG_LIST, which is defined as follows:
typedef struct
{
unsigned long NumOfParams; /* number of SCONFIG elements */
SCONFIG *ConfigPtr; /* array of SCONFIG */
} SCONFIG_LIST
where:
NumOfParms is an INPUT, which contains the number of SCONFIG elements in the array
pointed to by ConfigPtr.
ConfigPtr is a pointer to an array of SCONFIG structures.
The structure SCONFIG is defined as follows:
typedef struct
{
unsigned long Parameter; /* name of parameter */
unsigned long Value; /* value of the parameter */
} SCONFIG
Here are the 2 structures that I defined in C#:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public unsafe struct SConfig
{
public int Parameter;
public int Value;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public unsafe struct SConfig_List
{
public int NumOfParams;
// public List<SConfig> sconfig = new List<SConfig>(); // This throws compile time error
public List<SConfig> sconfig;
}
I know you can not have field initializers in structs, but I cant seem to figure out how to initialize the sconfig in the struct externally...
Here is snippet from the calling method
SConfig_List myConfig = new SConfig_List();
SConfig configData = new SConfig();
configData.Parameter = 0x04;
configData.Value = 0x10;
myConfig.NumOfParams = 1;
myConfig.sconfig.Add(configData);
This throws an error at runtime of "object reference not set to an instance of an object"
I understand this error because sconfig has not been initialized - I just cant figure out how to do that....
So my next thought was to get around this, I would just create the SCONFIG_LIST struct like this (without the list inside) - My reasoning for this is that I now do not have to initialize the object, and I could just make multiple calls to dll with a NumOfParams = 1, rather than NumOfParams > 1 and having the dll loop through the struct data.
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public unsafe struct SConfig_List
{
public int NumOfParams;
public SConfig sconfig;
}
And here is how I called the method
configData.Parameter = 0x04;
configData.Value = 0x10;
myConfig.NumOfParams = 1;
myConfig.sconfig.Parameter = configData.Parameter;
myConfig.sconfig.Value = configData.Value;
m_status = m_XXXXBox.SetConfig(m_channelId, ref myConfig);
This got rid of the errors to this point, now on the the actual method that calls the dll
There are still several questions / issues surrounding the Marshalling, but here it is:
public XXXXErr SetConfig(int channelId, ref SConfig_List config)
{
unsafe
{
IntPtr output = IntPtr.Zero;
IntPtr input = Marshal.AllocHGlobal(Marshal.SizeOf(config));
Marshal.StructureToPtr(config, input, true);
XXXXErr returnVal = (XXXXErr)m_wrapper.Ioctl(channelId, (int)Ioctl.SET_CONFIG, input, output);
return returnVal;
}
}
This gets past all of the initial setup without error, but when I try to actually invoke the dll I get an error : Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
I know this is a mouthful, and I really don't even know exactly what to ask as I am sure there are multiple issues within this post, but any ideas on getting me on the right track?
I have tried so many things at this point I am at a loss, and I just need some direction. I am not looking for a "do this for me" type answer, but rather an explanation and maybe some pointers on getting this done. As with all things, I am sure there are multiple ways of accomplishing the task - maybe a way that works, but is not good form, and a longer more complicated way that may be "better practice"
Any and all suggestions / comments will be greatly appreciated. And if I excluded any relevant data that is required to help me solve this riddle let me know and I will provide what I can.
I want to thank the responses so far.
I have been trying every combination to try and resolve this myself but I have not had any luck so far. I have found quite a few ways that do NOT work, however :-)
I have tried various combinations of "unsafe" - "MarshalAs", "StructLayout" and several other things I found on the web, now I am begging for mercy.
I have successfully implemented several other calls to this unmanaged dll, but all of them use simple integer pointers etc. My problem is passing the pointer to a Struct containing an array of another struct. If you look at the very top of my original question you can see the documentation from the dll and how it wants thing structured. There is NO return value, I am merely trying to pass some configuration settings to a device through this dll.
I am going to post a framework of my entire project so that maybe I can get someone to hold my hand through this process, and hopefully help others in the future trying to solve this type of issue as well.
Here is skeleton of Wrapper (not all functions displayed)
using System;
using System.Runtime.InteropServices;
namespace My_Project
{
internal static class NativeMethods
{
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32.dll")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
[DllImport("kernel32.dll")]
public static extern bool FreeLibrary(IntPtr hModule);
}
internal class APIDllWrapper
{
private IntPtr m_pDll;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int APIIoctl(int channelId, int ioctlID, IntPtr input, IntPtr output);
public APIIoctl Ioctl;
//extern ā€œCā€ long WINAPI APIIoctl
//(
//unsigned long ChannelID,
//unsigned long IoctlID,
//void *pInput,
//void *pOutput
//)
public bool LoadAPILibrary(string path)
{
m_pDll = NativeMethods.LoadLibrary(path);
if (m_pDll == IntPtr.Zero)
return false;
pAddressOfFunctionToCall = NativeMethods.GetProcAddress(m_pDll, "APIIoctl");
if (pAddressOfFunctionToCall != IntPtr.Zero)
Ioctl = (APIIoctl)Marshal.GetDelegateForFunctionPointer(
pAddressOfFunctionToCall,
typeof(APIIoctl));
return true;
}
public bool FreeLibrary()
{
return NativeMethods.FreeLibrary(m_pDll);
}
}
}
And Here is the class that defines the hardware I am trying to communicate with
namespace My_Project
{
public class APIDevice
{
public string Vendor { get; set; }
public string Name { get; set; }
public override string ToString()
{
return Name;
}
}
}
Interface
using System.Collections.Generic;
namespace My_Project
{
public interface I_API
{
APIErr SetConfig(int channelId, ref SConfig_List config);
}
}
The actual Class containing the API Code - this is where the error is, I know that how I have the IntPtrs now is Not correct - But this displays what I am trying to do
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace My_Project
{
public class API : I_API
{
private APIDevice m_device;
private APIDllWrapper m_wrapper;
public APIErr SetConfig(int channelId, ref SConfig_List config)
{
IntPtr output = IntPtr.Zero;
IntPtr input = Marshal.AllocHGlobal(Marshal.SizeOf(config));
Marshal.StructureToPtr(config, input, true);
APIErr returnVal = (APIErr)m_wrapper.Ioctl(channelId, (int)Ioctl.SET_CONFIG, input, output);
return returnVal;
}
}
}
Here is class containing the definitions for the Structs I am using
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace My_Project
{
public enum APIErr
{
STATUS_NOERROR = 0x00,
ERR_BUFFER_EMPTY = 0x10,
ERR_BUFFER_FULL = 0x11,
ERR_BUFFER_OVERFLOW = 0x12
}
public struct SConfig
{
public int Parameter;
public int Value;
}
public struct SConfig_List
{
public int NumOfParams;
public SConfig[] sconfig;
public SConfig_List(List<SConfig> param)
{
this.NumOfParams = param.Count;
this.sconfig = new SConfig[param.Count];
param.CopyTo(this.sconfig);
}
}
}
And finally - the actual application calling the dll through the wrapper
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using My_Project;
namespace Test_API
{
public class Comm
{
private I_API m_APIBox;
APIErr m_status;
int m_channelId;
bool m_isConnected;
public Comm(I_API apiInterface)
{
m_APIBox = apiInterface;
m_isConnected = false;
m_status = APIErr.STATUS_NOERROR;
}
public bool ConfigureDevice()
{
SConfig tempConfig = new SConfig();
tempConfig.Parameter = 0x04;
tempConfig.Value = 0x10;
SConfig_List setConfig = new SConfig_List(tempConfig);
m_status = m_APIBox.SetConfig(m_channelId, ref setConfig);
if (m_status != APIErr.STATUS_NOERROR)
{
m_APIBox.Disconnect(m_channelId);
return false;
}
return true;
}
}
}
You cannot marshal List<>, it must be an array. An array is already marshaled as a pointer so you don't have to do anything special. Go easy on the Pack, no need for the unsafe keyword.
You could add a constructor to the struct to make it easy to initialize it from a List<>. Like this:
[StructLayout(LayoutKind.Sequential)]
public struct SConfig {
public int Parameter;
public int Value;
}
[StructLayout(LayoutKind.Sequential)]
public struct SConfig_List {
public int NumOfParams;
public SConfig[] sconfig;
public SConfig_List(List<SConfig> param) {
this.NumOfParams = param.Count;
this.sconfig = new SConfig[param.Count];
param.CopyTo(this.sconfig);
}
}
To initialize list you just have to add line:
myConfig.sconfig = new List<SConfig>()
before you start adding elements into it.
I started another thread because I was asking the wrong question due to my inexperience
The working solution is here
Marshal array of struct and IntPtr
Thanks for the help
-Lee

Categories

Resources