freeprocess.bmx 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  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. ?win32
  37. Function fdProcess:Byte Ptr(exe:String,in_fd:Size_T Ptr,out_fd:Size_T Ptr,err_fd:Size_T Ptr,flags:Int)="fdProcess"
  38. Function fdProcessStatus:Int(processhandle:Byte Ptr)
  39. Function fdTerminateProcess:Int(processhandle:Byte Ptr)
  40. Function fdKillProcess:Int(processhandle:Byte Ptr)
  41. ?not win32
  42. Function fdProcess:Size_T(exe:String,in_fd:Size_T Ptr,out_fd:Size_T Ptr,err_fd:Size_T Ptr,flags:Int)="fdProcess"
  43. Function fdProcessStatus:Int(processhandle:Size_T)
  44. Function fdTerminateProcess:Int(processhandle:Size_T)
  45. Function fdKillProcess:Int(processhandle:Size_T)
  46. ?
  47. End Extern
  48. 'explicitely hide a possibly shown console
  49. Const HIDECONSOLE:Int=1
  50. 'explicitely show a (new) console
  51. Const SHOWCONSOLE:Int=2
  52. Type TPipeStream Extends TStream
  53. Field readbuffer:Byte[4096]
  54. Field bufferpos:Long
  55. Field readhandle:Size_T,writehandle:Size_T
  56. Method Close() Override
  57. If readhandle
  58. fdClose(readhandle)
  59. readhandle=0
  60. EndIf
  61. If writehandle
  62. fdClose(writehandle)
  63. writehandle=0
  64. EndIf
  65. End Method
  66. Method Read:Long( buf:Byte Ptr,count:Long ) Override
  67. Return fdRead(readhandle,buf,count)
  68. End Method
  69. Method Write:Long( buf:Byte Ptr,count:Long ) Override
  70. Return fdWrite(writehandle,buf,count)
  71. End Method
  72. Method Flush() Override
  73. fdFlush(writehandle)
  74. End Method
  75. Method ReadAvail:Int()
  76. Return fdAvail(readhandle)
  77. End Method
  78. Method ReadPipe:Byte[]()
  79. Local bytes:Byte[],n:Int
  80. n=ReadAvail()
  81. If n
  82. bytes=New Byte[n]
  83. Read(bytes,n)
  84. Return bytes
  85. EndIf
  86. End Method
  87. Method ReadLine:String() Override 'nonblocking - returns empty string if no data available
  88. Local n:Long,r:Long,p0:Int,p1:Int,line:String
  89. n=ReadAvail()
  90. If n
  91. If bufferpos+n>4096 n=4096-bufferpos
  92. If n<=0 RuntimeError "PipeStream ReadBuffer Overflow"
  93. r=Read(Varptr readbuffer[bufferpos],n)
  94. bufferpos:+r
  95. EndIf
  96. For n=0 Until bufferpos
  97. If readbuffer[n]=10
  98. p1=n
  99. If (n>0)
  100. If readbuffer[n-1]=13 p1=n-1
  101. EndIf
  102. p0=0
  103. If readbuffer[0]=13 p0=1
  104. If p1>p0 line:String=String.FromBytes(Varptr readbuffer[p0],p1-p0)
  105. n:+1
  106. bufferpos:-n
  107. If bufferpos MemMove(readbuffer,Varptr readbuffer[n],Size_T(bufferpos))
  108. Return line
  109. EndIf
  110. Next
  111. End Method
  112. Function Create:TPipeStream( in:Size_T,out:Size_T )
  113. Local stream:TPipeStream=New TPipeStream
  114. stream.readhandle=in
  115. stream.writehandle=out
  116. Return stream
  117. End Function
  118. End Type
  119. Type TProcess
  120. Global ProcessList:TList
  121. Field name:String
  122. ?win32
  123. Field handle:Byte Ptr
  124. ?not win32
  125. Field handle:Size_T
  126. ?
  127. Field pipe:TPipeStream
  128. Field err:TPipeStream
  129. Field detached:Int
  130. Method Detach:Int()
  131. detached = True
  132. Return 1
  133. End Method
  134. Method Attach:Int()
  135. detached = False
  136. Return 1
  137. End Method
  138. Method Status:Int()
  139. If handle
  140. If fdProcessStatus(handle) Return 1
  141. handle=0
  142. EndIf
  143. Return 0
  144. End Method
  145. Method Close()
  146. If pipe pipe.Close;pipe=Null
  147. If err err.Close;err=Null
  148. End Method
  149. Method Terminate:Int()
  150. Local res:Int
  151. If handle
  152. res=fdTerminateProcess( handle )
  153. handle=0
  154. EndIf
  155. Return res
  156. End Method
  157. 'the less nicer version of "terminate()" as it does not allow
  158. 'the process to finish its stuff
  159. Method Kill:Int()
  160. Local res:Int
  161. If handle
  162. res=fdKillProcess( handle )
  163. handle=0
  164. EndIf
  165. Return res
  166. End Method
  167. Function Create:TProcess(name:String,flags:Int)
  168. Local p:TProcess
  169. Local infd:Size_T,outfd:Size_T,errfd:Size_T
  170. ?MacOS
  171. If FileType(name)=2
  172. Local a:String=StripExt(StripDir(name))
  173. name:+"/Contents/MacOS/"+a
  174. EndIf
  175. ?
  176. FlushZombies
  177. p=New TProcess
  178. p.name=name
  179. p.handle=fdProcess(p.name,Varptr infd,Varptr outfd,Varptr errfd,flags)
  180. If Not p.handle Return Null
  181. p.pipe=TPipeStream.Create(infd,outfd)
  182. p.err=TPipeStream.Create(errfd,0)
  183. p.detached = False
  184. If Not ProcessList ProcessList=New TList
  185. ProcessList.AddLast p
  186. Return p
  187. End Function
  188. Function FlushZombies()
  189. If Not ProcessList Return
  190. Local live:TList=New TList
  191. For Local p:TProcess=EachIn ProcessList
  192. If p.Status() live.AddLast p
  193. Next
  194. ProcessList=live
  195. End Function
  196. Function TerminateAll() NoDebug
  197. If Not ProcessList Return
  198. For Local p:TProcess=EachIn ProcessList
  199. If p.detached = False Then
  200. p.Terminate
  201. EndIf
  202. Next
  203. ProcessList=Null
  204. End Function
  205. Function KillAll() NoDebug
  206. If Not ProcessList Return
  207. For Local p:TProcess=EachIn ProcessList
  208. If p.detached = False Then
  209. p.Kill
  210. EndIf
  211. Next
  212. ProcessList=Null
  213. End Function
  214. End Type
  215. Rem
  216. bbdoc: Creates a process
  217. returns: TProcess object that is linked to the process you have started
  218. End Rem
  219. Function CreateProcess:TProcess(cmd:String,flags:Int=0)
  220. Return TProcess.Create(cmd,flags)
  221. End Function
  222. Rem
  223. bbdoc: Checks status of program
  224. returns: 1 if program is still running and 0 otherwise.
  225. End Rem
  226. Function ProcessStatus:Int(process:TProcess)
  227. Return process.Status()
  228. End Function
  229. Rem
  230. bbdoc: Detaches a process from program
  231. End Rem
  232. Function DetachProcess:Int(process:TProcess)
  233. Return process.Detach()
  234. End Function
  235. Rem
  236. bbdoc: Reattaches a process from program
  237. End Rem
  238. Function AttachProcess:Int(process:TProcess)
  239. Return process.Attach()
  240. End Function
  241. Rem
  242. bbdoc: End Process
  243. returns: 1 if termination of program was successful and 0 otherwise.
  244. End Rem
  245. Function TerminateProcess:Int(process:TProcess)
  246. Return process.Terminate()
  247. End Function
  248. Rem
  249. bbdoc: Forcefully End Process
  250. returns: 1 if forceful termination of program was successful and 0 otherwise.
  251. End Rem
  252. Function KillProcess:Int(process:TProcess)
  253. Return process.Kill()
  254. End Function
  255. OnEnd TProcess.TerminateAll