v / vlib / os
Raw file | 293 loc (263 sloc) | 6.99 KB | Latest commit hash 868908b80
1module os
2
3import strings
4
5fn C.GenerateConsoleCtrlEvent(event u32, pgid u32) bool
6fn C.GetModuleHandleA(name &char) HMODULE
7fn C.GetProcAddress(handle voidptr, procname &u8) voidptr
8fn C.TerminateProcess(process HANDLE, exit_code u32) bool
9fn C.PeekNamedPipe(hNamedPipe voidptr, lpBuffer voidptr, nBufferSize int, lpBytesRead voidptr, lpTotalBytesAvail voidptr, lpBytesLeftThisMessage voidptr) bool
10
11type FN_NTSuspendResume = fn (voidptr)
12
13fn ntdll_fn(name &u8) FN_NTSuspendResume {
14 ntdll := C.GetModuleHandleA(c'NTDLL')
15 if ntdll == 0 {
16 return FN_NTSuspendResume(0)
17 }
18 the_fn := FN_NTSuspendResume(C.GetProcAddress(ntdll, name))
19 return the_fn
20}
21
22fn failed_cfn_report_error(ok bool, label string) {
23 if ok {
24 return
25 }
26 error_num := int(C.GetLastError())
27 error_msg := get_error_msg(error_num)
28 eprintln('failed ${label}: ${error_msg}')
29 exit(1)
30}
31
32type PU32 = &u32
33
34// TODO: the PU32 alias is used to compensate for the wrong number of &/*
35// that V does when doing: `h := &&u32(p)`, which should have casted
36// p to a double pointer.
37fn close_valid_handle(p voidptr) {
38 h := &PU32(p)
39 if *h != &u32(0) {
40 C.CloseHandle(*h)
41 unsafe {
42 *h = &u32(0)
43 }
44 }
45}
46
47pub struct WProcess {
48pub mut:
49 proc_info ProcessInformation
50 command_line [65536]u8
51 child_stdin &u32 = unsafe { nil }
52 //
53 child_stdout_read &u32 = unsafe { nil }
54 child_stdout_write &u32 = unsafe { nil }
55 //
56 child_stderr_read &u32 = unsafe { nil }
57 child_stderr_write &u32 = unsafe { nil }
58}
59
60[manualfree]
61fn (mut p Process) win_spawn_process() int {
62 mut to_be_freed := []voidptr{cap: 5}
63 defer {
64 for idx := to_be_freed.len - 1; idx >= 0; idx-- {
65 unsafe { free(to_be_freed[idx]) }
66 }
67 unsafe { to_be_freed.free() }
68 }
69 p.filename = abs_path(p.filename) // expand the path to an absolute one, in case we later change the working folder
70 mut wdata := &WProcess{
71 child_stdin: 0
72 child_stdout_read: 0
73 child_stdout_write: 0
74 child_stderr_read: 0
75 child_stderr_write: 0
76 }
77 p.wdata = voidptr(wdata)
78 mut start_info := StartupInfo{
79 lp_reserved2: 0
80 lp_reserved: 0
81 lp_desktop: 0
82 lp_title: 0
83 cb: sizeof(C.PROCESS_INFORMATION)
84 }
85 if p.use_stdio_ctl {
86 mut sa := SecurityAttributes{}
87 sa.n_length = sizeof(C.SECURITY_ATTRIBUTES)
88 sa.b_inherit_handle = true
89 create_pipe_ok1 := C.CreatePipe(voidptr(&wdata.child_stdout_read), voidptr(&wdata.child_stdout_write),
90 voidptr(&sa), 0)
91 failed_cfn_report_error(create_pipe_ok1, 'CreatePipe stdout')
92 set_handle_info_ok1 := C.SetHandleInformation(wdata.child_stdout_read, C.HANDLE_FLAG_INHERIT,
93 0)
94 failed_cfn_report_error(set_handle_info_ok1, 'SetHandleInformation')
95 create_pipe_ok2 := C.CreatePipe(voidptr(&wdata.child_stderr_read), voidptr(&wdata.child_stderr_write),
96 voidptr(&sa), 0)
97 failed_cfn_report_error(create_pipe_ok2, 'CreatePipe stderr')
98 set_handle_info_ok2 := C.SetHandleInformation(wdata.child_stderr_read, C.HANDLE_FLAG_INHERIT,
99 0)
100 failed_cfn_report_error(set_handle_info_ok2, 'SetHandleInformation stderr')
101 start_info.h_std_input = wdata.child_stdin
102 start_info.h_std_output = wdata.child_stdout_write
103 start_info.h_std_error = wdata.child_stderr_write
104 start_info.dw_flags = u32(C.STARTF_USESTDHANDLES)
105 }
106 cmd := '${p.filename} ' + p.args.join(' ')
107 cmd_wide_ptr := cmd.to_wide()
108 to_be_freed << cmd_wide_ptr
109 C.ExpandEnvironmentStringsW(cmd_wide_ptr, voidptr(&wdata.command_line[0]), 32768)
110
111 mut creation_flags := if p.create_no_window {
112 int(C.CREATE_NO_WINDOW)
113 } else {
114 int(C.NORMAL_PRIORITY_CLASS)
115 }
116 if p.use_pgroup {
117 creation_flags |= C.CREATE_NEW_PROCESS_GROUP
118 }
119
120 mut work_folder_ptr := voidptr(unsafe { nil })
121 if p.work_folder != '' {
122 work_folder_ptr = p.work_folder.to_wide()
123 to_be_freed << work_folder_ptr
124 }
125
126 create_process_ok := C.CreateProcessW(0, &wdata.command_line[0], 0, 0, C.TRUE, creation_flags,
127 0, work_folder_ptr, voidptr(&start_info), voidptr(&wdata.proc_info))
128 failed_cfn_report_error(create_process_ok, 'CreateProcess')
129 if p.use_stdio_ctl {
130 close_valid_handle(&wdata.child_stdout_write)
131 close_valid_handle(&wdata.child_stderr_write)
132 }
133 p.pid = int(wdata.proc_info.dw_process_id)
134 return p.pid
135}
136
137fn (mut p Process) win_stop_process() {
138 the_fn := ntdll_fn(c'NtSuspendProcess')
139 if voidptr(the_fn) == 0 {
140 return
141 }
142 wdata := unsafe { &WProcess(p.wdata) }
143 the_fn(wdata.proc_info.h_process)
144}
145
146fn (mut p Process) win_resume_process() {
147 the_fn := ntdll_fn(c'NtResumeProcess')
148 if voidptr(the_fn) == 0 {
149 return
150 }
151 wdata := unsafe { &WProcess(p.wdata) }
152 the_fn(wdata.proc_info.h_process)
153}
154
155fn (mut p Process) win_kill_process() {
156 wdata := unsafe { &WProcess(p.wdata) }
157 C.TerminateProcess(wdata.proc_info.h_process, 3)
158}
159
160fn (mut p Process) win_kill_pgroup() {
161 wdata := unsafe { &WProcess(p.wdata) }
162 C.GenerateConsoleCtrlEvent(C.CTRL_BREAK_EVENT, wdata.proc_info.dw_process_id)
163 C.Sleep(20)
164 C.TerminateProcess(wdata.proc_info.h_process, 3)
165}
166
167fn (mut p Process) win_wait() {
168 exit_code := u32(1)
169 mut wdata := unsafe { &WProcess(p.wdata) }
170 if p.wdata != 0 {
171 C.WaitForSingleObject(wdata.proc_info.h_process, C.INFINITE)
172 C.GetExitCodeProcess(wdata.proc_info.h_process, voidptr(&exit_code))
173 close_valid_handle(&wdata.child_stdin)
174 close_valid_handle(&wdata.child_stdout_write)
175 close_valid_handle(&wdata.child_stderr_write)
176 close_valid_handle(&wdata.proc_info.h_process)
177 close_valid_handle(&wdata.proc_info.h_thread)
178 }
179 p.status = .exited
180 p.code = int(exit_code)
181}
182
183fn (mut p Process) win_is_alive() bool {
184 exit_code := u32(0)
185 wdata := unsafe { &WProcess(p.wdata) }
186 C.GetExitCodeProcess(wdata.proc_info.h_process, voidptr(&exit_code))
187 if exit_code == C.STILL_ACTIVE {
188 return true
189 }
190 return false
191}
192
193///////////////
194
195fn (mut p Process) win_write_string(idx int, s string) {
196 panic('Process.write_string ${idx} is not implemented yet')
197}
198
199fn (mut p Process) win_read_string(idx int, maxbytes int) (string, int) {
200 mut wdata := unsafe { &WProcess(p.wdata) }
201 if unsafe { wdata == 0 } {
202 return '', 0
203 }
204 mut rhandle := &u32(0)
205 if idx == 1 {
206 rhandle = wdata.child_stdout_read
207 }
208 if idx == 2 {
209 rhandle = wdata.child_stderr_read
210 }
211 if rhandle == 0 {
212 return '', 0
213 }
214 mut bytes_avail := int(0)
215 if !C.PeekNamedPipe(rhandle, unsafe { nil }, int(0), unsafe { nil }, &bytes_avail,
216 unsafe { nil }) {
217 return '', 0
218 }
219 if bytes_avail == 0 {
220 return '', 0
221 }
222
223 mut bytes_read := int(0)
224 buf := []u8{len: bytes_avail + 300}
225 unsafe {
226 C.ReadFile(rhandle, &buf[0], buf.cap, voidptr(&bytes_read), 0)
227 }
228 return buf[..bytes_read].bytestr(), bytes_read
229}
230
231fn (mut p Process) win_slurp(idx int) string {
232 mut wdata := unsafe { &WProcess(p.wdata) }
233 if unsafe { wdata == 0 } {
234 return ''
235 }
236 mut rhandle := &u32(0)
237 if idx == 1 {
238 rhandle = wdata.child_stdout_read
239 }
240 if idx == 2 {
241 rhandle = wdata.child_stderr_read
242 }
243 if rhandle == 0 {
244 return ''
245 }
246 mut bytes_read := u32(0)
247 buf := [4096]u8{}
248 mut read_data := strings.new_builder(1024)
249 for {
250 mut result := false
251 unsafe {
252 result = C.ReadFile(rhandle, &buf[0], 1000, voidptr(&bytes_read), 0)
253 read_data.write_ptr(&buf[0], int(bytes_read))
254 }
255 if result == false || int(bytes_read) == 0 {
256 break
257 }
258 }
259 soutput := read_data.str()
260 unsafe { read_data.free() }
261 // if idx == 1 {
262 // close_valid_handle(&wdata.child_stdout_read)
263 // }
264 // if idx == 2 {
265 // close_valid_handle(&wdata.child_stderr_read)
266 // }
267 return soutput
268}
269
270//
271// these are here to make v_win.c/v.c generation work in all cases:
272fn (mut p Process) unix_spawn_process() int {
273 return 0
274}
275
276fn (mut p Process) unix_stop_process() {
277}
278
279fn (mut p Process) unix_resume_process() {
280}
281
282fn (mut p Process) unix_kill_process() {
283}
284
285fn (mut p Process) unix_kill_pgroup() {
286}
287
288fn (mut p Process) unix_wait() {
289}
290
291fn (mut p Process) unix_is_alive() bool {
292 return false
293}