1 // glib/Spawn.cs : Spawn g_spawn API wrapper
2 //
3 // Author: Mike Kestner  <mkestner@novell.com>
4 //
5 // Copyright (c) 2007 Novell, Inc.
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of version 2 of the Lesser GNU General
9 // Public License as published by the Free Software Foundation.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this program; if not, write to the
18 // Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 // Boston, MA 02111-1307, USA.
20 
21 
22 namespace GLib {
23 
24 	using System;
25 	using System.Runtime.InteropServices;
26 
27 	public enum SpawnError {
28 		Fork,
29 		Read,
30 		Chdir,
31 		Acces,
32 		Perm,
33 		TooBig,
34 		NoExec,
35 		NameTooLong,
36 		NoEnt,
37 		NoMem,
38 		NotDir,
39 		Loop,
40 		TxtBusy,
41 		IO,
42 		NFile,
43 		MFile,
44 		Inval,
45 		IsDir,
46 		LibBad,
47 		Failed,
48 	}
49 
50 	[Flags]
51 	public enum SpawnFlags {
52 		LeaveDescriptorsOpen = 1 << 0,
53 		DoNotReapChild         = 1 << 1,
54 		SearchPath             = 1 << 2,
55 		StdoutToDevNull        = 1 << 3,
56 		StderrToDevNull        = 1 << 4,
57 		ChildInheritsStdin     = 1 << 5,
58 		FileAndArgvZero        = 1 << 6,
59 	}
60 
SpawnChildSetupFunc()61 	public delegate void SpawnChildSetupFunc ();
62 
63 	[UnmanagedFunctionPointer (CallingConvention.Cdecl)]
SpawnChildSetupFuncNative(IntPtr gch)64 	internal delegate void SpawnChildSetupFuncNative (IntPtr gch);
65 
66 	internal class SpawnChildSetupWrapper {
67 
68 		SpawnChildSetupFunc handler;
69 
SpawnChildSetupWrapper(SpawnChildSetupFunc handler)70 		public SpawnChildSetupWrapper (SpawnChildSetupFunc handler)
71 		{
72 			if (handler == null)
73 				return;
74 
75 			this.handler = handler;
76 			Data = (IntPtr) GCHandle.Alloc (this);
77 			NativeCallback = new SpawnChildSetupFuncNative (InvokeHandler);
78 		}
79 
80 		public IntPtr Data;
81 		public SpawnChildSetupFuncNative NativeCallback;
82 
InvokeHandler(IntPtr data)83 		static void InvokeHandler (IntPtr data)
84 		{
85 			if (data == IntPtr.Zero)
86 				return;
87 			GCHandle gch = (GCHandle) data;
88 			(gch.Target as SpawnChildSetupWrapper).handler ();
89 			gch.Free ();
90 		}
91 	}
92 
93 	public class Process {
94 
95 		public const int IgnorePipe = Int32.MaxValue;
96 		public const int RequestPipe = 0;
97 
98 		long pid;
99 
Process(int pid)100 		private Process (int pid)
101 		{
102 			this.pid = pid;
103 		}
104 
105 		[DllImport (Global.GLibNativeDll, CallingConvention = CallingConvention.Cdecl)]
g_spawn_close_pid(int pid)106 		static extern void g_spawn_close_pid (int pid);
107 
Close()108 		public void Close ()
109 		{
110 			g_spawn_close_pid ((int) pid);
111 		}
112 
113 		[DllImport (Global.GLibNativeDll, CallingConvention = CallingConvention.Cdecl)]
g_spawn_async(IntPtr dir, IntPtr[] argv, IntPtr[] envp, int flags, SpawnChildSetupFuncNative func, IntPtr data, out int pid, out IntPtr error)114 		static extern bool g_spawn_async (IntPtr dir, IntPtr[] argv, IntPtr[] envp, int flags, SpawnChildSetupFuncNative func, IntPtr data, out int pid, out IntPtr error);
115 
116 		[DllImport (Global.GLibNativeDll)]
g_spawn_async_utf8(IntPtr dir, IntPtr[] argv, IntPtr[] envp, int flags, SpawnChildSetupFuncNative func, IntPtr data, out int pid, out IntPtr error)117 		static extern bool g_spawn_async_utf8 (IntPtr dir, IntPtr[] argv, IntPtr[] envp, int flags, SpawnChildSetupFuncNative func, IntPtr data, out int pid, out IntPtr error);
118 
SpawnAsync(string working_directory, string[] argv, string[] envp, SpawnFlags flags, SpawnChildSetupFunc child_setup, out Process child_process)119 		public static bool SpawnAsync (string working_directory, string[] argv, string[] envp, SpawnFlags flags, SpawnChildSetupFunc child_setup, out Process child_process)
120 		{
121 			int pid;
122 			IntPtr error;
123 			IntPtr native_dir = Marshaller.StringToPtrGStrdup (working_directory);
124 			IntPtr[] native_argv = Marshaller.StringArrayToNullTermPointer (argv);
125 			IntPtr[] native_envp = Marshaller.StringArrayToNullTermPointer (envp);
126 			SpawnChildSetupWrapper wrapper = new SpawnChildSetupWrapper (child_setup);
127 			bool result;
128 
129 			if (Global.IsWindowsPlatform)
130 				result = g_spawn_async_utf8 (native_dir, native_argv, native_envp, (int) flags, wrapper.NativeCallback, wrapper.Data, out pid, out error);
131 			else
132 				result = g_spawn_async (native_dir, native_argv, native_envp, (int) flags, wrapper.NativeCallback, wrapper.Data, out pid, out error);
133 
134 			child_process = new Process (pid);
135 			Marshaller.Free (native_dir);
136 			Marshaller.Free (native_argv);
137 			Marshaller.Free (native_envp);
138 			if (error != IntPtr.Zero) throw new GLib.GException (error);
139 			return result;
140 		}
141 
142 		[DllImport (Global.GLibNativeDll, CallingConvention = CallingConvention.Cdecl)]
g_spawn_async_with_pipes(IntPtr dir, IntPtr[] argv, IntPtr[] envp, int flags, SpawnChildSetupFuncNative func, IntPtr data, out int pid, IntPtr stdin, IntPtr stdout, IntPtr stderr, out IntPtr error)143 		static extern bool g_spawn_async_with_pipes (IntPtr dir, IntPtr[] argv, IntPtr[] envp, int flags, SpawnChildSetupFuncNative func, IntPtr data, out int pid, IntPtr stdin, IntPtr stdout, IntPtr stderr, out IntPtr error);
144 
145 		[DllImport (Global.GLibNativeDll)]
g_spawn_async_with_pipes_utf8(IntPtr dir, IntPtr[] argv, IntPtr[] envp, int flags, SpawnChildSetupFuncNative func, IntPtr data, out int pid, IntPtr stdin, IntPtr stdout, IntPtr stderr, out IntPtr error)146 		static extern bool g_spawn_async_with_pipes_utf8 (IntPtr dir, IntPtr[] argv, IntPtr[] envp, int flags, SpawnChildSetupFuncNative func, IntPtr data, out int pid, IntPtr stdin, IntPtr stdout, IntPtr stderr, out IntPtr error);
147 
SpawnAsyncWithPipes(string working_directory, string[] argv, string[] envp, SpawnFlags flags, SpawnChildSetupFunc child_setup, out Process child_process, ref int stdin, ref int stdout, ref int stderr)148 		public static bool SpawnAsyncWithPipes (string working_directory, string[] argv, string[] envp, SpawnFlags flags, SpawnChildSetupFunc child_setup, out Process child_process, ref int stdin, ref int stdout, ref int stderr)
149 		{
150 			int pid;
151 			IntPtr error;
152 			IntPtr native_dir = Marshaller.StringToPtrGStrdup (working_directory);
153 			IntPtr[] native_argv = Marshaller.StringArrayToNullTermPointer (argv);
154 			IntPtr[] native_envp = Marshaller.StringArrayToNullTermPointer (envp);
155 			SpawnChildSetupWrapper wrapper = new SpawnChildSetupWrapper (child_setup);
156 			IntPtr in_ptr = stdin == IgnorePipe ? IntPtr.Zero : Marshal.AllocHGlobal (4);
157 			IntPtr out_ptr = stdout == IgnorePipe ? IntPtr.Zero : Marshal.AllocHGlobal (4);
158 			IntPtr err_ptr = stderr == IgnorePipe ? IntPtr.Zero : Marshal.AllocHGlobal (4);
159 			bool result;
160 
161 			if (Global.IsWindowsPlatform)
162 				result = g_spawn_async_with_pipes_utf8 (native_dir, native_argv, native_envp, (int) flags, wrapper.NativeCallback, wrapper.Data, out pid, in_ptr, out_ptr, err_ptr, out error);
163 			else
164 				result = g_spawn_async_with_pipes (native_dir, native_argv, native_envp, (int) flags, wrapper.NativeCallback, wrapper.Data, out pid, in_ptr, out_ptr, err_ptr, out error);
165 
166 			child_process = new Process (pid);
167 			if (in_ptr != IntPtr.Zero) {
168 				stdin = Marshal.ReadInt32 (in_ptr);
169 				Marshal.FreeHGlobal (in_ptr);
170 			}
171 			if (out_ptr != IntPtr.Zero) {
172 				stdout = Marshal.ReadInt32 (out_ptr);
173 				Marshal.FreeHGlobal (out_ptr);
174 			}
175 			if (err_ptr != IntPtr.Zero) {
176 				stderr = Marshal.ReadInt32 (err_ptr);
177 				Marshal.FreeHGlobal (err_ptr);
178 			}
179 			Marshaller.Free (native_dir);
180 			Marshaller.Free (native_argv);
181 			Marshaller.Free (native_envp);
182 			if (error != IntPtr.Zero) throw new GLib.GException (error);
183 			return result;
184 		}
185 
186 		[DllImport (Global.GLibNativeDll, CallingConvention = CallingConvention.Cdecl)]
g_spawn_sync(IntPtr dir, IntPtr[] argv, IntPtr[] envp, int flags, SpawnChildSetupFuncNative func, IntPtr data, out IntPtr stdout, out IntPtr stderr, out int exit_status, out IntPtr error)187 		static extern bool g_spawn_sync (IntPtr dir, IntPtr[] argv, IntPtr[] envp, int flags, SpawnChildSetupFuncNative func, IntPtr data, out IntPtr stdout, out IntPtr stderr, out int exit_status, out IntPtr error);
188 
189 		[DllImport (Global.GLibNativeDll)]
g_spawn_sync_utf8(IntPtr dir, IntPtr[] argv, IntPtr[] envp, int flags, SpawnChildSetupFuncNative func, IntPtr data, out IntPtr stdout, out IntPtr stderr, out int exit_status, out IntPtr error)190 		static extern bool g_spawn_sync_utf8 (IntPtr dir, IntPtr[] argv, IntPtr[] envp, int flags, SpawnChildSetupFuncNative func, IntPtr data, out IntPtr stdout, out IntPtr stderr, out int exit_status, out IntPtr error);
191 
SpawnSync(string working_directory, string[] argv, string[] envp, SpawnFlags flags, SpawnChildSetupFunc child_setup, out string stdout, out string stderr, out int exit_status)192 		public static bool SpawnSync (string working_directory, string[] argv, string[] envp, SpawnFlags flags, SpawnChildSetupFunc child_setup, out string stdout, out string stderr, out int exit_status)
193 		{
194 			IntPtr native_stdout, native_stderr, error;
195 			IntPtr native_dir = Marshaller.StringToPtrGStrdup (working_directory);
196 			IntPtr[] native_argv = Marshaller.StringArrayToNullTermPointer (argv);
197 			IntPtr[] native_envp = Marshaller.StringArrayToNullTermPointer (envp);
198 			SpawnChildSetupWrapper wrapper = new SpawnChildSetupWrapper (child_setup);
199 			bool result;
200 
201 			if (Global.IsWindowsPlatform)
202 				result = g_spawn_sync (native_dir, native_argv, native_envp, (int) flags, wrapper.NativeCallback, wrapper.Data, out native_stdout, out native_stderr, out exit_status, out error);
203 			else
204 				result = g_spawn_sync (native_dir, native_argv, native_envp, (int) flags, wrapper.NativeCallback, wrapper.Data, out native_stdout, out native_stderr, out exit_status, out error);
205 
206 			Marshaller.Free (native_dir);
207 			Marshaller.Free (native_argv);
208 			Marshaller.Free (native_envp);
209 			stdout = Marshaller.PtrToStringGFree (native_stdout);
210 			stderr = Marshaller.PtrToStringGFree (native_stderr);
211 			if (error != IntPtr.Zero) throw new GLib.GException (error);
212 			return result;
213 		}
214 
215 		[DllImport (Global.GLibNativeDll, CallingConvention = CallingConvention.Cdecl)]
g_spawn_command_line_async(IntPtr cmdline, out IntPtr error)216 		static extern bool g_spawn_command_line_async (IntPtr cmdline, out IntPtr error);
217 
218 		[DllImport (Global.GLibNativeDll)]
g_spawn_command_line_async_utf8(IntPtr cmdline, out IntPtr error)219 		static extern bool g_spawn_command_line_async_utf8 (IntPtr cmdline, out IntPtr error);
220 
SpawnCommandLineAsync(string command_line)221 		public static bool SpawnCommandLineAsync (string command_line)
222 		{
223 			IntPtr error;
224 			IntPtr native_cmd = Marshaller.StringToPtrGStrdup (command_line);
225 			bool result;
226 
227 			if (Global.IsWindowsPlatform)
228 				result = g_spawn_command_line_async_utf8 (native_cmd, out error);
229 			else
230 				result = g_spawn_command_line_async (native_cmd, out error);
231 
232 			Marshaller.Free (native_cmd);
233 			if (error != IntPtr.Zero) throw new GLib.GException (error);
234 			return result;
235 		}
236 
237 		[DllImport (Global.GLibNativeDll, CallingConvention = CallingConvention.Cdecl)]
g_spawn_command_line_sync(IntPtr cmdline, out IntPtr stdout, out IntPtr stderr, out int exit_status, out IntPtr error)238 		static extern bool g_spawn_command_line_sync (IntPtr cmdline, out IntPtr stdout, out IntPtr stderr, out int exit_status, out IntPtr error);
239 
240 		[DllImport (Global.GLibNativeDll)]
g_spawn_command_line_sync_utf8(IntPtr cmdline, out IntPtr stdout, out IntPtr stderr, out int exit_status, out IntPtr error)241 		static extern bool g_spawn_command_line_sync_utf8 (IntPtr cmdline, out IntPtr stdout, out IntPtr stderr, out int exit_status, out IntPtr error);
242 
SpawnCommandLineSync(string command_line, out string stdout, out string stderr, out int exit_status)243 		public static bool SpawnCommandLineSync (string command_line, out string stdout, out string stderr, out int exit_status)
244 		{
245 			IntPtr error, native_stdout, native_stderr;
246 			IntPtr native_cmd = Marshaller.StringToPtrGStrdup (command_line);
247 			bool result;
248 
249 			if (Global.IsWindowsPlatform)
250 				result = g_spawn_command_line_sync_utf8 (native_cmd, out native_stdout, out native_stderr, out exit_status, out error);
251 			else
252 				result = g_spawn_command_line_sync (native_cmd, out native_stdout, out native_stderr, out exit_status, out error);
253 
254 			Marshaller.Free (native_cmd);
255 			stdout = Marshaller.PtrToStringGFree (native_stdout);
256 			stderr = Marshaller.PtrToStringGFree (native_stderr);
257 			if (error != IntPtr.Zero) throw new GLib.GException (error);
258 			return result;
259 		}
260 	}
261 }
262