freeprocess.bmx 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. SuperStrict
  2. Rem
  3. bbdoc: System/Execute Processes
  4. End Rem
  5. Module PUB.FreeProcess
  6. ModuleInfo "Version: 1.04"
  7. ModuleInfo "Framework: FreeProcess multi platform external process control"
  8. ModuleInfo "License: zlib/libpng"
  9. ModuleInfo "Copyright: Blitz Research Ltd"
  10. ModuleInfo "Author: Simon Armstrong"
  11. ModuleInfo "Modserver: BRL"
  12. ModuleInfo "History: 1.04 Release"
  13. ModuleInfo "History: Added Documentation, Added Detach and Attach Process Functions"
  14. ModuleInfo "History: 1.03 Release"
  15. ModuleInfo "History: Changed fork() to vfork() and exit() to _exit() to fix more hangs."
  16. ModuleInfo "History: 1.02 Release"
  17. ModuleInfo "History: Fixed a Linux hang when fork() is called."
  18. ModuleInfo "History: Added SIGCHLD handling and fdReapZombies function."
  19. ModuleInfo "History: 1.01 Release"
  20. ModuleInfo "History: Inserts /Contents/MacOS/ into process path for Apple app packages"
  21. ' createproc - to launch external executable
  22. ' TPipeStream - nonblocking readlines with fd file handles
  23. Import brl.stream
  24. Import brl.linkedlist
  25. Import brl.filesystem
  26. Import "freeprocess.c"
  27. 'note: Once fdProcessStatus() returns 0 OR fdTerminateProcess()/fdKillProcess is called,
  28. 'processhandle should be assumed to be invalid, and neither function should be called
  29. 'again.
  30. Extern
  31. Function fdClose(fd:Size_T)
  32. Function fdRead:Long(fd:Size_T,buffer:Byte Ptr,count:Long)
  33. Function fdWrite:Long(fd:Size_T,buffer:Byte Ptr,count:Long)
  34. Function fdFlush(fd:Size_T)
  35. Function fdAvail:Int(fd:Size_T)
  36. Function fdProcess:Int(exe$,in_fd:Size_T Ptr,out_fd:Size_T Ptr,err_fd:Size_T Ptr,flags:Int)="fdProcess"
  37. Function fdProcessStatus:Int(processhandle:Size_T)
  38. Function fdTerminateProcess:Int(processhandle:Size_T)
  39. Function fdKillProcess:Int(processhandle:Size_T)
  40. End Extern
  41. Const HIDECONSOLE:Int=1
  42. Type TPipeStream Extends TStream
  43. Field readbuffer:Byte[4096]
  44. Field bufferpos:Long
  45. Field readhandle:Size_T,writehandle:Size_T
  46. Method Close()
  47. If readhandle
  48. fdClose(readhandle)
  49. readhandle=0
  50. EndIf
  51. If writehandle
  52. fdClose(writehandle)
  53. writehandle=0
  54. EndIf
  55. End Method
  56. Method Read:Long( buf:Byte Ptr,count:Long )
  57. Return fdRead(readhandle,buf,count)
  58. End Method
  59. Method Write:Long( buf:Byte Ptr,count:Long )
  60. Return fdWrite(writehandle,buf,count)
  61. End Method
  62. Method Flush()
  63. fdFlush(writehandle)
  64. End Method
  65. Method ReadAvail:Int()
  66. Return fdAvail(readhandle)
  67. End Method
  68. Method ReadPipe:Byte[]()
  69. Local bytes:Byte[],n:Int
  70. n=ReadAvail()
  71. If n
  72. bytes=New Byte[n]
  73. Read(bytes,n)
  74. Return bytes
  75. EndIf
  76. End Method
  77. Method ReadLine$() 'nonblocking - returns empty string if no data available
  78. Local n:Long,r:Long,p0:Int,p1:Int,line$
  79. n=ReadAvail()
  80. If n
  81. If bufferpos+n>4096 n=4096-bufferpos
  82. If n<=0 RuntimeError "PipeStream ReadBuffer Overflow"
  83. r=Read(Varptr readbuffer[bufferpos],n)
  84. bufferpos:+r
  85. EndIf
  86. For n=0 To bufferpos
  87. If readbuffer[n]=10
  88. p1=n
  89. If (n>0)
  90. If readbuffer[n-1]=13 p1=n-1
  91. EndIf
  92. p0=0
  93. If readbuffer[0]=13 p0=1
  94. If p1>p0 line$=String.FromBytes(Varptr readbuffer[p0],p1-p0)
  95. n:+1
  96. bufferpos:-n
  97. If bufferpos MemMove(readbuffer,Varptr readbuffer[n],Size_T(bufferpos))
  98. Return line$
  99. EndIf
  100. Next
  101. End Method
  102. Function Create:TPipeStream( in:Size_T,out:Size_T )
  103. Local stream:TPipeStream=New TPipeStream
  104. stream.readhandle=in
  105. stream.writehandle=out
  106. Return stream
  107. End Function
  108. End Type
  109. Type TProcess
  110. Global ProcessList:TList
  111. Field name$
  112. Field handle:Size_T
  113. Field pipe:TPipeStream
  114. Field err:TPipeStream
  115. Field detached:Int
  116. Method Detach:Int()
  117. detached = True
  118. Return 1
  119. End Method
  120. Method Attach:Int()
  121. detached = False
  122. Return 1
  123. End Method
  124. Method Status:Int()
  125. If handle
  126. If fdProcessStatus(handle) Return 1
  127. handle=0
  128. EndIf
  129. Return 0
  130. End Method
  131. Method Close()
  132. If pipe pipe.Close;pipe=Null
  133. If err err.Close;err=Null
  134. End Method
  135. Method Terminate:Int()
  136. Local res:Int
  137. If handle
  138. res=fdTerminateProcess( handle )
  139. handle=0
  140. EndIf
  141. Return res
  142. End Method
  143. 'the less nicer version of "terminate()" as it does not allow
  144. 'the process to finish its stuff
  145. Method Kill:Int()
  146. Local res:Int
  147. If handle
  148. res=fdKillProcess( handle )
  149. handle=0
  150. EndIf
  151. Return res
  152. End Method
  153. Function Create:TProcess(name$,flags:Int)
  154. Local p:TProcess
  155. Local infd:Size_T,outfd:Size_T,errfd:Size_T
  156. ?MacOS
  157. If FileType(name)=2
  158. Local a$=StripExt(StripDir(name))
  159. name:+"/Contents/MacOS/"+a$
  160. EndIf
  161. ?
  162. FlushZombies
  163. p=New TProcess
  164. p.name=name
  165. p.handle=fdProcess(p.name,Varptr infd,Varptr outfd,Varptr errfd,flags)
  166. If Not p.handle Return Null
  167. p.pipe=TPipeStream.Create(infd,outfd)
  168. p.err=TPipeStream.Create(errfd,0)
  169. p.detached = False
  170. If Not ProcessList ProcessList=New TList
  171. ProcessList.AddLast p
  172. Return p
  173. End Function
  174. Function FlushZombies()
  175. If Not ProcessList Return
  176. Local live:TList=New TList
  177. For Local p:TProcess=EachIn ProcessList
  178. If p.Status() live.AddLast p
  179. Next
  180. ProcessList=live
  181. End Function
  182. Function TerminateAll() NoDebug
  183. If Not ProcessList Return
  184. For Local p:TProcess=EachIn ProcessList
  185. If p.detached = False Then
  186. p.Terminate
  187. EndIf
  188. Next
  189. ProcessList=Null
  190. End Function
  191. Function KillAll() NoDebug
  192. If Not ProcessList Return
  193. For Local p:TProcess=EachIn ProcessList
  194. If p.detached = False Then
  195. p.Kill
  196. EndIf
  197. Next
  198. ProcessList=Null
  199. End Function
  200. End Type
  201. Rem
  202. bbdoc: Creates a process
  203. returns: TProcess object that is linked to the process you have started
  204. End Rem
  205. Function CreateProcess:TProcess(cmd$,flags:Int=0)
  206. Return TProcess.Create(cmd,flags)
  207. End Function
  208. Rem
  209. bbdoc: Checks status of program
  210. returns: 1 if program is still running and 0 otherwise.
  211. End Rem
  212. Function ProcessStatus:Int(process:TProcess)
  213. Return process.Status()
  214. End Function
  215. Rem
  216. bbdoc: Detaches a process from program
  217. End Rem
  218. Function DetachProcess:Int(process:TProcess)
  219. Return process.Detach()
  220. End Function
  221. Rem
  222. bbdoc: Reattaches a process from program
  223. End Rem
  224. Function AttachProcess:Int(process:TProcess)
  225. Return process.Attach()
  226. End Function
  227. Rem
  228. bbdoc: End Process
  229. returns: 1 if termination of program was successful and 0 otherwise.
  230. End Rem
  231. Function TerminateProcess:Int(process:TProcess)
  232. Return process.Terminate()
  233. End Function
  234. Rem
  235. bbdoc: Forcefully End Process
  236. returns: 1 if forceful termination of program was successful and 0 otherwise.
  237. End Rem
  238. Function KillProcess:Int(process:TProcess)
  239. Return process.Kill()
  240. End Function
  241. OnEnd TProcess.TerminateAll