ninja_build/subprocess.rs
1// Copyright 2012 Google Inc. All Rights Reserved.
2// Copyright 2017 The Ninja-rs Project Developers. All Rights Reserved.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use std::collections::{VecDeque, HashMap};
17use std::cell::Cell;
18#[cfg(unix)]
19use super::utils::set_close_on_exec;
20
21use super::exit_status::ExitStatus;
22
23#[cfg(windows)]
24struct SubprocessOs {
25 pub child: ::winapi::HANDLE,
26 pub pipe: ::winapi::HANDLE,
27 pub overlapped: ::winapi::OVERLAPPED,
28 pub overlapped_buf: [u8; 4096],
29 pub is_reading: bool,
30}
31
32#[cfg(windows)]
33impl Default for SubprocessOs {
34 fn default() -> Self {
35 unsafe { ::std::mem::zeroed() }
36 }
37}
38
39
40#[cfg(unix)]
41#[derive(Default)]
42struct SubprocessOs {
43 pub fd: Option<::libc::c_int>,
44 pub pid: Option<::libc::pid_t>,
45}
46
47/// Subprocess wraps a single async subprocess. It is entirely
48/// passive: it expects the caller to notify it when its fds are ready
49/// for reading, as well as call Finish() to reap the child once done()
50/// is true.
51pub struct Subprocess {
52 use_console: bool,
53 buf: Vec<u8>,
54 extra: Box<SubprocessOs>,
55}
56
57impl Subprocess {
58 // always boxed to make sure pointer to self is not changed.
59 pub(super) fn new(use_console: bool) -> Box<Self> {
60 Box::new(Subprocess {
61 use_console,
62 buf: Vec::new(),
63 extra: Default::default(),
64 })
65 }
66
67 pub fn output(&self) -> &[u8] {
68 &self.buf
69 }
70}
71
72#[cfg(windows)]
73impl Drop for Subprocess {
74 fn drop(&mut self) {
75 use winapi;
76 use errno;
77 use kernel32;
78
79 if !self.extra.pipe.is_null() {
80 if unsafe { kernel32::CloseHandle(self.extra.pipe) } == winapi::FALSE {
81 fatal!("CloseHandle: {}", errno::errno());
82 }
83 }
84
85 // Reap child if forgotten.
86 if self.exist() {
87 self.finish();
88 }
89 }
90}
91
92#[cfg(windows)]
93impl Subprocess {
94 pub(super) fn exist(&self) -> bool {
95 !self.extra.child.is_null()
96 }
97
98 pub(super) fn start<T>(&mut self, set: &mut SubprocessSet<T>, command: &[u8]) -> bool {
99 use winapi;
100 use kernel32;
101 use std::ptr::null_mut;
102 use std::mem::{zeroed, size_of};
103
104 let child_pipe = self.setup_pipe(set.extra.ioport());
105
106 let mut security_attributes = unsafe { zeroed::<winapi::SECURITY_ATTRIBUTES>() };
107 security_attributes.nLength = size_of::<winapi::SECURITY_ATTRIBUTES>() as _;
108 security_attributes.bInheritHandle = winapi::TRUE;
109
110 let nul_name = wstrz!("NUL");
111
112 let nul = unsafe {
113 kernel32::CreateFileW(
114 nul_name.as_ptr(),
115 winapi::GENERIC_READ,
116 winapi::FILE_SHARE_READ | winapi::FILE_SHARE_WRITE | winapi::FILE_SHARE_DELETE,
117 &mut security_attributes as _,
118 winapi::OPEN_EXISTING,
119 0,
120 null_mut(),
121 )
122 };
123
124 if nul == winapi::INVALID_HANDLE_VALUE {
125 fatal!("couldn't open nul");
126 }
127
128 let mut startup_info = unsafe { zeroed::<winapi::STARTUPINFOW>() };
129 startup_info.cb = size_of::<winapi::STARTUPINFOW>() as _;
130 if !self.use_console {
131 startup_info.dwFlags = winapi::STARTF_USESTDHANDLES;
132 startup_info.hStdInput = nul;
133 startup_info.hStdOutput = child_pipe;
134 startup_info.hStdError = child_pipe;
135 }
136
137 // In the console case, child_pipe is still inherited by the child and closed
138 // when the subprocess finishes, which then notifies ninja.
139 let mut process_info = unsafe { zeroed::<winapi::PROCESS_INFORMATION>() };
140
141 // Ninja handles ctrl-c, except for subprocesses in console pools.
142 let process_flags = if self.use_console {
143 0
144 } else {
145 winapi::CREATE_NEW_PROCESS_GROUP
146 };
147
148 // Do not prepend 'cmd /c' on Windows, this breaks command
149 // lines greater than 8,191 chars.
150 let cmd_unicode = ::std::str::from_utf8(command);
151 let create_process_result = match &cmd_unicode {
152 &Err(_) => Err(None),
153 &Ok(ref cmd_unicode) => {
154 if let Ok(cmd) = ::widestring::WideCString::from_str(cmd_unicode) {
155 let mut cmd = cmd.into_vec();
156 if unsafe {
157 kernel32::CreateProcessW(
158 null_mut(),
159 cmd.as_mut_ptr(),
160 null_mut(),
161 null_mut(),
162 winapi::TRUE,
163 process_flags,
164 null_mut(),
165 null_mut(),
166 &mut startup_info as _,
167 &mut process_info as _,
168 )
169 } != winapi::FALSE
170 {
171 Ok(())
172 } else {
173 Err(Some(unsafe { kernel32::GetLastError() }))
174 }
175 } else {
176 Err(None)
177 }
178 }
179 };
180
181 if !child_pipe.is_null() {
182 unsafe { kernel32::CloseHandle(child_pipe) };
183 }
184
185 unsafe { kernel32::CloseHandle(nul) };
186
187 match create_process_result {
188 Ok(()) => {
189 unsafe { kernel32::CloseHandle(process_info.hThread) };
190 self.extra.child = process_info.hProcess;
191 true
192 }
193 Err(e @ Some(winapi::ERROR_FILE_NOT_FOUND)) |
194 Err(e @ None) => {
195 // File (program) not found error is treated as a normal build
196 // action failure.
197 unsafe { kernel32::CloseHandle(self.extra.pipe) };
198 self.extra.pipe = null_mut();
199 // child_ is already NULL;
200 self.buf = if e.is_some() {
201 b"CreateProcess failed: The system cannot find the file specified.\n"
202 .as_ref()
203 .to_owned()
204 } else {
205 b"CreateProcess failed: The command is not valid UTF-8 string.\n"
206 .as_ref()
207 .to_owned()
208 };
209 true
210 }
211 Err(Some(e)) => fatal!("CreateProcess : {}", ::errno::Errno(e as _)),
212 }
213 }
214
215
216 /// Set up pipe_ as the parent-side pipe of the subprocess; return the
217 /// other end of the pipe, usable in the child process.
218 fn setup_pipe(&mut self, ioport: ::winapi::HANDLE) -> ::winapi::HANDLE {
219 use winapi;
220 use kernel32;
221 use errno;
222 use std::mem::zeroed;
223 use std::ptr::null_mut;
224 use widestring::WideCString;
225 let pipe_name = format!(
226 "\\\\.\\pipe\\ninja_pid{}_sp{:p}",
227 unsafe { kernel32::GetCurrentProcessId() },
228 self
229 );
230
231 let pipe_name = WideCString::from_str(pipe_name).unwrap().into_vec();
232
233 self.extra.pipe = unsafe {
234 kernel32::CreateNamedPipeW(
235 pipe_name.as_ptr(),
236 winapi::PIPE_ACCESS_INBOUND | winapi::FILE_FLAG_OVERLAPPED,
237 winapi::PIPE_TYPE_BYTE,
238 winapi::PIPE_UNLIMITED_INSTANCES,
239 0,
240 0,
241 winapi::INFINITE,
242 null_mut(),
243 )
244 };
245
246 if self.extra.pipe == winapi::INVALID_HANDLE_VALUE {
247 fatal!("CreateNamedPipe : {}", errno::errno());
248 }
249
250 let create_port_result = unsafe {
251 kernel32::CreateIoCompletionPort(
252 self.extra.pipe,
253 ioport,
254 self as *mut _ as usize as _,
255 0,
256 )
257 };
258 if create_port_result.is_null() {
259 fatal!("CreateIoCompletionPort : {}", errno::errno());
260 }
261
262 self.extra.overlapped = unsafe { zeroed() };
263 if unsafe {
264 kernel32::ConnectNamedPipe(self.extra.pipe, &mut self.extra.overlapped as _)
265 } == winapi::FALSE &&
266 unsafe { kernel32::GetLastError() } != winapi::ERROR_IO_PENDING
267 {
268 fatal!("ConnectNamedPipe : {}", errno::errno());
269 }
270
271 // Get the write end of the pipe as a handle inheritable across processes.
272 let output_write_handle = unsafe {
273 kernel32::CreateFileW(
274 pipe_name.as_ptr(),
275 winapi::GENERIC_WRITE,
276 0,
277 null_mut(),
278 winapi::OPEN_EXISTING,
279 0,
280 null_mut(),
281 )
282 };
283 let mut output_write_child = null_mut();
284 if unsafe {
285 kernel32::DuplicateHandle(
286 kernel32::GetCurrentProcess(),
287 output_write_handle,
288 kernel32::GetCurrentProcess(),
289 &mut output_write_child as _,
290 0,
291 winapi::TRUE,
292 winapi::DUPLICATE_SAME_ACCESS,
293 )
294 } == winapi::FALSE
295 {
296
297 fatal!("DuplicateHandle : {}", errno::errno());
298 }
299
300 unsafe {
301 kernel32::CloseHandle(output_write_handle);
302 }
303
304 output_write_child
305 }
306
307 pub fn on_pipe_ready(&mut self) {
308 use winapi;
309 use kernel32;
310 use errno;
311 use std::mem::{zeroed, size_of_val};
312 use std::ptr::null_mut;
313
314 let mut bytes = 0 as winapi::DWORD;
315 if unsafe {
316 kernel32::GetOverlappedResult(
317 self.extra.pipe,
318 &mut self.extra.overlapped as *mut _,
319 &mut bytes as *mut _,
320 winapi::TRUE,
321 )
322 } == winapi::FALSE
323 {
324
325 if unsafe { kernel32::GetLastError() } == winapi::ERROR_BROKEN_PIPE {
326 unsafe { kernel32::CloseHandle(self.extra.pipe) };
327 self.extra.pipe = null_mut();
328 } else {
329 fatal!("GetOverlappedResult: {}", errno::errno());
330 }
331 return;
332 }
333 if self.extra.is_reading && bytes > 0 {
334 self.buf.extend_from_slice(
335 &self.extra.overlapped_buf[0..(bytes as usize)],
336 );
337 }
338
339 self.extra.overlapped = unsafe { zeroed() };
340 self.extra.is_reading = true;
341
342 if unsafe {
343 kernel32::ReadFile(
344 self.extra.pipe,
345 self.extra.overlapped_buf.as_mut_ptr() as usize as _,
346 size_of_val(&self.extra.overlapped_buf) as _,
347 &mut bytes as *mut _,
348 &mut self.extra.overlapped as *mut _,
349 )
350 } == winapi::FALSE
351 {
352
353 match unsafe { kernel32::GetLastError() } {
354 winapi::ERROR_IO_PENDING => {}
355 winapi::ERROR_BROKEN_PIPE => {
356 unsafe { kernel32::CloseHandle(self.extra.pipe) };
357 self.extra.pipe = null_mut();
358 }
359 e => {
360 fatal!("ReadFile : {}", errno::errno());
361 }
362 }
363 return;
364 }
365
366 // Even if we read any bytes in the readfile call, we'll enter this
367 // function again later and get them at that point.
368 }
369
370 /// Returns ExitSuccess on successful process exit, ExitInterrupted if
371 /// the process was interrupted, ExitFailure if it otherwise failed.
372 pub fn finish(&mut self) -> ExitStatus {
373 use winapi;
374 use kernel32;
375
376 if !self.exist() {
377 return ExitStatus::ExitFailure;
378 }
379
380 // TODO: add error handling for all of these.
381 unsafe {
382 kernel32::WaitForSingleObject(self.extra.child, winapi::INFINITE);
383 }
384
385 let mut exit_code = 0 as winapi::DWORD;
386 unsafe { kernel32::GetExitCodeProcess(self.extra.child, &mut exit_code as _) };
387 unsafe { kernel32::CloseHandle(self.extra.child) };
388
389 self.extra.child = ::std::ptr::null_mut();
390
391 match exit_code as _ {
392 0 => ExitStatus::ExitSuccess,
393 winapi::STATUS_CONTROL_C_EXIT => ExitStatus::ExitInterrupted,
394 _ => ExitStatus::ExitFailure,
395 }
396 }
397
398 fn done(&self) -> bool {
399 self.extra.pipe.is_null()
400 }
401}
402
403#[cfg(unix)]
404impl Subprocess {
405 pub(super) fn exist(&self) -> bool {
406 true
407 }
408
409 pub(super) fn start<T>(&mut self, set: &mut SubprocessSet<T>, command: &[u8]) -> bool {
410 use libc;
411 use libc_spawn;
412 use std::mem;
413 use std::ffi;
414 use std::ptr;
415 use errno;
416
417 unsafe {
418 let mut output_pipe: [libc::c_int; 2] = mem::zeroed();
419 if libc::pipe(output_pipe.as_mut_ptr()) < 0 {
420 fatal!("pipe: {}", errno::errno());
421 }
422
423 let pipe0 = output_pipe[0];
424 let pipe1 = output_pipe[1];
425 self.extra.fd = Some(pipe0);
426 if !set.use_ppoll() {
427 // If available, we use ppoll in DoWork(); otherwise we use pselect
428 // and so must avoid overly-large FDs.
429 if pipe0 >= libc::FD_SETSIZE as _ {
430 fatal!("pipe: {}", errno::Errno(libc::EMFILE));
431 }
432 }
433
434 set_close_on_exec(pipe0);
435
436 let mut action: libc_spawn::posix_spawn_file_actions_t = mem::zeroed();
437 if libc_spawn::posix_spawn_file_actions_init(&mut action as _) != 0 {
438 fatal!("posix_spawn_file_actions_init: {}", errno::errno());
439 }
440
441 if libc_spawn::posix_spawn_file_actions_addclose(&mut action as _, pipe0) != 0 {
442 fatal!("posix_spawn_file_actions_addclose: {}", errno::errno());
443 }
444
445 let mut attr = mem::zeroed::<libc_spawn::posix_spawnattr_t>();
446 if libc_spawn::posix_spawnattr_init(&mut attr as _) != 0 {
447 fatal!("posix_spawnattr_init: {}", errno::errno());
448 }
449
450 let mut flags = 0;
451 flags |= libc_spawn::POSIX_SPAWN_SETSIGMASK;
452
453 if libc_spawn::posix_spawnattr_setsigmask(&mut attr as _, &mut set.extra.old_mask as _) != 0 {
454 fatal!("posix_spawnattr_setsigmask: {}", errno::errno());
455 }
456 // Signals which are set to be caught in the calling process image are set to
457 // default action in the new process image, so no explicit
458 // POSIX_SPAWN_SETSIGDEF parameter is needed.
459
460 if !self.use_console {
461 // Put the child in its own process group, so ctrl-c won't reach it.
462 flags |= libc_spawn::POSIX_SPAWN_SETPGROUP;
463 // No need to posix_spawnattr_setpgroup(&attr, 0), it's the default.
464
465 // Open /dev/null over stdin.
466 let dev_null = ffi::CString::new("/dev/null").unwrap();
467 if libc_spawn::posix_spawn_file_actions_addopen(&mut action as _, 0, dev_null.as_ptr(), libc::O_RDONLY, 0) != 0 {
468 fatal!("posix_spawn_file_actions_addopen: {}", errno::errno());
469 }
470 if libc_spawn::posix_spawn_file_actions_adddup2(&mut action as _, pipe1, 1) != 0 {
471 fatal!("posix_spawn_file_actions_adddup2: {}", errno::errno());
472 }
473 if libc_spawn::posix_spawn_file_actions_adddup2(&mut action as _, pipe1, 2) != 0 {
474 fatal!("posix_spawn_file_actions_adddup2: {}", errno::errno());
475 }
476 if libc_spawn::posix_spawn_file_actions_addclose(&mut action as _, pipe1) != 0 {
477 fatal!("posix_spawn_file_actions_addclose: {}", errno::errno());
478 }
479 // In the console case, output_pipe is still inherited by the child and
480 // closed when the subprocess finishes, which then notifies ninja.
481 }
482 if let Some(v) = libc_spawn::optional_const::posix_spawn_usevfork() {
483 flags |= v;
484 }
485
486 if libc_spawn::posix_spawnattr_setflags(&mut attr as _, flags) != 0 {
487 fatal!("posix_spawnattr_setflags: {}", errno::errno());
488 }
489
490 let spawned_args0 = ffi::CString::new("/bin/sh").unwrap();
491 let spawned_args1 = ffi::CString::new("-c").unwrap();
492 let spawned_args2 = ffi::CString::from_vec_unchecked(command.to_owned());
493 let mut spawned_args = [spawned_args0.as_ptr(),
494 spawned_args1.as_ptr(), spawned_args2.as_ptr(), ptr::null_mut()];
495 self.extra.pid = Some(-1);
496 if libc_spawn::posix_spawn(self.extra.pid.as_mut().unwrap() as _,
497 spawned_args0.as_ptr(), &mut action as _, &mut attr as _,
498 spawned_args.as_mut_ptr(), libc_spawn::optional_const::environ()) != 0 {
499 self.extra.pid = None;
500 fatal!("posix_spawn: {}", errno::errno());
501 }
502
503 if libc_spawn::posix_spawnattr_destroy(&mut attr as _) != 0 {
504 fatal!("posix_spawnattr_destroy: {}", errno::errno());
505 }
506
507 if libc_spawn::posix_spawn_file_actions_destroy(&mut action as _) != 0 {
508 fatal!("posix_spawn_file_actions_destroy: {}", errno::errno());
509 }
510
511 libc::close(pipe1);
512 }
513 true
514 }
515
516 pub fn on_pipe_ready(&mut self) {
517 use libc;
518 use errno;
519 use std::mem;
520
521 unsafe {
522 let fd = self.extra.fd.unwrap_or(-1);
523 let mut buf = [0u8; 4096];
524 let len = libc::read(fd, buf.as_mut_ptr() as usize as _, mem::size_of_val(&buf));
525 if len < 0 {
526 fatal!("read: {}", errno::errno());
527 } else if len > 0 {
528 self.buf.extend_from_slice(&buf[0..len as usize]);
529 } else {
530 libc::close(fd);
531 self.extra.fd = None;
532 }
533 }
534
535 }
536
537 fn done(&self) -> bool {
538 self.extra.fd.is_none()
539 }
540
541 /// Returns ExitSuccess on successful process exit, ExitInterrupted if
542 /// the process was interrupted, ExitFailure if it otherwise failed.
543 pub fn finish(&mut self) -> ExitStatus {
544 use libc;
545 use errno;
546
547 debug_assert!(self.extra.pid.is_some());
548 unsafe {
549 let mut status: libc::c_int = 0;
550 let pid = self.extra.pid.unwrap();
551 if libc::waitpid(pid, &mut status as _, 0) < 0 {
552 fatal!("waitpid({}): {}", pid, errno::errno());
553 }
554
555 self.extra.pid = None;
556 if libc::WIFEXITED(status) {
557 let exit = libc::WEXITSTATUS(status);
558 if exit == 0 {
559 return ExitStatus::ExitSuccess;
560 }
561 } else if libc::WIFSIGNALED(status) {
562 match libc::WTERMSIG(status) {
563 libc::SIGINT | libc::SIGTERM | libc::SIGHUP => {
564 return ExitStatus::ExitInterrupted;
565 },
566 _ => {},
567 }
568 }
569 }
570 return ExitStatus::ExitFailure;
571 }
572}
573
574
575/*
576struct Subprocess {
577 ~Subprocess();
578
579 /// Returns ExitSuccess on successful process exit, ExitInterrupted if
580 /// the process was interrupted, ExitFailure if it otherwise failed.
581 ExitStatus Finish();
582
583 bool Done() const;
584
585 const string& GetOutput() const;
586
587 private:
588 Subprocess(bool use_console);
589 bool Start(struct SubprocessSet* set, const string& command);
590 void OnPipeReady();
591
592 string buf_;
593
594#ifdef _WIN32
595
596 HANDLE child_;
597 HANDLE pipe_;
598 OVERLAPPED overlapped_;
599 char overlapped_buf_[4 << 10];
600 bool is_reading_;
601#else
602 int fd_;
603 pid_t pid_;
604#endif
605 bool use_console_;
606
607 friend struct SubprocessSet;
608};
609*/
610
611#[cfg(windows)]
612struct SubprocessSetOs {}
613
614#[cfg(windows)]
615thread_local! {
616 static IOPORT : ::std::cell::Cell<::winapi::HANDLE> =
617 ::std::cell::Cell::new(::std::ptr::null_mut());
618}
619
620#[cfg(windows)]
621unsafe extern "system" fn notify_interrupted(_: ::winapi::DWORD) -> ::winapi::BOOL {
622 unimplemented!{}
623}
624
625#[cfg(windows)]
626impl SubprocessSetOs {
627 pub fn new() -> Self {
628 use winapi;
629 use kernel32;
630 use errno;
631 use std::ptr::null_mut;
632
633 let v = SubprocessSetOs {};
634 let ioport = unsafe {
635 kernel32::CreateIoCompletionPort(winapi::INVALID_HANDLE_VALUE, null_mut(), 0, 1)
636 };
637 if ioport.is_null() {
638 fatal!("CreateIoCompletionPort: {}", errno::errno());
639 }
640 v.set_ioport(ioport);
641 if unsafe { kernel32::SetConsoleCtrlHandler(Some(notify_interrupted), winapi::TRUE) } ==
642 winapi::FALSE
643 {
644 fatal!("SetConsoleCtrlHandler: {}", errno::errno());
645 }
646 v
647 }
648
649 pub fn ioport(&self) -> ::winapi::HANDLE {
650 IOPORT.with(|p| p.get())
651 }
652
653 pub fn set_ioport(&self, ioport: ::winapi::HANDLE) {
654 IOPORT.with(|p| p.set(ioport))
655 }
656}
657
658#[cfg(unix)]
659thread_local! {
660 static INTERRUPTED : ::std::cell::Cell<::libc::c_int> =
661 ::std::cell::Cell::new(0);
662}
663
664#[cfg(unix)]
665unsafe extern "C" fn set_interrupted_flag(signum: ::libc::c_int) {
666 INTERRUPTED.with(|x| x.set(signum));
667}
668
669#[cfg(unix)]
670unsafe fn handle_pending_interruption() {
671 use libc;
672 use std::mem;
673 use errno;
674
675 let mut pending = mem::zeroed::<libc::sigset_t>();
676 libc::sigemptyset(&mut pending as _);
677 if libc::sigpending(&mut pending as _) == -1 {
678 fatal!("ninja: sigpending: {}", errno::errno());
679 }
680
681 if libc::sigismember(&mut pending as _, libc::SIGINT) != 0 {
682 INTERRUPTED.with(|x| x.set(libc::SIGINT));
683 } else if libc::sigismember(&mut pending as _, libc::SIGTERM) != 0 {
684 INTERRUPTED.with(|x| x.set(libc::SIGTERM));
685 } else if libc::sigismember(&mut pending as _, libc::SIGHUP) != 0 {
686 INTERRUPTED.with(|x| x.set(libc::SIGHUP));
687 }
688}
689
690#[cfg(unix)]
691fn is_interrupted() -> bool {
692 return INTERRUPTED.with(|x| x.get() != 0);
693}
694
695
696#[cfg(unix)]
697struct SubprocessSetOs {
698 old_int_act: ::libc::sigaction,
699 old_term_act: ::libc::sigaction,
700 old_hup_act: ::libc::sigaction,
701 old_mask: ::libc::sigset_t,
702}
703
704#[cfg(unix)]
705impl SubprocessSetOs {
706 pub fn new() -> Self {
707 use std::mem;
708 use libc;
709 use errno;
710
711 let mut v = unsafe { mem::zeroed::<Self>() };
712 unsafe {
713 let mut set = mem::zeroed::<libc::sigset_t>();
714 libc::sigemptyset(&mut set as _);
715 libc::sigaddset(&mut set as _, libc::SIGINT);
716 libc::sigaddset(&mut set as _, libc::SIGTERM);
717 libc::sigaddset(&mut set as _, libc::SIGHUP);
718 if libc::sigprocmask(libc::SIG_BLOCK, &mut set as _, &mut v.old_mask as _) < 0 {
719 fatal!("sigprocmask: {}", errno::errno());
720 }
721
722 let mut act = mem::zeroed::<libc::sigaction>();
723 act.sa_sigaction = set_interrupted_flag as _;
724
725 if libc::sigaction(libc::SIGINT, &mut act as _, &mut v.old_int_act) < 0
726 || libc::sigaction(libc::SIGTERM, &mut act as _, &mut v.old_term_act) < 0
727 || libc::sigaction(libc::SIGHUP, &mut act as _, &mut v.old_hup_act) < 0 {
728 fatal!("sigaction: {}", errno::errno());
729 }
730
731 if libc::sigprocmask(libc::SIG_BLOCK, &mut set as _, &mut v.old_mask as _) < 0 {
732 fatal!("sigprocmask: {}", errno::errno());
733 }
734 }
735
736 v
737 }
738}
739
740#[cfg(unix)]
741impl Drop for SubprocessSetOs {
742 fn drop(&mut self) {
743 use libc;
744 use errno;
745 use std::ptr;
746
747 unsafe {
748 if libc::sigaction(libc::SIGINT, &mut self.old_int_act, ptr::null_mut()) < 0
749 || libc::sigaction(libc::SIGTERM, &mut self.old_term_act, ptr::null_mut()) < 0
750 || libc::sigaction(libc::SIGHUP, &mut self.old_hup_act, ptr::null_mut()) < 0 {
751 fatal!("sigaction: {}", errno::errno());
752 }
753
754 if libc::sigprocmask(libc::SIG_SETMASK, &mut self.old_mask as _, ptr::null_mut()) < 0 {
755 fatal!("sigprocmask: {}", errno::errno());
756 }
757
758 }
759 }
760}
761
762/// SubprocessSet runs a ppoll/pselect() loop around a set of Subprocesses.
763/// DoWork() waits for any state change in subprocesses; finished_
764/// is a queue of subprocesses as they finish.
765pub struct SubprocessSet<Data = ()> {
766 running: HashMap<usize, (Box<Subprocess>, Data)>,
767 finished: VecDeque<(Box<Subprocess>, Data)>,
768 extra: SubprocessSetOs,
769}
770
771type Iter<'a, Data> = ::std::iter::Chain<
772 ::std::collections::vec_deque::Iter<
773 'a,
774 (Box<Subprocess>,
775 Data),
776 >,
777 ::std::collections::hash_map::Values<
778 'a,
779 usize,
780 (Box<Subprocess>,
781 Data),
782 >,
783>;
784
785impl<Data> SubprocessSet<Data> {
786 pub fn new() -> Self {
787 SubprocessSet {
788 running: HashMap::new(),
789 finished: VecDeque::new(),
790 extra: SubprocessSetOs::new(),
791 }
792 }
793
794 pub fn running(&self) -> &HashMap<usize, (Box<Subprocess>, Data)> {
795 &self.running
796 }
797
798 pub fn finished(&self) -> &VecDeque<(Box<Subprocess>, Data)> {
799 &self.finished
800 }
801
802 pub fn add(
803 &mut self,
804 command: &[u8],
805 use_console: bool,
806 data: Data,
807 ) -> Option<&mut (Box<Subprocess>, Data)> {
808
809 let mut subprocess = Subprocess::new(use_console);
810 if !subprocess.start(self, command) {
811 return None;
812 }
813
814 if subprocess.exist() {
815 let key = subprocess.as_ref() as *const _ as usize;
816 self.running.insert(key, (subprocess, data));
817 return self.running.get_mut(&key);
818 } else {
819 self.finished.push_back((subprocess, data));
820 return self.finished.back_mut();
821 }
822 }
823
824 pub fn next_finished(&mut self) -> Option<(Box<Subprocess>, Data)> {
825 self.finished.pop_front()
826 }
827
828 pub fn iter<'a>(&'a self) -> Iter<'a, Data> {
829 self.finished.iter().chain(self.running.values())
830 }
831
832 pub fn clear(&mut self) {
833 self.running.clear();
834 return;
835 unimplemented!{}
836 }
837}
838
839#[cfg(windows)]
840impl<Data> SubprocessSet<Data> {
841 // return Err(()) if interrupted.
842 pub fn do_work(&mut self) -> Result<(), ()> {
843 use winapi;
844 use kernel32;
845 use errno;
846 use std::ptr::null_mut;
847
848 let mut bytes_read = 0 as winapi::DWORD;
849 let mut subproc = null_mut::<Subprocess>();
850 let mut overlapped = null_mut::<winapi::OVERLAPPED>();
851
852 if unsafe {
853 kernel32::GetQueuedCompletionStatus(
854 self.extra.ioport(),
855 &mut bytes_read as _,
856 &mut subproc as *mut _ as usize as _,
857 &mut overlapped as *mut _,
858 winapi::INFINITE,
859 )
860 } == winapi::FALSE
861 {
862 if unsafe { kernel32::GetLastError() } != winapi::ERROR_BROKEN_PIPE {
863 fatal!("GetQueuedCompletionStatus: {}", errno::errno());
864 }
865 }
866
867 let done = if let Some(subproc) = unsafe { subproc.as_mut() } {
868 subproc.on_pipe_ready();
869
870 subproc.done()
871 } else {
872 // A NULL subproc indicates that we were interrupted and is
873 // delivered by NotifyInterrupted above.
874 return Err(());
875 };
876
877 if done {
878 self.finished.extend(
879 self.running
880 .remove(&(subproc as usize))
881 .into_iter(),
882 );
883 }
884
885 return Ok(());
886 }
887}
888
889#[cfg(unix)]
890impl<Data> SubprocessSet<Data> {
891
892}
893
894#[cfg(all(unix,
895 not(any(target_env = "uclibc", target_env = "newlib")),
896 any(target_os = "linux",
897 target_os = "android",
898 target_os = "emscripten",
899 target_os = "fuchsia")))]
900impl<Data> SubprocessSet<Data> {
901 pub fn use_ppoll(&self) -> bool {
902 true
903 }
904
905 // return Err(()) if interrupted.
906 pub fn do_work(&mut self) -> Result<(), ()> {
907 use std::mem;
908 use std::ptr;
909 use libc;
910 use errno;
911
912 unsafe {
913 let mut fds = Vec::new();
914 let mut nfds = 0 as libc::nfds_t;
915
916 self.running.iter().for_each(|p| {
917 if let Some(fd) = (p.1).0.extra.fd.clone() {
918 fds.push(libc::pollfd {
919 fd,
920 events: libc::POLLIN | libc::POLLPRI,
921 revents: 0,
922 });
923 nfds += 1;
924 }
925 });
926 INTERRUPTED.with(|x| x.set(0));
927 let ret = libc::ppoll(fds.as_mut_ptr(), nfds, ptr::null_mut(), &mut self.extra.old_mask as _);
928 if ret == -1 {
929 let errno = errno::errno();
930 if errno.0 != libc::EINTR {
931 fatal!("ninja: ppoll: {}", errno);
932 } else if is_interrupted() {
933 return Err(());
934 } else {
935 return Ok(());
936 }
937 }
938
939 handle_pending_interruption();
940
941 if is_interrupted() {
942 return Err(());
943 }
944
945 let mut removals = Vec::new();
946
947 self.running.iter_mut().enumerate().for_each(|(n, p)| {
948 if let Some(fd) = (p.1).0.extra.fd.clone() {
949 debug_assert!(fd == fds[n].fd);
950 if fds[n].revents != 0 {
951 (p.1).0.on_pipe_ready();
952 if (p.1).0.done() {
953 removals.push(*p.0);
954 }
955 }
956 }
957 });
958
959 removals.into_iter().for_each(|p| {
960 self.finished.extend(self.running.remove(&p));
961 });
962
963 if is_interrupted() {
964 return Err(());
965 } else {
966 return Ok(());
967 }
968 }
969 }
970}
971
972#[cfg(all(unix,
973 any(target_env = "uclibc", target_env = "newlib"),
974 not(any(target_os = "linux",
975 target_os = "android",
976 target_os = "emscripten",
977 target_os = "fuchsia"))))]
978impl<Data> SubprocessSet<Data> {
979 pub fn use_ppoll(&self) -> bool {
980 false
981 }
982
983
984 // return Err(()) if interrupted.
985 pub fn do_work(&mut self) -> Result<(), ()> {
986 use std::mem;
987 use libc;
988 use errno;
989
990 unsafe {
991 let mut set = mem::zeroed::<libc::fd_set>();
992 let mut nfds = 0;
993 libc::FD_ZERO(&mut set as _);
994
995 self.running.iter().for_each(|p| {
996 if let Some(fd) = (p.1).0.extra.fd.clone() {
997 libc::FD_SET(fd, &mut set as _);
998 nfds = std::cmp::max(nfds, fd + 1);
999 }
1000 });
1001 INTERRUPTED.with(|x| x.set(0));
1002 let ret = libc::pselect(nfds, &mut set as _, 0, 0, 0, &mut self.extra.old_mask as _);
1003 if ret == -1 {
1004 let errno = errno::errno();
1005 if errno.0 != libc::EINTR {
1006 fatal!("ninja: pselect: {}", errno);
1007 } else if is_interrupted() {
1008 return Err(());
1009 } else {
1010 return Ok(());
1011 }
1012 }
1013
1014 handle_pending_interruption();
1015
1016 if is_interrupted() {
1017 return Err(());
1018 }
1019
1020 let mut removals = Vec::new();
1021
1022 self.running.iter_mut().for_each(|p| {
1023 if let Some(fd) = (p.1).0.extra.fd.clone() {
1024 if libc::FD_ISSET(fd, &mut set as _) {
1025 (p.1).0.on_pipe_ready();
1026 if (p.1).0.done() {
1027 removals.push(*p.0);
1028 }
1029 }
1030 }
1031 });
1032
1033 removals.into_iter().for_each(|p| {
1034 self.finished.extend(self.running.remove(&p));
1035 });
1036
1037 if is_interrupted() {
1038 return Err(());
1039 } else {
1040 return Ok(());
1041 }
1042 }
1043 }
1044}
1045
1046
1047/*
1048struct SubprocessSet {
1049 SubprocessSet();
1050 ~SubprocessSet();
1051
1052 Subprocess* Add(const string& command, bool use_console = false);
1053 bool DoWork();
1054 Subprocess* NextFinished();
1055 void Clear();
1056
1057 vector<Subprocess*> running_;
1058 queue<Subprocess*> finished_;
1059
1060#ifdef _WIN32
1061 static BOOL WINAPI NotifyInterrupted(DWORD dwCtrlType);
1062 static HANDLE ioport_;
1063#else
1064 static void SetInterruptedFlag(int signum);
1065 static void HandlePendingInterruption();
1066 /// Store the signal number that causes the interruption.
1067 /// 0 if not interruption.
1068 static int interrupted_;
1069
1070 static bool IsInterrupted() { return interrupted_ != 0; }
1071
1072 struct sigaction old_int_act_;
1073 struct sigaction old_term_act_;
1074 struct sigaction old_hup_act_;
1075 sigset_t old_mask_;
1076#endif
1077};
1078
1079#endif // NINJA_SUBPROCESS_H_
1080
1081*/
1082
1083#[cfg(windows)]
1084mod imp {
1085 /*
1086// Copyright 2012 Google Inc. All Rights Reserved.
1087//
1088// Licensed under the Apache License, Version 2.0 (the "License");
1089// you may not use this file except in compliance with the License.
1090// You may obtain a copy of the License at
1091//
1092// http://www.apache.org/licenses/LICENSE-2.0
1093//
1094// Unless required by applicable law or agreed to in writing, software
1095// distributed under the License is distributed on an "AS IS" BASIS,
1096// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1097// See the License for the specific language governing permissions and
1098// limitations under the License.
1099
1100#include "subprocess.h"
1101
1102#include <assert.h>
1103#include <stdio.h>
1104
1105#include <algorithm>
1106
1107#include "util.h"
1108
1109Subprocess::Subprocess(bool use_console) : child_(NULL) , overlapped_(),
1110 is_reading_(false),
1111 use_console_(use_console) {
1112}
1113
1114Subprocess::~Subprocess() {
1115 if (pipe_) {
1116 if (!CloseHandle(pipe_))
1117 Win32Fatal("CloseHandle");
1118 }
1119 // Reap child if forgotten.
1120 if (child_)
1121 Finish();
1122}
1123
1124HANDLE Subprocess::SetupPipe(HANDLE ioport) {
1125 char pipe_name[100];
1126 snprintf(pipe_name, sizeof(pipe_name),
1127 "\\\\.\\pipe\\ninja_pid%lu_sp%p", GetCurrentProcessId(), this);
1128
1129 pipe_ = ::CreateNamedPipeA(pipe_name,
1130 PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
1131 PIPE_TYPE_BYTE,
1132 PIPE_UNLIMITED_INSTANCES,
1133 0, 0, INFINITE, NULL);
1134 if (pipe_ == INVALID_HANDLE_VALUE)
1135 Win32Fatal("CreateNamedPipe");
1136
1137 if (!CreateIoCompletionPort(pipe_, ioport, (ULONG_PTR)this, 0))
1138 Win32Fatal("CreateIoCompletionPort");
1139
1140 memset(&overlapped_, 0, sizeof(overlapped_));
1141 if (!ConnectNamedPipe(pipe_, &overlapped_) &&
1142 GetLastError() != ERROR_IO_PENDING) {
1143 Win32Fatal("ConnectNamedPipe");
1144 }
1145
1146 // Get the write end of the pipe as a handle inheritable across processes.
1147 HANDLE output_write_handle = CreateFile(pipe_name, GENERIC_WRITE, 0,
1148 NULL, OPEN_EXISTING, 0, NULL);
1149 HANDLE output_write_child;
1150 if (!DuplicateHandle(GetCurrentProcess(), output_write_handle,
1151 GetCurrentProcess(), &output_write_child,
1152 0, TRUE, DUPLICATE_SAME_ACCESS)) {
1153 Win32Fatal("DuplicateHandle");
1154 }
1155 CloseHandle(output_write_handle);
1156
1157 return output_write_child;
1158}
1159
1160void Subprocess::OnPipeReady() {
1161 DWORD bytes;
1162 if (!GetOverlappedResult(pipe_, &overlapped_, &bytes, TRUE)) {
1163 if (GetLastError() == ERROR_BROKEN_PIPE) {
1164 CloseHandle(pipe_);
1165 pipe_ = NULL;
1166 return;
1167 }
1168 Win32Fatal("GetOverlappedResult");
1169 }
1170
1171 if (is_reading_ && bytes)
1172 buf_.append(overlapped_buf_, bytes);
1173
1174 memset(&overlapped_, 0, sizeof(overlapped_));
1175 is_reading_ = true;
1176 if (!::ReadFile(pipe_, overlapped_buf_, sizeof(overlapped_buf_),
1177 &bytes, &overlapped_)) {
1178 if (GetLastError() == ERROR_BROKEN_PIPE) {
1179 CloseHandle(pipe_);
1180 pipe_ = NULL;
1181 return;
1182 }
1183 if (GetLastError() != ERROR_IO_PENDING)
1184 Win32Fatal("ReadFile");
1185 }
1186
1187 // Even if we read any bytes in the readfile call, we'll enter this
1188 // function again later and get them at that point.
1189}
1190
1191ExitStatus Subprocess::Finish() {
1192 if (!child_)
1193 return ExitFailure;
1194
1195 // TODO: add error handling for all of these.
1196 WaitForSingleObject(child_, INFINITE);
1197
1198 DWORD exit_code = 0;
1199 GetExitCodeProcess(child_, &exit_code);
1200
1201 CloseHandle(child_);
1202 child_ = NULL;
1203
1204 return exit_code == 0 ? ExitSuccess :
1205 exit_code == CONTROL_C_EXIT ? ExitInterrupted :
1206 ExitFailure;
1207}
1208
1209
1210HANDLE SubprocessSet::ioport_;
1211
1212SubprocessSet::SubprocessSet() {
1213 ioport_ = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
1214 if (!ioport_)
1215 Win32Fatal("CreateIoCompletionPort");
1216 if (!SetConsoleCtrlHandler(NotifyInterrupted, TRUE))
1217 Win32Fatal("SetConsoleCtrlHandler");
1218}
1219
1220SubprocessSet::~SubprocessSet() {
1221 Clear();
1222
1223 SetConsoleCtrlHandler(NotifyInterrupted, FALSE);
1224 CloseHandle(ioport_);
1225}
1226
1227BOOL WINAPI SubprocessSet::NotifyInterrupted(DWORD dwCtrlType) {
1228 if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) {
1229 if (!PostQueuedCompletionStatus(ioport_, 0, 0, NULL))
1230 Win32Fatal("PostQueuedCompletionStatus");
1231 return TRUE;
1232 }
1233
1234 return FALSE;
1235}
1236
1237Subprocess *SubprocessSet::Add(const string& command, bool use_console) {
1238 Subprocess *subprocess = new Subprocess(use_console);
1239 if (!subprocess->Start(this, command)) {
1240 delete subprocess;
1241 return 0;
1242 }
1243 if (subprocess->child_)
1244 running_.push_back(subprocess);
1245 else
1246 finished_.push(subprocess);
1247 return subprocess;
1248}
1249
1250bool SubprocessSet::DoWork() {
1251 DWORD bytes_read;
1252 Subprocess* subproc;
1253 OVERLAPPED* overlapped;
1254
1255 if (!GetQueuedCompletionStatus(ioport_, &bytes_read, (PULONG_PTR)&subproc,
1256 &overlapped, INFINITE)) {
1257 if (GetLastError() != ERROR_BROKEN_PIPE)
1258 Win32Fatal("GetQueuedCompletionStatus");
1259 }
1260
1261 if (!subproc) // A NULL subproc indicates that we were interrupted and is
1262 // delivered by NotifyInterrupted above.
1263 return true;
1264
1265 subproc->OnPipeReady();
1266
1267 if (subproc->Done()) {
1268 vector<Subprocess*>::iterator end =
1269 remove(running_.begin(), running_.end(), subproc);
1270 if (running_.end() != end) {
1271 finished_.push(subproc);
1272 running_.resize(end - running_.begin());
1273 }
1274 }
1275
1276 return false;
1277}
1278
1279void SubprocessSet::Clear() {
1280 for (vector<Subprocess*>::iterator i = running_.begin();
1281 i != running_.end(); ++i) {
1282 // Since the foreground process is in our process group, it will receive a
1283 // CTRL_C_EVENT or CTRL_BREAK_EVENT at the same time as us.
1284 if ((*i)->child_ && !(*i)->use_console_) {
1285 if (!GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT,
1286 GetProcessId((*i)->child_))) {
1287 Win32Fatal("GenerateConsoleCtrlEvent");
1288 }
1289 }
1290 }
1291 for (vector<Subprocess*>::iterator i = running_.begin();
1292 i != running_.end(); ++i)
1293 delete *i;
1294 running_.clear();
1295}
1296
1297*/
1298}
1299
1300#[cfg(unix)]
1301mod imp {
1302 /*
1303// Copyright 2012 Google Inc. All Rights Reserved.
1304//
1305// Licensed under the Apache License, Version 2.0 (the "License");
1306// you may not use this file except in compliance with the License.
1307// You may obtain a copy of the License at
1308//
1309// http://www.apache.org/licenses/LICENSE-2.0
1310//
1311// Unless required by applicable law or agreed to in writing, software
1312// distributed under the License is distributed on an "AS IS" BASIS,
1313// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1314// See the License for the specific language governing permissions and
1315// limitations under the License.
1316
1317#include "subprocess.h"
1318
1319#include <assert.h>
1320#include <errno.h>
1321#include <fcntl.h>
1322#include <poll.h>
1323#include <unistd.h>
1324#include <stdio.h>
1325#include <string.h>
1326#include <sys/wait.h>
1327#include <spawn.h>
1328
1329extern char** environ;
1330
1331#include "util.h"
1332
1333Subprocess::Subprocess(bool use_console) : fd_(-1), pid_(-1),
1334 use_console_(use_console) {
1335}
1336
1337Subprocess::~Subprocess() {
1338 if (fd_ >= 0)
1339 close(fd_);
1340 // Reap child if forgotten.
1341 if (pid_ != -1)
1342 Finish();
1343}
1344
1345bool Subprocess::Start(SubprocessSet* set, const string& command) {
1346 int output_pipe[2];
1347 if (pipe(output_pipe) < 0)
1348 Fatal("pipe: %s", strerror(errno));
1349 fd_ = output_pipe[0];
1350#if !defined(USE_PPOLL)
1351 // If available, we use ppoll in DoWork(); otherwise we use pselect
1352 // and so must avoid overly-large FDs.
1353 if (fd_ >= static_cast<int>(FD_SETSIZE))
1354 Fatal("pipe: %s", strerror(EMFILE));
1355#endif // !USE_PPOLL
1356 SetCloseOnExec(fd_);
1357
1358 posix_spawn_file_actions_t action;
1359 if (posix_spawn_file_actions_init(&action) != 0)
1360 Fatal("posix_spawn_file_actions_init: %s", strerror(errno));
1361
1362 if (posix_spawn_file_actions_addclose(&action, output_pipe[0]) != 0)
1363 Fatal("posix_spawn_file_actions_addclose: %s", strerror(errno));
1364
1365 posix_spawnattr_t attr;
1366 if (posix_spawnattr_init(&attr) != 0)
1367 Fatal("posix_spawnattr_init: %s", strerror(errno));
1368
1369 short flags = 0;
1370
1371 flags |= POSIX_SPAWN_SETSIGMASK;
1372 if (posix_spawnattr_setsigmask(&attr, &set->old_mask_) != 0)
1373 Fatal("posix_spawnattr_setsigmask: %s", strerror(errno));
1374 // Signals which are set to be caught in the calling process image are set to
1375 // default action in the new process image, so no explicit
1376 // POSIX_SPAWN_SETSIGDEF parameter is needed.
1377
1378 if (!use_console_) {
1379 // Put the child in its own process group, so ctrl-c won't reach it.
1380 flags |= POSIX_SPAWN_SETPGROUP;
1381 // No need to posix_spawnattr_setpgroup(&attr, 0), it's the default.
1382
1383 // Open /dev/null over stdin.
1384 if (posix_spawn_file_actions_addopen(&action, 0, "/dev/null", O_RDONLY,
1385 0) != 0) {
1386 Fatal("posix_spawn_file_actions_addopen: %s", strerror(errno));
1387 }
1388
1389 if (posix_spawn_file_actions_adddup2(&action, output_pipe[1], 1) != 0)
1390 Fatal("posix_spawn_file_actions_adddup2: %s", strerror(errno));
1391 if (posix_spawn_file_actions_adddup2(&action, output_pipe[1], 2) != 0)
1392 Fatal("posix_spawn_file_actions_adddup2: %s", strerror(errno));
1393 if (posix_spawn_file_actions_addclose(&action, output_pipe[1]) != 0)
1394 Fatal("posix_spawn_file_actions_addclose: %s", strerror(errno));
1395 // In the console case, output_pipe is still inherited by the child and
1396 // closed when the subprocess finishes, which then notifies ninja.
1397 }
1398#ifdef POSIX_SPAWN_USEVFORK
1399 flags |= POSIX_SPAWN_USEVFORK;
1400#endif
1401
1402 if (posix_spawnattr_setflags(&attr, flags) != 0)
1403 Fatal("posix_spawnattr_setflags: %s", strerror(errno));
1404
1405 const char* spawned_args[] = { "/bin/sh", "-c", command.c_str(), NULL };
1406 if (posix_spawn(&pid_, "/bin/sh", &action, &attr,
1407 const_cast<char**>(spawned_args), environ) != 0)
1408 Fatal("posix_spawn: %s", strerror(errno));
1409
1410 if (posix_spawnattr_destroy(&attr) != 0)
1411 Fatal("posix_spawnattr_destroy: %s", strerror(errno));
1412 if (posix_spawn_file_actions_destroy(&action) != 0)
1413 Fatal("posix_spawn_file_actions_destroy: %s", strerror(errno));
1414
1415 close(output_pipe[1]);
1416 return true;
1417}
1418
1419void Subprocess::OnPipeReady() {
1420 char buf[4 << 10];
1421 ssize_t len = read(fd_, buf, sizeof(buf));
1422 if (len > 0) {
1423 buf_.append(buf, len);
1424 } else {
1425 if (len < 0)
1426 Fatal("read: %s", strerror(errno));
1427 close(fd_);
1428 fd_ = -1;
1429 }
1430}
1431
1432ExitStatus Subprocess::Finish() {
1433 assert(pid_ != -1);
1434 int status;
1435 if (waitpid(pid_, &status, 0) < 0)
1436 Fatal("waitpid(%d): %s", pid_, strerror(errno));
1437 pid_ = -1;
1438
1439 if (WIFEXITED(status)) {
1440 int exit = WEXITSTATUS(status);
1441 if (exit == 0)
1442 return ExitSuccess;
1443 } else if (WIFSIGNALED(status)) {
1444 if (WTERMSIG(status) == SIGINT || WTERMSIG(status) == SIGTERM
1445 || WTERMSIG(status) == SIGHUP)
1446 return ExitInterrupted;
1447 }
1448 return ExitFailure;
1449}
1450
1451bool Subprocess::Done() const {
1452 return fd_ == -1;
1453}
1454
1455const string& Subprocess::GetOutput() const {
1456 return buf_;
1457}
1458
1459int SubprocessSet::interrupted_;
1460
1461void SubprocessSet::SetInterruptedFlag(int signum) {
1462 interrupted_ = signum;
1463}
1464
1465void SubprocessSet::HandlePendingInterruption() {
1466 sigset_t pending;
1467 sigemptyset(&pending);
1468 if (sigpending(&pending) == -1) {
1469 perror("ninja: sigpending");
1470 return;
1471 }
1472 if (sigismember(&pending, SIGINT))
1473 interrupted_ = SIGINT;
1474 else if (sigismember(&pending, SIGTERM))
1475 interrupted_ = SIGTERM;
1476 else if (sigismember(&pending, SIGHUP))
1477 interrupted_ = SIGHUP;
1478}
1479
1480SubprocessSet::SubprocessSet() {
1481 sigset_t set;
1482 sigemptyset(&set);
1483 sigaddset(&set, SIGINT);
1484 sigaddset(&set, SIGTERM);
1485 sigaddset(&set, SIGHUP);
1486 if (sigprocmask(SIG_BLOCK, &set, &old_mask_) < 0)
1487 Fatal("sigprocmask: %s", strerror(errno));
1488
1489 struct sigaction act;
1490 memset(&act, 0, sizeof(act));
1491 act.sa_handler = SetInterruptedFlag;
1492 if (sigaction(SIGINT, &act, &old_int_act_) < 0)
1493 Fatal("sigaction: %s", strerror(errno));
1494 if (sigaction(SIGTERM, &act, &old_term_act_) < 0)
1495 Fatal("sigaction: %s", strerror(errno));
1496 if (sigaction(SIGHUP, &act, &old_hup_act_) < 0)
1497 Fatal("sigaction: %s", strerror(errno));
1498}
1499
1500SubprocessSet::~SubprocessSet() {
1501 Clear();
1502
1503 if (sigaction(SIGINT, &old_int_act_, 0) < 0)
1504 Fatal("sigaction: %s", strerror(errno));
1505 if (sigaction(SIGTERM, &old_term_act_, 0) < 0)
1506 Fatal("sigaction: %s", strerror(errno));
1507 if (sigaction(SIGHUP, &old_hup_act_, 0) < 0)
1508 Fatal("sigaction: %s", strerror(errno));
1509 if (sigprocmask(SIG_SETMASK, &old_mask_, 0) < 0)
1510 Fatal("sigprocmask: %s", strerror(errno));
1511}
1512
1513Subprocess *SubprocessSet::Add(const string& command, bool use_console) {
1514 Subprocess *subprocess = new Subprocess(use_console);
1515 if (!subprocess->Start(this, command)) {
1516 delete subprocess;
1517 return 0;
1518 }
1519 running_.push_back(subprocess);
1520 return subprocess;
1521}
1522
1523#ifdef USE_PPOLL
1524bool SubprocessSet::DoWork() {
1525 vector<pollfd> fds;
1526 nfds_t nfds = 0;
1527
1528 for (vector<Subprocess*>::iterator i = running_.begin();
1529 i != running_.end(); ++i) {
1530 int fd = (*i)->fd_;
1531 if (fd < 0)
1532 continue;
1533 pollfd pfd = { fd, POLLIN | POLLPRI, 0 };
1534 fds.push_back(pfd);
1535 ++nfds;
1536 }
1537
1538 interrupted_ = 0;
1539 int ret = ppoll(&fds.front(), nfds, NULL, &old_mask_);
1540 if (ret == -1) {
1541 if (errno != EINTR) {
1542 perror("ninja: ppoll");
1543 return false;
1544 }
1545 return IsInterrupted();
1546 }
1547
1548 HandlePendingInterruption();
1549 if (IsInterrupted())
1550 return true;
1551
1552 nfds_t cur_nfd = 0;
1553 for (vector<Subprocess*>::iterator i = running_.begin();
1554 i != running_.end(); ) {
1555 int fd = (*i)->fd_;
1556 if (fd < 0)
1557 continue;
1558 assert(fd == fds[cur_nfd].fd);
1559 if (fds[cur_nfd++].revents) {
1560 (*i)->OnPipeReady();
1561 if ((*i)->Done()) {
1562 finished_.push(*i);
1563 i = running_.erase(i);
1564 continue;
1565 }
1566 }
1567 ++i;
1568 }
1569
1570 return IsInterrupted();
1571}
1572
1573#else // !defined(USE_PPOLL)
1574bool SubprocessSet::DoWork() {
1575 fd_set set;
1576 int nfds = 0;
1577 FD_ZERO(&set);
1578
1579 for (vector<Subprocess*>::iterator i = running_.begin();
1580 i != running_.end(); ++i) {
1581 int fd = (*i)->fd_;
1582 if (fd >= 0) {
1583 FD_SET(fd, &set);
1584 if (nfds < fd+1)
1585 nfds = fd+1;
1586 }
1587 }
1588
1589 interrupted_ = 0;
1590 int ret = pselect(nfds, &set, 0, 0, 0, &old_mask_);
1591 if (ret == -1) {
1592 if (errno != EINTR) {
1593 perror("ninja: pselect");
1594 return false;
1595 }
1596 return IsInterrupted();
1597 }
1598
1599 HandlePendingInterruption();
1600 if (IsInterrupted())
1601 return true;
1602
1603 for (vector<Subprocess*>::iterator i = running_.begin();
1604 i != running_.end(); ) {
1605 int fd = (*i)->fd_;
1606 if (fd >= 0 && FD_ISSET(fd, &set)) {
1607 (*i)->OnPipeReady();
1608 if ((*i)->Done()) {
1609 finished_.push(*i);
1610 i = running_.erase(i);
1611 continue;
1612 }
1613 }
1614 ++i;
1615 }
1616
1617 return IsInterrupted();
1618}
1619#endif // !defined(USE_PPOLL)
1620
1621Subprocess* SubprocessSet::NextFinished() {
1622 if (finished_.empty())
1623 return NULL;
1624 Subprocess* subproc = finished_.front();
1625 finished_.pop();
1626 return subproc;
1627}
1628
1629void SubprocessSet::Clear() {
1630 for (vector<Subprocess*>::iterator i = running_.begin();
1631 i != running_.end(); ++i)
1632 // Since the foreground process is in our process group, it will receive
1633 // the interruption signal (i.e. SIGINT or SIGTERM) at the same time as us.
1634 if (!(*i)->use_console_)
1635 kill(-(*i)->pid_, interrupted_);
1636 for (vector<Subprocess*>::iterator i = running_.begin();
1637 i != running_.end(); ++i)
1638 delete *i;
1639 running_.clear();
1640}
1641
1642*/
1643}
1644
1645/*
1646
1647// Copyright 2012 Google Inc. All Rights Reserved.
1648//
1649// Licensed under the Apache License, Version 2.0 (the "License");
1650// you may not use this file except in compliance with the License.
1651// You may obtain a copy of the License at
1652//
1653// http://www.apache.org/licenses/LICENSE-2.0
1654//
1655// Unless required by applicable law or agreed to in writing, software
1656// distributed under the License is distributed on an "AS IS" BASIS,
1657// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1658// See the License for the specific language governing permissions and
1659// limitations under the License.
1660
1661#include "subprocess.h"
1662
1663#include "test.h"
1664
1665#ifndef _WIN32
1666// SetWithLots need setrlimit.
1667#include <stdio.h>
1668#include <sys/time.h>
1669#include <sys/resource.h>
1670#include <unistd.h>
1671#endif
1672
1673namespace {
1674
1675#ifdef _WIN32
1676const char* kSimpleCommand = "cmd /c dir \\";
1677#else
1678const char* kSimpleCommand = "ls /";
1679#endif
1680
1681struct SubprocessTest : public testing::Test {
1682 SubprocessSet subprocs_;
1683};
1684
1685} // anonymous namespace
1686
1687// Run a command that fails and emits to stderr.
1688TEST_F(SubprocessTest, BadCommandStderr) {
1689 Subprocess* subproc = subprocs_.Add("cmd /c ninja_no_such_command");
1690 ASSERT_NE((Subprocess *) 0, subproc);
1691
1692 while (!subproc->Done()) {
1693 // Pretend we discovered that stderr was ready for writing.
1694 subprocs_.DoWork();
1695 }
1696
1697 EXPECT_EQ(ExitFailure, subproc->Finish());
1698 EXPECT_NE("", subproc->GetOutput());
1699}
1700
1701// Run a command that does not exist
1702TEST_F(SubprocessTest, NoSuchCommand) {
1703 Subprocess* subproc = subprocs_.Add("ninja_no_such_command");
1704 ASSERT_NE((Subprocess *) 0, subproc);
1705
1706 while (!subproc->Done()) {
1707 // Pretend we discovered that stderr was ready for writing.
1708 subprocs_.DoWork();
1709 }
1710
1711 EXPECT_EQ(ExitFailure, subproc->Finish());
1712 EXPECT_NE("", subproc->GetOutput());
1713#ifdef _WIN32
1714 ASSERT_EQ("CreateProcess failed: The system cannot find the file "
1715 "specified.\n", subproc->GetOutput());
1716#endif
1717}
1718
1719#ifndef _WIN32
1720
1721TEST_F(SubprocessTest, InterruptChild) {
1722 Subprocess* subproc = subprocs_.Add("kill -INT $$");
1723 ASSERT_NE((Subprocess *) 0, subproc);
1724
1725 while (!subproc->Done()) {
1726 subprocs_.DoWork();
1727 }
1728
1729 EXPECT_EQ(ExitInterrupted, subproc->Finish());
1730}
1731
1732TEST_F(SubprocessTest, InterruptParent) {
1733 Subprocess* subproc = subprocs_.Add("kill -INT $PPID ; sleep 1");
1734 ASSERT_NE((Subprocess *) 0, subproc);
1735
1736 while (!subproc->Done()) {
1737 bool interrupted = subprocs_.DoWork();
1738 if (interrupted)
1739 return;
1740 }
1741
1742 ASSERT_FALSE("We should have been interrupted");
1743}
1744
1745TEST_F(SubprocessTest, InterruptChildWithSigTerm) {
1746 Subprocess* subproc = subprocs_.Add("kill -TERM $$");
1747 ASSERT_NE((Subprocess *) 0, subproc);
1748
1749 while (!subproc->Done()) {
1750 subprocs_.DoWork();
1751 }
1752
1753 EXPECT_EQ(ExitInterrupted, subproc->Finish());
1754}
1755
1756TEST_F(SubprocessTest, InterruptParentWithSigTerm) {
1757 Subprocess* subproc = subprocs_.Add("kill -TERM $PPID ; sleep 1");
1758 ASSERT_NE((Subprocess *) 0, subproc);
1759
1760 while (!subproc->Done()) {
1761 bool interrupted = subprocs_.DoWork();
1762 if (interrupted)
1763 return;
1764 }
1765
1766 ASSERT_FALSE("We should have been interrupted");
1767}
1768
1769TEST_F(SubprocessTest, InterruptChildWithSigHup) {
1770 Subprocess* subproc = subprocs_.Add("kill -HUP $$");
1771 ASSERT_NE((Subprocess *) 0, subproc);
1772
1773 while (!subproc->Done()) {
1774 subprocs_.DoWork();
1775 }
1776
1777 EXPECT_EQ(ExitInterrupted, subproc->Finish());
1778}
1779
1780TEST_F(SubprocessTest, InterruptParentWithSigHup) {
1781 Subprocess* subproc = subprocs_.Add("kill -HUP $PPID ; sleep 1");
1782 ASSERT_NE((Subprocess *) 0, subproc);
1783
1784 while (!subproc->Done()) {
1785 bool interrupted = subprocs_.DoWork();
1786 if (interrupted)
1787 return;
1788 }
1789
1790 ASSERT_FALSE("We should have been interrupted");
1791}
1792
1793TEST_F(SubprocessTest, Console) {
1794 // Skip test if we don't have the console ourselves.
1795 if (isatty(0) && isatty(1) && isatty(2)) {
1796 Subprocess* subproc =
1797 subprocs_.Add("test -t 0 -a -t 1 -a -t 2", /*use_console=*/true);
1798 ASSERT_NE((Subprocess*)0, subproc);
1799
1800 while (!subproc->Done()) {
1801 subprocs_.DoWork();
1802 }
1803
1804 EXPECT_EQ(ExitSuccess, subproc->Finish());
1805 }
1806}
1807
1808#endif
1809
1810TEST_F(SubprocessTest, SetWithSingle) {
1811 Subprocess* subproc = subprocs_.Add(kSimpleCommand);
1812 ASSERT_NE((Subprocess *) 0, subproc);
1813
1814 while (!subproc->Done()) {
1815 subprocs_.DoWork();
1816 }
1817 ASSERT_EQ(ExitSuccess, subproc->Finish());
1818 ASSERT_NE("", subproc->GetOutput());
1819
1820 ASSERT_EQ(1u, subprocs_.finished_.size());
1821}
1822
1823TEST_F(SubprocessTest, SetWithMulti) {
1824 Subprocess* processes[3];
1825 const char* kCommands[3] = {
1826 kSimpleCommand,
1827#ifdef _WIN32
1828 "cmd /c echo hi",
1829 "cmd /c time /t",
1830#else
1831 "whoami",
1832 "pwd",
1833#endif
1834 };
1835
1836 for (int i = 0; i < 3; ++i) {
1837 processes[i] = subprocs_.Add(kCommands[i]);
1838 ASSERT_NE((Subprocess *) 0, processes[i]);
1839 }
1840
1841 ASSERT_EQ(3u, subprocs_.running_.size());
1842 for (int i = 0; i < 3; ++i) {
1843 ASSERT_FALSE(processes[i]->Done());
1844 ASSERT_EQ("", processes[i]->GetOutput());
1845 }
1846
1847 while (!processes[0]->Done() || !processes[1]->Done() ||
1848 !processes[2]->Done()) {
1849 ASSERT_GT(subprocs_.running_.size(), 0u);
1850 subprocs_.DoWork();
1851 }
1852
1853 ASSERT_EQ(0u, subprocs_.running_.size());
1854 ASSERT_EQ(3u, subprocs_.finished_.size());
1855
1856 for (int i = 0; i < 3; ++i) {
1857 ASSERT_EQ(ExitSuccess, processes[i]->Finish());
1858 ASSERT_NE("", processes[i]->GetOutput());
1859 delete processes[i];
1860 }
1861}
1862
1863#if defined(USE_PPOLL)
1864TEST_F(SubprocessTest, SetWithLots) {
1865 // Arbitrary big number; needs to be over 1024 to confirm we're no longer
1866 // hostage to pselect.
1867 const unsigned kNumProcs = 1025;
1868
1869 // Make sure [ulimit -n] isn't going to stop us from working.
1870 rlimit rlim;
1871 ASSERT_EQ(0, getrlimit(RLIMIT_NOFILE, &rlim));
1872 if (rlim.rlim_cur < kNumProcs) {
1873 printf("Raise [ulimit -n] above %u (currently %lu) to make this test go\n",
1874 kNumProcs, rlim.rlim_cur);
1875 return;
1876 }
1877
1878 vector<Subprocess*> procs;
1879 for (size_t i = 0; i < kNumProcs; ++i) {
1880 Subprocess* subproc = subprocs_.Add("/bin/echo");
1881 ASSERT_NE((Subprocess *) 0, subproc);
1882 procs.push_back(subproc);
1883 }
1884 while (!subprocs_.running_.empty())
1885 subprocs_.DoWork();
1886 for (size_t i = 0; i < procs.size(); ++i) {
1887 ASSERT_EQ(ExitSuccess, procs[i]->Finish());
1888 ASSERT_NE("", procs[i]->GetOutput());
1889 }
1890 ASSERT_EQ(kNumProcs, subprocs_.finished_.size());
1891}
1892#endif // !__APPLE__ && !_WIN32
1893
1894// TODO: this test could work on Windows, just not sure how to simply
1895// read stdin.
1896#ifndef _WIN32
1897// Verify that a command that attempts to read stdin correctly thinks
1898// that stdin is closed.
1899TEST_F(SubprocessTest, ReadStdin) {
1900 Subprocess* subproc = subprocs_.Add("cat -");
1901 while (!subproc->Done()) {
1902 subprocs_.DoWork();
1903 }
1904 ASSERT_EQ(ExitSuccess, subproc->Finish());
1905 ASSERT_EQ(1u, subprocs_.finished_.size());
1906}
1907#endif // _WIN32
1908
1909*/