2 // Mono.Unix/UnixMarshal.cs
5 // Jonathan Pryor (jonpryor@vt.edu)
7 // (C) 2004 Jonathan Pryor
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System.Net.Sockets;
32 using System.Runtime.InteropServices;
33 using System.Runtime.Serialization;
39 // Scenario: We want to be able to translate an Error to a string.
40 // Problem: Thread-safety. Strerror(3) isn't thread safe (unless
41 // thread-local-variables are used, which is probably only
43 // Solution: Use strerror_r().
44 // Problem: strerror_r() isn't portable.
45 // (Apparently Solaris doesn't provide it.)
46 // Solution: Cry. Then introduce an intermediary, ErrorMarshal.
47 // ErrorMarshal exposes a single public delegate, Translator,
48 // which will convert an Error to a string. It's static
49 // constructor first tries using strerror_r(). If it works,
50 // great; use it in the future. If it doesn't work, fallback to
52 // This should be thread safe, since the check is done within the
53 // class constructor lock.
54 // Problem: But if strerror_r() isn't present, we're not thread safe!
55 // Solution: Tough. It's still better than nothing. I think.
56 // Check UnixMarshal.IsErrorDescriptionThreadSafe if you need to
57 // know which error translator is being used.
58 internal class ErrorMarshal
60 public delegate string ErrorTranslator (Error errno);
62 public static readonly ErrorTranslator Translate;
63 public static readonly bool HaveStrerror_r;
65 static ErrorMarshal ()
68 Translate = new ErrorTranslator (strerror_r);
69 string ignore = Translate (Error.EPERM);
70 HaveStrerror_r = true;
72 catch (MissingMethodException e) {
73 Translate = new ErrorTranslator (strerror);
74 HaveStrerror_r = false;
78 private static string strerror (Error errno)
80 return Syscall.strerror (errno);
83 private static string strerror_r (Error errno)
85 StringBuilder buf = new StringBuilder (16);
89 r = Syscall.strerror_r (errno, buf);
90 } while (r == -1 && Syscall.GetLastError() == Error.ERANGE);
93 return "** Unknown error code: " + ((int) errno) + "**";
94 return buf.ToString();
98 public sealed /* static */ class UnixMarshal
100 private UnixMarshal () {}
102 public static string GetErrorDescription (Error errno)
104 return ErrorMarshal.Translate (errno);
107 public static bool IsErrorDescriptionThreadSafe {
108 get {return ErrorMarshal.HaveStrerror_r;}
111 public static IntPtr Alloc (long size)
114 throw new ArgumentOutOfRangeException ("size", "< 0");
115 return Stdlib.malloc ((ulong) size);
118 public static IntPtr ReAlloc (IntPtr ptr, long size)
121 throw new ArgumentOutOfRangeException ("size", "< 0");
122 return Stdlib.realloc (ptr, (ulong) size);
125 public static void Free (IntPtr ptr)
130 public static string PtrToString (IntPtr p)
132 // TODO: deal with character set issues. Will PtrToStringAnsi always
133 // "Do The Right Thing"?
134 if (p == IntPtr.Zero)
136 return Marshal.PtrToStringAnsi (p);
140 * Marshal a C `char **'. ANSI C `main' requirements are assumed:
142 * stringArray is an array of pointers to C strings
143 * stringArray has a terminating NULL string.
146 * stringArray[0] = "string 1";
147 * stringArray[1] = "string 2";
148 * stringArray[2] = NULL
150 * The terminating NULL is required so that we know when to stop looking
153 public static string[] PtrToStringArray (IntPtr stringArray)
155 if (stringArray == IntPtr.Zero)
156 return new string[]{};
158 int argc = CountStrings (stringArray);
159 return PtrToStringArray (argc, stringArray);
162 private static int CountStrings (IntPtr stringArray)
165 IntPtr item = Marshal.ReadIntPtr (stringArray, count * IntPtr.Size);
168 } while (Marshal.ReadIntPtr (stringArray, count * IntPtr.Size) != IntPtr.Zero);
173 * Like PtrToStringArray(IntPtr), but it allows the user to specify how
174 * many strings to look for in the array. As such, the requirement for a
175 * terminating NULL element is not required.
177 * Usage is similar to ANSI C `main': count is argc, stringArray is argv.
178 * stringArray[count] is NOT accessed (though ANSI C requires that
179 * argv[argc] = NULL, which PtrToStringArray(IntPtr) requires).
181 public static string[] PtrToStringArray (int count, IntPtr stringArray)
184 throw new ArgumentOutOfRangeException ("count", "< 0");
185 if (stringArray == IntPtr.Zero)
186 return new string[count];
188 string[] members = new string[count];
189 for (int i = 0; i < count; ++i) {
190 IntPtr s = Marshal.ReadIntPtr (stringArray, i * IntPtr.Size);
191 members[i] = PtrToString (s);
197 public static bool ShouldRetrySyscall (int r)
199 if (r == -1 && Syscall.GetLastError () == Error.EINTR)
204 public static bool ShouldRetrySyscall (int r, out Error error)
207 if (r == -1 && (error = Syscall.GetLastError ()) == Error.EINTR)
212 private static Exception CreateExceptionForError (Error errno)
214 string message = GetErrorDescription (errno);
215 UnixIOException p = new UnixIOException (errno);
217 case Error.EFAULT: return new NullReferenceException (message, p);
218 case Error.EINVAL: return new ArgumentException (message, p);
223 return new IOException (message, p);
224 case Error.ENAMETOOLONG: return new PathTooLongException (message, p);
225 case Error.ENOENT: return new FileNotFoundException (message, p);
226 case Error.ENOEXEC: return new InvalidProgramException (message, p);
227 case Error.EOVERFLOW: return new OverflowException (message, p);
228 case Error.ERANGE: return new ArgumentOutOfRangeException (message);
229 default: /* ignore */ break;
234 public static void ThrowExceptionForError (Error errno)
236 throw CreateExceptionForError (errno);
239 public static void ThrowExceptionForLastError ()
241 throw CreateExceptionForError (Syscall.GetLastError());
244 public static void ThrowExceptionForErrorIf (int retval, Error errno)
247 ThrowExceptionForError (errno);
250 public static void ThrowExceptionForLastErrorIf (int retval)
253 ThrowExceptionForLastError ();