process.inc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. {
  2. This file is part of the Free Component Library (FCL)
  3. Copyright (c) 1999-2008 by the Free Pascal development team
  4. See the file COPYING.FPC, included in this distribution,
  5. for details about the copyright.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  9. **********************************************************************}
  10. {$DEFINE OS_HASEXITCODE}
  11. uses
  12. ctypes,
  13. UnixType,
  14. Unix,
  15. Baseunix;
  16. Resourcestring
  17. SNoCommandLine = 'Cannot execute empty command-line';
  18. SErrNoSuchProgram = 'Executable not found: "%s"';
  19. SErrNoTerminalProgram = 'Could not detect X-Terminal program';
  20. SErrCannotFork = 'Failed to Fork process';
  21. SErrCannotCreatePipes = 'Failed to create pipes';
  22. Const
  23. PriorityConstants : Array [TProcessPriority] of Integer =
  24. (20,20,0,-20);
  25. Const
  26. GeometryOption : String = '-geometry';
  27. TitleOption : String ='-title';
  28. procedure TProcess.CloseProcessHandles;
  29. begin
  30. // Do nothing. Win32 call.
  31. end;
  32. Function TProcess.GetExitCode : Integer;
  33. begin
  34. if (Not Running) and wifexited(FExitCode) then
  35. Result:=wexitstatus(FExitCode)
  36. else
  37. Result:=0;
  38. end;
  39. Function TProcess.PeekExitStatus : Boolean;
  40. var
  41. res: cint;
  42. begin
  43. repeat
  44. res:=fpWaitPid(Handle,pcint(@FExitCode),WNOHANG);
  45. until (res<>-1) or (fpgeterrno<>ESysEINTR);
  46. result:=res=Handle;
  47. If Not Result then
  48. FexitCode:=cardinal(-1); // was 0, better testable for abnormal exit.
  49. end;
  50. Type
  51. TPCharArray = Array[Word] of pchar;
  52. PPCharArray = ^TPcharArray;
  53. Function StringsToPCharList(List : TStrings) : PPChar;
  54. Var
  55. I : Integer;
  56. S : String;
  57. begin
  58. I:=(List.Count)+1;
  59. GetMem(Result,I*sizeOf(PChar));
  60. PPCharArray(Result)^[List.Count]:=Nil;
  61. For I:=0 to List.Count-1 do
  62. begin
  63. S:=List[i];
  64. Result[i]:=StrNew(PChar(S));
  65. end;
  66. end;
  67. Procedure FreePCharList(List : PPChar);
  68. Var
  69. I : integer;
  70. begin
  71. I:=0;
  72. While List[i]<>Nil do
  73. begin
  74. StrDispose(List[i]);
  75. Inc(I);
  76. end;
  77. FreeMem(List);
  78. end;
  79. Function DetectXterm : String;
  80. Function TestTerminal(S : String) : Boolean;
  81. begin
  82. Result:=FileSearch(s,GetEnvironmentVariable('PATH'),False)<>'';
  83. If Result then
  84. XTermProgram:=S;
  85. end;
  86. Function TestTerminals(Terminals : Array of String) : Boolean;
  87. Var
  88. I : integer;
  89. begin
  90. I:=Low(Terminals);
  91. Result:=False;
  92. While (Not Result) and (I<=High(Terminals)) do
  93. begin
  94. Result:=TestTerminal(Terminals[i]);
  95. inc(i);
  96. end;
  97. end;
  98. Const
  99. Konsole = 'konsole';
  100. GNomeTerm = 'gnome-terminal';
  101. DefaultTerminals : Array [1..6] of string
  102. = ('x-terminal-emulator','xterm','aterm','wterm','rxvt','xfce4-terminal');
  103. Var
  104. D :String;
  105. begin
  106. If (XTermProgram='') then
  107. begin
  108. // try predefined
  109. If Length(TryTerminals)>0 then
  110. TestTerminals(TryTerminals);
  111. // try session-specific terminal
  112. if (XTermProgram='') then
  113. begin
  114. D:=LowerCase(GetEnvironmentVariable('DESKTOP_SESSION'));
  115. If (Pos('kde',D)<>0) then
  116. begin
  117. TestTerminal('konsole');
  118. end
  119. else if (D='gnome') then
  120. begin
  121. TestTerminal('gnome-terminal');
  122. end
  123. else if (D='windowmaker') then
  124. begin
  125. If not TestTerminal('aterm') then
  126. TestTerminal('wterm');
  127. end
  128. else if (D='xfce') then
  129. TestTerminal('xfce4-terminal');
  130. end;
  131. if (XTermProgram='') then
  132. TestTerminals(DefaultTerminals)
  133. end;
  134. Result:=XTermProgram;
  135. If (Result='') then
  136. Raise EProcess.Create(SErrNoTerminalProgram);
  137. end;
  138. Function MakeCommand(P : TProcess) : PPchar;
  139. {$ifdef darwin}
  140. Const
  141. TerminalApp = 'open';
  142. {$endif}
  143. {$ifdef haiku}
  144. Const
  145. TerminalApp = 'Terminal';
  146. {$endif}
  147. Var
  148. Cmd : String;
  149. S : TStringList;
  150. G : String;
  151. begin
  152. If (P.ApplicationName='') and (P.CommandLine='') and (P.Executable='') then
  153. Raise EProcess.Create(SNoCommandline);
  154. S:=TStringList.Create;
  155. try
  156. if (P.ApplicationName='') and (P.CommandLine='') then
  157. begin
  158. S.Assign(P.Parameters);
  159. S.Insert(0,P.Executable);
  160. end
  161. else
  162. begin
  163. If (P.CommandLine='') then
  164. Cmd:=P.ApplicationName
  165. else
  166. Cmd:=P.CommandLine;
  167. CommandToList(Cmd,S);
  168. end;
  169. if poNewConsole in P.Options then
  170. begin
  171. {$ifdef haiku}
  172. If (P.ApplicationName<>'') then
  173. begin
  174. S.Insert(0,P.ApplicationName);
  175. S.Insert(0,'--title');
  176. end;
  177. {$endif}
  178. {$if defined(darwin) or defined(haiku)}
  179. S.Insert(0,TerminalApp);
  180. {$else}
  181. S.Insert(0,'-e');
  182. If (P.ApplicationName<>'') then
  183. begin
  184. S.Insert(0,P.ApplicationName);
  185. S.Insert(0,'-title');
  186. end;
  187. if suoUseCountChars in P.StartupOptions then
  188. begin
  189. S.Insert(0,Format('%dx%d',[P.dwXCountChars,P.dwYCountChars]));
  190. S.Insert(0,'-geometry');
  191. end;
  192. If (P.XTermProgram<>'') then
  193. S.Insert(0,P.XTermProgram)
  194. else
  195. S.Insert(0,DetectXterm);
  196. {$endif}
  197. end;
  198. {$ifndef haiku}
  199. if (P.ApplicationName<>'') then
  200. begin
  201. S.Add(TitleOption);
  202. S.Add(P.ApplicationName);
  203. end;
  204. G:='';
  205. if (suoUseSize in P.StartupOptions) then
  206. g:=format('%dx%d',[P.dwXSize,P.dwYsize]);
  207. if (suoUsePosition in P.StartupOptions) then
  208. g:=g+Format('+%d+%d',[P.dwX,P.dwY]);
  209. if G<>'' then
  210. begin
  211. S.Add(GeometryOption);
  212. S.Add(g);
  213. end;
  214. {$endif}
  215. Result:=StringsToPcharList(S);
  216. Finally
  217. S.free;
  218. end;
  219. end;
  220. Function GetLastError : Integer;
  221. begin
  222. Result:=-1;
  223. end;
  224. Type
  225. TPipeEnd = (peRead,peWrite);
  226. TPipePair = Array[TPipeEnd] of cint;
  227. Procedure CreatePipes(Var HI,HO,HE : TPipePair; CE : Boolean);
  228. Procedure CreatePair(Var P : TPipePair);
  229. begin
  230. If not CreatePipeHandles(P[peRead],P[peWrite]) then
  231. Raise EProcess.Create(SErrCannotCreatePipes);
  232. end;
  233. Procedure ClosePair(Var P : TPipePair);
  234. begin
  235. if (P[peRead]<>-1) then
  236. FileClose(P[peRead]);
  237. if (P[peWrite]<>-1) then
  238. FileClose(P[peWrite]);
  239. end;
  240. begin
  241. HO[peRead]:=-1;HO[peWrite]:=-1;
  242. HI[peRead]:=-1;HI[peWrite]:=-1;
  243. HE[peRead]:=-1;HE[peWrite]:=-1;
  244. Try
  245. CreatePair(HO);
  246. CreatePair(HI);
  247. If CE then
  248. CreatePair(HE);
  249. except
  250. ClosePair(HO);
  251. ClosePair(HI);
  252. If CE then
  253. ClosePair(HE);
  254. Raise;
  255. end;
  256. end;
  257. Function safefpdup2(fildes, fildes2 : cInt): cInt;
  258. begin
  259. repeat
  260. safefpdup2:=fpdup2(fildes,fildes2);
  261. until (safefpdup2<>-1) or (fpgeterrno<>ESysEINTR);
  262. end;
  263. Procedure TProcess.Execute;
  264. Var
  265. HI,HO,HE : TPipePair;
  266. PID : Longint;
  267. FEnv : PPChar;
  268. Argv : PPChar;
  269. fd : Integer;
  270. res : cint;
  271. FoundName,
  272. PName : String;
  273. begin
  274. If (poUsePipes in FProcessOptions) then
  275. CreatePipes(HI,HO,HE,Not (poStdErrToOutPut in FProcessOptions));
  276. Try
  277. if FEnvironment.Count<>0 then
  278. FEnv:=StringsToPcharList(FEnvironment)
  279. else
  280. FEnv:=Nil;
  281. Try
  282. Argv:=MakeCommand(Self);
  283. Try
  284. If (Argv<>Nil) and (ArgV[0]<>Nil) then
  285. PName:=StrPas(Argv[0])
  286. else
  287. begin
  288. // This should never happen, actually.
  289. PName:=ApplicationName;
  290. If (PName='') then
  291. PName:=CommandLine;
  292. end;
  293. if not FileExists(PName) then begin
  294. FoundName := ExeSearch(Pname,fpgetenv('PATH'));
  295. if FoundName<>'' then
  296. PName:=FoundName
  297. else
  298. raise EProcess.CreateFmt(SErrNoSuchProgram,[PName]);
  299. end;
  300. {$if (defined(DARWIN) or defined(SUNOS))}
  301. { can't use vfork in case the child has to be
  302. suspended immediately, because with vfork the
  303. child borrows the execution thread of the parent
  304. unit it either exits or execs -> potential
  305. deadlock depending on how quickly the SIGSTOP
  306. signal is delivered }
  307. if not(poRunSuspended in Options) then
  308. Pid:=fpvfork
  309. else
  310. Pid:=fpfork;
  311. {$else}
  312. Pid:=fpfork;
  313. {$endif}
  314. if Pid<0 then
  315. Raise EProcess.Create(SErrCannotFork);
  316. if (PID>0) then
  317. begin
  318. // Parent process. Copy process information.
  319. FProcessHandle:=PID;
  320. FThreadHandle:=PID;
  321. FProcessId:=PID;
  322. //FThreadId:=PID;
  323. end
  324. else
  325. begin
  326. { We're in the child }
  327. if (FCurrentDirectory<>'') then
  328. ChDir(FCurrentDirectory);
  329. if PoUsePipes in Options then
  330. begin
  331. FileClose(HI[peWrite]);
  332. safefpdup2(HI[peRead],0);
  333. FileClose(HO[peRead]);
  334. safefpdup2(HO[peWrite],1);
  335. if (poStdErrToOutPut in Options) then
  336. safefpdup2(HO[peWrite],2)
  337. else
  338. begin
  339. FileClose(HE[peRead]);
  340. safefpdup2(HE[peWrite],2);
  341. end
  342. end
  343. else if poNoConsole in Options then
  344. begin
  345. fd:=FileOpen('/dev/null',fmOpenReadWrite or fmShareDenyNone);
  346. safefpdup2(fd,0);
  347. safefpdup2(fd,1);
  348. safefpdup2(fd,2);
  349. end;
  350. if Assigned(FForkEvent) then
  351. FForkEvent(Self);
  352. if (poRunSuspended in Options) then
  353. sigraise(SIGSTOP);
  354. if FEnv<>Nil then
  355. fpexecve(PName,Argv,Fenv)
  356. else
  357. fpexecv(PName,argv);
  358. fpExit(127);
  359. end
  360. Finally
  361. FreePcharList(Argv);
  362. end;
  363. Finally
  364. If (FEnv<>Nil) then
  365. FreePCharList(FEnv);
  366. end;
  367. Finally
  368. if POUsePipes in FProcessOptions then
  369. begin
  370. FileClose(HO[peWrite]);
  371. FileClose(HI[peRead]);
  372. if Not (poStdErrToOutPut in FProcessOptions) then
  373. FileClose(HE[peWrite]);
  374. CreateStreams(HI[peWrite],HO[peRead],HE[peRead]);
  375. end;
  376. end;
  377. FRunning:=True;
  378. if not (csDesigning in ComponentState) and // This would hang the IDE !
  379. (poWaitOnExit in FProcessOptions) and
  380. not (poRunSuspended in FProcessOptions) then
  381. WaitOnExit;
  382. end;
  383. Function TProcess.WaitOnExit : Boolean;
  384. Var
  385. R : Dword;
  386. begin
  387. if FRunning then
  388. fexitcode:=waitprocess(handle);
  389. Result:=(fexitcode>=0);
  390. FRunning:=False;
  391. end;
  392. Function TProcess.Suspend : Longint;
  393. begin
  394. If fpkill(Handle,SIGSTOP)<>0 then
  395. Result:=-1
  396. else
  397. Result:=1;
  398. end;
  399. Function TProcess.Resume : LongInt;
  400. begin
  401. If fpKill(Handle,SIGCONT)<>0 then
  402. Result:=-1
  403. else
  404. Result:=0;
  405. end;
  406. Function TProcess.Terminate(AExitCode : Integer) : Boolean;
  407. begin
  408. Result:=False;
  409. Result:=fpkill(Handle,SIGTERM)=0;
  410. If Result then
  411. begin
  412. If Running then
  413. Result:=fpkill(Handle,SIGKILL)=0;
  414. end;
  415. { the fact that the signal has been sent does not
  416. mean that the process has already handled the
  417. signal -> wait instead of calling getexitstatus }
  418. if Result then
  419. WaitOnExit;
  420. end;
  421. Procedure TProcess.SetShowWindow (Value : TShowWindowOptions);
  422. begin
  423. FShowWindow:=Value;
  424. end;