1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2004-2008 Jean Privat <jean@pryen.org>
4 # Copyright 2008 Floréal Morandat <morandat@lirmm.fr>
6 # This file is free software, which comes along with NIT. This software is
7 # distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
8 # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
9 # PARTICULAR PURPOSE. You can modify it is you want, provided this header
10 # is kept unaltered, and a notification of the changes is added.
11 # You are allowed to redistribute it and sell it, alone or is a part of
14 # Invocation and management of operating system sub-processes.
15 # Standard input and output can be handled through streams.
27 #include <sys/types.h>
36 typedef struct se_exec_data se_exec_data_t;
53 # The pid of the process
54 fun id
: Int do return data
.id
56 # Is the process finished?
57 fun is_finished
: Bool do return data
.is_finished
59 # Wait the termination of the process
66 # The status once finished
68 # Require: `is_finished`
75 # The target executable
76 # Either a file path or the name of an executable available in PATH.
79 # The arguments of the command
80 # Starts with the first real arguments---ie. does not include the progname (`argv[0]`, in C)
81 var arguments
: nullable Array[Text]
83 # Launch a command with some arguments
84 init(command
: Text, arguments
: Text...) is old_style_init
do
85 self.command
= command
86 self.arguments
= arguments
90 # Launch a simple command with arguments passed as an array
91 init from_a
(command
: Text, arguments
: nullable Array[Text])
93 self.command
= command
94 self.arguments
= arguments
98 # Flags used internally to know which pipe to open
99 private fun pipeflags
: Int do return 0
101 # Internal code to handle execution
102 protected fun execute
104 var arguments
= self.arguments
106 var args
= new FlatBuffer
109 if not is_windows
then
110 # Pass the arguments as a big C string where elements are separated with '\0'
112 if arguments
!= null then
113 for a
in arguments
do
117 argc
+= arguments
.length
120 # Combine the program and args in a single string
121 assert not command
.chars
.has
('"')
122 args
= new FlatBuffer
128 if arguments
!= null then
129 for a
in arguments
do
131 args.append a.replace('"', "\\\"")
137 data = basic_exec_execute(command.to_cstring, args.to_s.to_cstring, argc, pipeflags)
138 assert not data.address_is_null else print_error "Internal error executing
: {command}"
141 private var data: NativeProcess
143 private fun basic_exec_execute(prog, args: CString, argc: Int, pipeflag: Int): NativeProcess `{
145 SECURITY_ATTRIBUTES sec_attr;
146 sec_attr.nLength = sizeof(SECURITY_ATTRIBUTES);
147 sec_attr.bInheritHandle = TRUE;
148 sec_attr.lpSecurityDescriptor = NULL;
150 STARTUPINFO start_info;
151 ZeroMemory(&start_info, sizeof(STARTUPINFO));
152 start_info.cb = sizeof(STARTUPINFO);
153 start_info.dwFlags = STARTF_USESTDHANDLES;
159 se_exec_data_t *result = (se_exec_data_t*)malloc(sizeof(se_exec_data_t));
163 if (!CreatePipe(&in_fd[0], &in_fd[1], &sec_attr, 0)) {
166 start_info.hStdInput = in_fd[0];
167 result->in_fd = _open_osfhandle((intptr_t)in_fd[1], _O_WRONLY);
168 if ( !SetHandleInformation(in_fd[1], HANDLE_FLAG_INHERIT, 0) )
171 start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
177 if (!CreatePipe(&out_fd[0], &out_fd[1], &sec_attr, 0)) {
180 start_info.hStdOutput = out_fd[1];
181 result->out_fd = _open_osfhandle((intptr_t)out_fd[0], _O_RDONLY);
182 if ( !SetHandleInformation(out_fd[0], HANDLE_FLAG_INHERIT, 0) )
185 start_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
191 if (!CreatePipe(&err_fd[0], &err_fd[1], &sec_attr, 0)) {
194 start_info.hStdError = err_fd[1];
195 result->err_fd = _open_osfhandle((intptr_t)err_fd[0], _O_RDONLY);
196 if ( !SetHandleInformation(err_fd[0], HANDLE_FLAG_INHERIT, 0) )
199 start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
203 PROCESS_INFORMATION proc_info;
204 ZeroMemory(&proc_info, sizeof(PROCESS_INFORMATION));
206 BOOL created = CreateProcess(NULL,
207 args, // command line
208 NULL, // process security attributes
209 NULL, // primary thread security attributes
210 TRUE, // inherit handles
212 NULL, // use parent's environment
213 NULL, // use parent's current directory
217 if (pipeflag & 1) CloseHandle(in_fd[0]);
218 if (pipeflag & 2) CloseHandle(out_fd[1]);
219 if (pipeflag & 3) CloseHandle(err_fd[1]);
224 result->status = 127;
226 // Close subprocess pipes
227 if (pipeflag & 1) CloseHandle(in_fd[1]);
228 if (pipeflag & 2) CloseHandle(out_fd[0]);
229 if (pipeflag & 3) CloseHandle(err_fd[0]);
231 result->h_process = proc_info.hProcess;
232 result->h_thread = proc_info.hThread;
233 result->id = GetProcessId(proc_info.hProcess);
239 se_exec_data_t* result = NULL;
245 int res = pipe(in_fd);
251 int res = pipe(out_fd);
257 int res = pipe(err_fd);
266 char **arg = malloc(sizeof(char*) * (argc+1));
271 for(i=0; i<argc; i++)
307 result = (se_exec_data_t*)malloc(sizeof(se_exec_data_t));
312 result->in_fd = in_fd[1];
319 result->out_fd = out_fd[0];
326 result->err_fd = err_fd[0];
337 # `Process` on which the `stdout` is readable like a `Reader`
342 # File Descriptor used for the input.
343 var stream_in: FileReader is noinit
345 redef fun close do stream_in.close
347 redef fun read_char do return stream_in.read_char
349 redef fun read_byte do return stream_in.read_byte
351 redef fun eof do return stream_in.eof
353 redef fun pipeflags do return 2
358 stream_in = new FileReader.from_fd(data.out_fd)
362 # `Process` on which `stdin` is writable like a `Writer`
367 # File Descriptor used for the output.
368 var stream_out: Writer is noinit
370 redef fun close do stream_out.close
372 redef fun is_writable do return stream_out.is_writable
374 redef fun write(s) do stream_out.write(s)
376 redef fun pipeflags do return 1
381 var out = new FileWriter.from_fd(data.in_fd)
382 out.set_buffering_mode(0, sys.buffer_mode_none)
387 # `Process` on which stdout can be read and stdin can be written to like a `Duplex`
399 redef fun pipeflags do return 3
401 redef fun execute do super
403 # Write `input` to process and return its output
405 # Writing and reading are processed line by line,
406 # reading only when something is available.
409 # var proc = new ProcessDuplex("tr
", "[:lower
:]", "[:upper
:]")
410 # assert proc.write_and_read("""
418 fun write_and_read(input: Text): String
420 var read = new Buffer
422 # Main loop, read and write line by line
424 for delimiter in input.search_all('\n') do
425 write input.substring(prev, delimiter.after-prev)
426 prev = delimiter.after
428 while stream_in.poll_in do
429 read.append stream_in.read_line
433 # Write the last line
434 write input.substring_from(prev)
437 # Read the rest, may be everything for some programs
438 read.append stream_in.read_all
448 # Execute a shell command and return its error code
449 fun system(command: Text): Int
451 return command.to_cstring.system
454 # The pid of the program
455 fun pid: Int `{ return getpid(); `}
459 # Execute self as a shell command.
461 # See the posix function system(3).
463 int status = system(self);
465 if (WIFSIGNALED(status) && WTERMSIG(status) == SIGINT) {
466 // system exited on SIGINT: in my opinion the user wants the main to be discontinued
467 kill(getpid(), SIGINT);
474 private extern class NativeProcess `{ se_exec_data_t* `}
476 fun id: Int `{ return (long)self->id; `}
477 fun status: Int `{ return self->status; `}
478 fun in_fd: Int `{ return self->in_fd; `}
479 fun out_fd: Int `{ return self->out_fd; `}
480 fun err_fd: Int `{ return self->err_fd; `}
482 fun is_finished: Bool `{
486 if (WaitForSingleObject(self->h_process, 0) == 0) {
487 /* child is finished */
490 long unsigned int status;
491 GetExitCodeProcess(self->h_process, &status);
493 self->status = (int)status;
495 CloseHandle(self->h_process);
496 CloseHandle(self->h_thread);
500 int id = waitpid(self->id, &status, WNOHANG);
502 /* child is finished */
503 result = (int)(id == self->id);
504 self->status = WEXITSTATUS(status);
517 long unsigned int status;
519 WaitForSingleObject(self->h_process, INFINITE);
520 GetExitCodeProcess(self->h_process, &status);
522 CloseHandle(self->h_process);
523 CloseHandle(self->h_thread);
525 self->status = (int)status;
531 waitpid(self->id, &status, 0);
532 self->status = WEXITSTATUS(status);