PageRenderTime 26ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/Languages/IronPython/IronPython.Modules/_subprocess.cs

https://github.com/thomo13/ironruby
C# | 489 lines | 368 code | 77 blank | 44 comment | 40 complexity | 928f68e84a02f27d6282a32b0e989ea1 MD5 | raw file
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Microsoft Public License. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Microsoft Public License, please send an email to
  8. * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Microsoft Public License.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. #if CLR2
  16. using Microsoft.Scripting.Math;
  17. #else
  18. using System.Numerics;
  19. #endif
  20. using System;
  21. using System.Collections.Generic;
  22. using System.Diagnostics.CodeAnalysis;
  23. using System.Text;
  24. using System.Runtime.InteropServices;
  25. using Microsoft.Scripting.Runtime;
  26. using IronPython.Runtime;
  27. using IronPython.Runtime.Exceptions;
  28. using IronPython.Runtime.Operations;
  29. #if !SILVERLIGHT
  30. [assembly: PythonModule("_subprocess", typeof(IronPython.Modules.PythonSubprocess))]
  31. namespace IronPython.Modules {
  32. [PythonType("_subprocess")]
  33. public static class PythonSubprocess {
  34. public const string __doc__ = "_subprocess Module";
  35. #region Public API
  36. public static PythonTuple CreatePipe(
  37. CodeContext context,
  38. object pSec /*Python passes None*/,
  39. int bufferSize) {
  40. IntPtr hReadPipe;
  41. IntPtr hWritePipe;
  42. SECURITY_ATTRIBUTES pSecA = new SECURITY_ATTRIBUTES();
  43. pSecA.nLength = Marshal.SizeOf(pSecA);
  44. if (pSec != null) {
  45. /* If pSec paseed in from Python is not NULL
  46. * there needs to be some conversion done here...*/
  47. }
  48. bool result = CreatePipePI(
  49. out hReadPipe,
  50. out hWritePipe,
  51. ref pSecA,
  52. (uint)bufferSize);
  53. return PythonTuple.MakeTuple(
  54. new PythonSubprocessHandle(hReadPipe),
  55. new PythonSubprocessHandle(hWritePipe)
  56. );
  57. }
  58. public static PythonTuple CreateProcess(
  59. CodeContext context,
  60. string applicationName,
  61. string commandLineArgs,
  62. object pSec /*subprocess.py passes None*/,
  63. object tSec /*subprocess.py passes None*/,
  64. int? bInheritHandles,
  65. uint? dwCreationFlags,
  66. PythonDictionary lpEnvironment,
  67. string lpCurrentDirectory,
  68. object lpStartupInfo /* subprocess.py passes STARTUPINFO*/) {
  69. object dwFlags = PythonOps.GetBoundAttr(context, lpStartupInfo, "dwFlags"); //public Int32 dwFlags;
  70. object hStdInput = PythonOps.GetBoundAttr(context, lpStartupInfo, "hStdInput"); //public IntPtr hStdInput;
  71. object hStdOutput = PythonOps.GetBoundAttr(context, lpStartupInfo, "hStdOutput"); //public IntPtr hStdOutput;
  72. object hStdError = PythonOps.GetBoundAttr(context, lpStartupInfo, "hStdError"); //public IntPtr hStdError;
  73. object wShowWindow = PythonOps.GetBoundAttr(context, lpStartupInfo, "wShowWindow"); //Int16 wShowWindow;
  74. Int32 dwFlagsInt32 = dwFlags != null ? Converter.ConvertToInt32(dwFlags) : 0;
  75. IntPtr hStdInputIntPtr = hStdInput != null ? new IntPtr(Converter.ConvertToInt32(hStdInput)) : IntPtr.Zero;
  76. IntPtr hStdOutputIntPtr = hStdOutput != null ? new IntPtr(Converter.ConvertToInt32(hStdOutput)) : IntPtr.Zero;
  77. IntPtr hStdErrorIntPtr = hStdError != null ? new IntPtr(Converter.ConvertToInt32(hStdError)) : IntPtr.Zero;
  78. Int16 wShowWindowInt16 = wShowWindow != null ? Converter.ConvertToInt16(wShowWindow) : (short)0;
  79. STARTUPINFO startupInfo = new STARTUPINFO();
  80. startupInfo.dwFlags = dwFlagsInt32;
  81. startupInfo.hStdInput = hStdInputIntPtr;
  82. startupInfo.hStdOutput = hStdOutputIntPtr;
  83. startupInfo.hStdError = hStdErrorIntPtr;
  84. startupInfo.wShowWindow = wShowWindowInt16;
  85. // No special security
  86. SECURITY_ATTRIBUTES pSecSA = new SECURITY_ATTRIBUTES();
  87. pSecSA.nLength = Marshal.SizeOf(pSecSA);
  88. SECURITY_ATTRIBUTES tSecSA = new SECURITY_ATTRIBUTES();
  89. tSecSA.nLength = Marshal.SizeOf(tSecSA);
  90. if (pSec != null) {
  91. /* If pSec paseed in from Python is not NULL
  92. * there needs to be some conversion done here...*/
  93. }
  94. if (tSec != null) {
  95. /* If tSec paseed in from Python is not NULL
  96. * there needs to be some conversion done here...*/
  97. }
  98. // If needed convert lpEnvironment Dictonary to lpEnvironmentIntPtr
  99. string lpEnvironmentStr = EnvironmentToNative(lpEnvironment);
  100. PROCESS_INFORMATION lpProcessInformation = new PROCESS_INFORMATION();
  101. bool result = CreateProcessPI(
  102. String.IsNullOrEmpty(applicationName) ? null : applicationName/*applicationNameHelper*//*processStartInfo.FileName*/,
  103. String.IsNullOrEmpty(commandLineArgs) ? null : commandLineArgs/*commandLineArgsHelper*//*processStartInfo.Arguments*/,
  104. ref pSecSA, ref tSecSA,
  105. bInheritHandles.HasValue && bInheritHandles.Value > 0 ? true : false,
  106. dwCreationFlags.HasValue ? dwCreationFlags.Value : 0,
  107. lpEnvironmentStr,
  108. lpCurrentDirectory,
  109. ref startupInfo,
  110. out lpProcessInformation);
  111. if (!result) {
  112. int error = Marshal.GetLastWin32Error();
  113. throw PythonExceptions.CreateThrowable(PythonExceptions.WindowsError, error, CTypes.FormatError(error));
  114. }
  115. IntPtr hp = lpProcessInformation.hProcess;
  116. IntPtr ht = lpProcessInformation.hThread;
  117. int pid = lpProcessInformation.dwProcessId;
  118. int tid = lpProcessInformation.dwThreadId;
  119. return PythonTuple.MakeTuple(
  120. new PythonSubprocessHandle(hp, true),
  121. new PythonSubprocessHandle(ht),
  122. pid, tid);
  123. }
  124. private static string EnvironmentToNative(PythonDictionary lpEnvironment) {
  125. if (lpEnvironment == null) {
  126. return null;
  127. }
  128. StringBuilder res = new StringBuilder();
  129. foreach (var keyValue in lpEnvironment) {
  130. res.Append(keyValue.Key);
  131. res.Append('=');
  132. res.Append(keyValue.Value);
  133. res.Append('\0');
  134. }
  135. return res.ToString();
  136. }
  137. /// <summary>
  138. /// Duplicates a subprocess handle which was created for piping.
  139. ///
  140. /// This is only called when we're duplicating the handle to make it inheritable to the child process. In CPython
  141. /// the parent handle is always reliably garbage collected. Because we know this handle is not going to be
  142. /// used we close the handle being duplicated.
  143. /// </summary>
  144. public static PythonSubprocessHandle DuplicateHandle(CodeContext context,
  145. BigInteger sourceProcess,
  146. PythonSubprocessHandle handle,
  147. BigInteger targetProcess,
  148. int desiredAccess,
  149. bool inherit_handle,
  150. object DUPLICATE_SAME_ACCESS) {
  151. if (handle._duplicated) {
  152. // more ref counting issues - when stderr is set to subprocess.STDOUT we can't close the target handle so we need
  153. // to track this situation.
  154. return DuplicateHandle(context, sourceProcess, (BigInteger)handle, targetProcess, desiredAccess, inherit_handle, DUPLICATE_SAME_ACCESS);
  155. }
  156. var res = DuplicateHandle(context, sourceProcess, (BigInteger)handle, targetProcess, desiredAccess, inherit_handle, DUPLICATE_SAME_ACCESS);
  157. res._duplicated = true;
  158. handle.Close();
  159. return res;
  160. }
  161. public static PythonSubprocessHandle DuplicateHandle(
  162. CodeContext context,
  163. BigInteger sourceProcess,
  164. BigInteger handle,
  165. BigInteger targetProcess,
  166. int desiredAccess,
  167. bool inherit_handle,
  168. object DUPLICATE_SAME_ACCESS) {
  169. IntPtr currentProcessIntPtr = new IntPtr((long)sourceProcess);
  170. IntPtr handleIntPtr = new IntPtr((long)handle);
  171. IntPtr currentProcess2IntPtr = new IntPtr((long)targetProcess);
  172. IntPtr lpTargetHandle;
  173. bool sameAccess = DUPLICATE_SAME_ACCESS != null && Converter.ConvertToBoolean(DUPLICATE_SAME_ACCESS);
  174. bool result = DuplicateHandlePI(
  175. currentProcessIntPtr,
  176. handleIntPtr,
  177. currentProcess2IntPtr,
  178. out lpTargetHandle,
  179. Converter.ConvertToUInt32(desiredAccess),
  180. inherit_handle,
  181. sameAccess ? (uint)DuplicateOptions.DUPLICATE_SAME_ACCESS : (uint)DuplicateOptions.DUPLICATE_CLOSE_SOURCE
  182. );
  183. return new PythonSubprocessHandle(lpTargetHandle); //Converter.ConvertToBigInteger( lpTargetHandle.ToInt32());
  184. }
  185. public static PythonSubprocessHandle GetCurrentProcess() {
  186. IntPtr id = GetCurrentProcessPI();
  187. return new PythonSubprocessHandle(id);
  188. }
  189. public static int GetExitCodeProcess(PythonSubprocessHandle hProcess) {
  190. if (hProcess._isProcess && hProcess._closed) {
  191. // deal with finalization & resurrection oddness... see PythonSubprocessHandle finalizer
  192. return hProcess._exitCode;
  193. }
  194. IntPtr hProcessIntPtr = new IntPtr(Converter.ConvertToInt32(hProcess));
  195. int exitCode = int.MinValue;
  196. var result = GetExitCodeProcessPI(hProcessIntPtr, out exitCode);
  197. return exitCode;
  198. }
  199. public static string GetModuleFileName(object ignored) {
  200. // Alternative Managed API: System.Diagnostics.ProcessModule.FileName or System.Reflection.Module.FullyQualifiedName.
  201. return System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;
  202. }
  203. public static object GetStdHandle(int STD_OUTPUT_HANDLE) {
  204. return GetStdHandlePI(STD_OUTPUT_HANDLE).ToPython();
  205. }
  206. public static int GetVersion() {
  207. return GetVersionPI();
  208. }
  209. public static bool TerminateProcess(
  210. PythonSubprocessHandle handle,
  211. object uExitCode) {
  212. // Alternative Managed API: System.Diagnostics.Process.Kill()
  213. IntPtr hProcessIntPtr = new IntPtr(Converter.ConvertToInt32(handle));
  214. uint uExitCodeUint = Converter.ConvertToUInt32(uExitCode);
  215. bool result = TerminateProcessPI(
  216. hProcessIntPtr,
  217. uExitCodeUint);
  218. return result;
  219. }
  220. public static int WaitForSingleObject(PythonSubprocessHandle handle, int dwMilliseconds) {
  221. return WaitForSingleObjectPI(handle, dwMilliseconds);
  222. }
  223. #endregion
  224. #region struct's and enum's
  225. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
  226. internal struct STARTUPINFO {
  227. public Int32 cb;
  228. public string lpReserved;
  229. public string lpDesktop;
  230. public string lpTitle;
  231. public Int32 dwX;
  232. public Int32 dwY;
  233. public Int32 dwXSize;
  234. public Int32 dwYSize;
  235. public Int32 dwXCountChars;
  236. public Int32 dwYCountChars;
  237. public Int32 dwFillAttribute;
  238. public Int32 dwFlags;
  239. public Int16 wShowWindow;
  240. public Int16 cbReserved2;
  241. public IntPtr lpReserved2;
  242. public IntPtr hStdInput;
  243. public IntPtr hStdOutput;
  244. public IntPtr hStdError;
  245. }
  246. [StructLayout(LayoutKind.Sequential)]
  247. internal struct PROCESS_INFORMATION {
  248. public IntPtr hProcess;
  249. public IntPtr hThread;
  250. public int dwProcessId;
  251. public int dwThreadId;
  252. }
  253. [StructLayout(LayoutKind.Sequential)]
  254. internal /*class*/ struct SECURITY_ATTRIBUTES {
  255. public int nLength;
  256. public IntPtr lpSecurityDescriptor;
  257. public int bInheritHandle;
  258. }
  259. [Flags]
  260. internal enum DuplicateOptions : uint {
  261. DUPLICATE_CLOSE_SOURCE = (0x00000001),// Closes the source handle. This occurs regardless of any error status returned.
  262. DUPLICATE_SAME_ACCESS = (0x00000002), //Ignores the dwDesiredAccess parameter. The duplicate handle has the same access as the source handle.
  263. }
  264. #endregion
  265. #region Privates / PInvokes
  266. [return: MarshalAs(UnmanagedType.Bool)]
  267. [DllImport("kernel32.dll", EntryPoint = "CreateProcess", SetLastError = true)]
  268. private static extern bool CreateProcessPI(string lpApplicationName,
  269. string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes,
  270. ref SECURITY_ATTRIBUTES lpThreadAttributes, [MarshalAs(UnmanagedType.Bool)]bool bInheritHandles,
  271. uint dwCreationFlags, string lpEnvironment, string lpCurrentDirectory,
  272. [In] ref STARTUPINFO lpStartupInfo,
  273. out PROCESS_INFORMATION lpProcessInformation);
  274. [DllImport("kernel32.dll", EntryPoint = "CreatePipe")]
  275. [return: MarshalAs(UnmanagedType.Bool)]
  276. internal static extern bool CreatePipePI(out IntPtr hReadPipe, out IntPtr hWritePipe,
  277. ref SECURITY_ATTRIBUTES lpPipeAttributes, uint nSize);
  278. [DllImport("kernel32.dll", SetLastError = true, EntryPoint = "DuplicateHandle")]
  279. [return: MarshalAs(UnmanagedType.Bool)]
  280. private static extern bool DuplicateHandlePI(IntPtr hSourceProcessHandle,
  281. IntPtr hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr lpTargetHandle,
  282. uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwOptions);
  283. [DllImport("kernel32.dll", EntryPoint = "GetCurrentProcess")]
  284. private static extern IntPtr GetCurrentProcessPI();
  285. [DllImport("kernel32.dll", SetLastError = true, EntryPoint = "GetExitCodeProcess")]
  286. [return: MarshalAs(UnmanagedType.Bool)]
  287. internal static extern bool GetExitCodeProcessPI(IntPtr hProcess, out /*uint*/ int lpExitCode);
  288. [DllImport("kernel32.dll", SetLastError = true, EntryPoint = "GetStdHandle")]
  289. private static extern IntPtr GetStdHandlePI(int nStdHandle);
  290. [DllImport("kernel32.dll", EntryPoint = "GetVersion")]
  291. [SuppressMessage("Microsoft.Usage", "CA2205:UseManagedEquivalentsOfWin32Api")]
  292. private static extern int GetVersionPI();
  293. [return: MarshalAs(UnmanagedType.Bool)]
  294. [DllImport("kernel32.dll", SetLastError = true, EntryPoint = "CloseHandle")]
  295. internal static extern bool CloseHandle(IntPtr hObject);
  296. [DllImport("kernel32.dll", SetLastError = true, EntryPoint = "TerminateProcess")]
  297. [return: MarshalAs(UnmanagedType.Bool)]
  298. private static extern bool TerminateProcessPI(IntPtr hProcess, uint uExitCode);
  299. [DllImport("kernel32", CharSet = CharSet.Ansi,
  300. SetLastError = true, ExactSpelling = true, EntryPoint = "WaitForSingleObject")]
  301. internal static extern int WaitForSingleObjectPI(IntPtr hHandle, int dwMilliseconds);
  302. #endregion
  303. #region Constants
  304. public const int CREATE_NEW_CONSOLE = 16;
  305. public const int CREATE_NEW_PROCESS_GROUP = 512;
  306. public const int DUPLICATE_SAME_ACCESS = 2;
  307. public const int INFINITE = -1;
  308. public const int STARTF_USESHOWWINDOW = 1;
  309. public const int STARTF_USESTDHANDLES = 256;
  310. public const int STD_ERROR_HANDLE = -12;
  311. public const int STD_INPUT_HANDLE = -10;
  312. public const int STD_OUTPUT_HANDLE = -11;
  313. public const int SW_HIDE = 0;
  314. public const int WAIT_OBJECT_0 = 0;
  315. public const int PIPE = -1;
  316. public const int STDOUT = -2;
  317. #endregion
  318. }
  319. [PythonType("_subprocess_handle")]
  320. public class PythonSubprocessHandle {
  321. private readonly IntPtr _internalHandle;
  322. internal bool _closed;
  323. internal bool _duplicated, _isProcess;
  324. internal int _exitCode;
  325. private static List<PythonSubprocessHandle> _active = new List<PythonSubprocessHandle>();
  326. internal PythonSubprocessHandle(IntPtr handle) {
  327. _internalHandle = handle;
  328. }
  329. internal PythonSubprocessHandle(IntPtr handle, bool isProcess) {
  330. _internalHandle = handle;
  331. _isProcess = isProcess;
  332. }
  333. ~PythonSubprocessHandle() {
  334. if (_isProcess) {
  335. lock (_active) {
  336. // we need to deal w/ order of finalization and the fact that Popen will resurrect
  337. // it's self and want to be able to poll us for exit. Therefore we can't close until
  338. // the process has really exited (and we've captured that exit code). So we keep
  339. // resurrecting ourselves until it finally happens.
  340. int insertion = -1;
  341. for (int i = 0; i < _active.Count; i++) {
  342. if (_active[i] == null) {
  343. insertion = i;
  344. } else if (_active[i].PollForExit()) {
  345. _active[i] = null;
  346. insertion = i;
  347. if (_active[i] == this) {
  348. // we've exited, and we're removed from the list
  349. Close();
  350. return;
  351. }
  352. } else if (_active[i] == this) {
  353. // we haven't exited
  354. return;
  355. }
  356. }
  357. // we're not in the list.
  358. if (!PollForExit()) {
  359. // resurrect ourselves - this is to account for subprocess.py's resurrection of
  360. // handles. We cannot close our handle until we have been successfully polled
  361. // for the end of the process.
  362. if (insertion != -1) {
  363. _active[insertion] = this;
  364. } else {
  365. _active.Add(this);
  366. }
  367. return;
  368. } else {
  369. Close();
  370. }
  371. }
  372. }
  373. Close();
  374. }
  375. private bool PollForExit() {
  376. if (PythonSubprocess.WaitForSingleObjectPI(_internalHandle, 0) == PythonSubprocess.WAIT_OBJECT_0) {
  377. PythonSubprocess.GetExitCodeProcessPI(_internalHandle, out _exitCode);
  378. return true;
  379. }
  380. return false;
  381. }
  382. public void Close() {
  383. lock (this) {
  384. if (!_closed) {
  385. PythonSubprocess.CloseHandle(_internalHandle);
  386. _closed = true;
  387. GC.SuppressFinalize(this);
  388. }
  389. }
  390. }
  391. public object Detach(CodeContext context) {
  392. lock (this) {
  393. if (!_closed) {
  394. _closed = true;
  395. GC.SuppressFinalize(this);
  396. return _internalHandle.ToPython();
  397. }
  398. }
  399. return -1;
  400. }
  401. public static implicit operator int(PythonSubprocessHandle type) {
  402. return type._internalHandle.ToInt32(); // ToPython()
  403. }
  404. public static implicit operator BigInteger(PythonSubprocessHandle type) {
  405. return type._internalHandle.ToInt32(); // ToPython()
  406. }
  407. public static implicit operator IntPtr(PythonSubprocessHandle type) {
  408. return type._internalHandle; // ToPython()
  409. }
  410. }
  411. }
  412. #endif