1313#include < sys/time.h>
1414#include < sys/types.h>
1515#include < sys/wait.h>
16+ #include < sys/poll.h>
1617#include < dlfcn.h>
1718
1819#include < fcntl.h> /* Obtain O_* constant definitions */
3132 * this is redundant in non-Solaris builds but fixes Solaris */
3233extern char ** environ;
3334
34-
3535#ifdef TRUE
3636#undef TRUE
3737#endif
@@ -56,7 +56,7 @@ string strerror (int _code, const string & _message)
5656 message << _message << " : " << buffer.data ();
5757 }
5858 else {
59- message << _message << " : system error message could not be fetched" ;
59+ message << _message << " : system error message could not be fetched, errno = " << _code ;
6060 }
6161
6262 return message.str ();
@@ -96,7 +96,7 @@ static void exit_on_failure ()
9696{
9797 void * process_handle = dlopen (NULL , RTLD_NOW);
9898 void * exit_handle = dlsym (process_handle, " exit" );
99-
99+
100100 // it's hard to imagine a situation where this symbol would not be
101101 // present; regardless, we cause a SEGMENTATION error because the
102102 // child needs to die;
@@ -107,7 +107,7 @@ static void exit_on_failure ()
107107 *(int *)exit_handle = 0 ;
108108 ++ret; // hide compiler warning
109109 }
110-
110+
111111 typedef void (* exit_t )(int );
112112 exit_t exit_fun = (exit_t )exit_handle;
113113 exit_fun (EXIT_FAILURE);
@@ -118,7 +118,7 @@ static void exit_on_failure ()
118118
119119
120120/*
121- * Duplicate handle and zero the original
121+ * Duplicate handle and zero the original
122122 */
123123inline void dup2 (int _from, int _to) {
124124 if (::dup2 (_from, _to) < 0 ) {
@@ -215,6 +215,7 @@ void process_handle_t::spawn (const char * _command, char *const _arguments[],
215215 throw subprocess_exception (EALREADY, " process already started" );
216216 }
217217
218+ int rc = 0 ;
218219 // can be addressed with PIPE_STDIN, PIPE_STDOUT, PIPE_STDERR
219220 pipe_holder pipes[3 ];
220221
@@ -247,7 +248,7 @@ void process_handle_t::spawn (const char * _command, char *const _arguments[],
247248 if (_termination_mode == TERMINATION_GROUP) {
248249 setsid ();
249250 }
250-
251+
251252 /* if environment is empty, use parent's environment */
252253 if (!_environment) {
253254 _environment = environ;
@@ -284,6 +285,9 @@ void process_handle_t::spawn (const char * _command, char *const _arguments[],
284285 pipes[PIPE_STDIN][pipe_holder::WRITE] = HANDLE_CLOSED;
285286 pipes[PIPE_STDOUT][pipe_holder::READ] = HANDLE_CLOSED;
286287 pipes[PIPE_STDERR][pipe_holder::READ] = HANDLE_CLOSED;
288+
289+ // update process state
290+ wait (TIMEOUT_IMMEDIATE);
287291}
288292
289293
@@ -346,75 +350,57 @@ struct enable_block_mode {
346350 enable_block_mode (int _fd) : fd (_fd) {
347351 set_block (fd);
348352 }
349-
353+
350354 ~enable_block_mode () {
351355 set_non_block (fd);
352356 }
353357};
354358
355359
356- struct select_reader {
357-
358- fd_set set;
359- int max_fd;
360-
361- select_reader () : max_fd(0 ) { }
362-
363- void put_fd (int _fd) {
364- FD_SET (_fd, &set);
365- max_fd = std::max (max_fd, _fd);
366- }
367-
368- ssize_t timed_read (process_handle_t & _handle, pipe_type _pipe, int _timeout)
369- {
370- // this should never be called with "infinite" timeout
371- if (_timeout < 0 )
372- return -1 ;
373-
374- FD_ZERO (&set);
375- if (_pipe & PIPE_STDOUT) {
376- put_fd (_handle.pipe_stdout );
377- _handle.stdout_ .clear ();
378- }
379- if (_pipe & PIPE_STDERR) {
380- put_fd (_handle.pipe_stderr );
381- _handle.stderr_ .clear ();
382- }
383-
384- struct timeval timeout;
385- int start = clock_millisec (), timediff;
386- ssize_t rc;
387-
388- do {
389- timediff = _timeout - (clock_millisec () - start);
390-
391- // use max so that _timeout can be TIMEOUT_IMMEDIATE and yet
392- // select can be tried at least once
393- timeout.tv_sec = std::max (0 , timediff/1000 );
394- timeout.tv_usec = std::max (0 , (timediff % 1000 ) * 1000 );
395-
396- rc = select (max_fd + 1 , &set, NULL , NULL , &timeout);
397- if (rc == -1 && errno != EINTR && errno != EAGAIN)
398- return -1 ;
399-
400- } while (rc == 0 && timediff > 0 );
401-
402- // nothing to read; if errno == EINTR try reading one last time
403- if (rc == 0 || errno == EAGAIN)
404- return 0 ;
405-
406- if (FD_ISSET (_handle.pipe_stdout , &set)) {
407- rc = std::min (rc, (ssize_t )_handle.stdout_ .read (_handle.pipe_stdout , mbcslocale));
408- }
409- if (FD_ISSET (_handle.pipe_stderr , &set)) {
410- rc = std::min (rc, (ssize_t )_handle.stderr_ .read (_handle.pipe_stderr , mbcslocale));
360+ ssize_t timed_read (process_handle_t & _handle, pipe_type _pipe, int _timeout)
361+ {
362+ struct pollfd fds[2 ] { { .fd = -1 }, { .fd = -1 } };
363+
364+ if (_pipe & PIPE_STDOUT) {
365+ fds[0 ].fd = _handle.pipe_stdout ;
366+ fds[0 ].events = POLLIN;
367+ _handle.stdout_ .clear ();
368+ }
369+
370+ if (_pipe & PIPE_STDERR) {
371+ fds[1 ].fd = _handle.pipe_stderr ;
372+ fds[1 ].events = POLLIN;
373+ _handle.stderr_ .clear ();
374+ }
375+
376+ time_t start = clock_millisec (), timediff = _timeout;
377+ ssize_t rc;
378+
379+ do {
380+ rc = poll (fds, 2 , timediff);
381+ timediff = _timeout - (clock_millisec () - start);
382+
383+ // interrupted or kernel failed to allocate internal resources
384+ if (rc < 0 && (errno == EINTR || errno == EAGAIN)) {
385+ rc = 0 ;
411386 }
412-
413- return rc;
387+ } while (rc == 0 && timediff > 0 );
388+
389+ // nothing to read
390+ if (rc == 0 ) {
391+ return 0 ;
414392 }
415- };
416393
394+ // TODO if an error occurs in the first read() it will be lost
395+ if (fds[0 ].fd != -1 && fds[0 ].revents == POLLIN) {
396+ rc = std::min (rc, (ssize_t )_handle.stdout_ .read (_handle.pipe_stdout , mbcslocale));
397+ }
398+ if (fds[1 ].fd != -1 && fds[1 ].revents == POLLIN) {
399+ rc = std::min (rc, (ssize_t )_handle.stderr_ .read (_handle.pipe_stderr , mbcslocale));
400+ }
417401
402+ return rc;
403+ }
418404
419405
420406size_t process_handle_t::read (pipe_type _pipe, int _timeout)
@@ -423,25 +409,7 @@ size_t process_handle_t::read (pipe_type _pipe, int _timeout)
423409 throw subprocess_exception (ECHILD, " child does not exist" );
424410 }
425411
426- ssize_t rc;
427- select_reader reader;
428-
429- // infinite timeout
430- if (_timeout == TIMEOUT_INFINITE) {
431- enable_block_mode blocker_out (pipe_stdout);
432- enable_block_mode blocker_err (pipe_stderr);
433- rc = reader.timed_read (*this , _pipe, 1000 );
434- }
435- // finite or no timeout
436- else {
437- rc = reader.timed_read (*this , _pipe, _timeout);
438-
439- if (rc < 0 && errno == EAGAIN) {
440- /* stdin pipe is opened with O_NONBLOCK, so this means "would block" */
441- errno = 0 ;
442- return 0 ;
443- }
444- }
412+ ssize_t rc = timed_read (*this , _pipe, _timeout);
445413
446414 if (rc < 0 ) {
447415 throw subprocess_exception (errno, " could not read from child process" );
@@ -476,16 +444,17 @@ void process_handle_t::wait (int _timeout)
476444 return ;
477445 }
478446
479- /* to wait or not to wait? */
447+ /* to wait or not to wait? */
480448 int options = 0 ;
481- if (_timeout >= 0 )
449+ if (_timeout >= 0 ) {
482450 options = WNOHANG;
483-
451+ }
452+
484453 /* make the actual system call */
485454 int start = clock_millisec (), rc;
486455 do {
487456 rc = waitpid (child_id, &return_code, options);
488-
457+
489458 // there's been an error (<0)
490459 if (rc < 0 ) {
491460 throw subprocess_exception (errno, " waitpid() failed" );
@@ -495,7 +464,9 @@ void process_handle_t::wait (int _timeout)
495464 } while (rc == 0 && _timeout > 0 );
496465
497466 // the child is still running
498- if (rc == 0 ) return ;
467+ if (rc == 0 ) {
468+ return ;
469+ }
499470
500471 // the child has exited or has been terminated
501472 if (WIFEXITED (return_code)) {
@@ -520,7 +491,6 @@ void process_handle_t::send_signal(int _signal)
520491 if (!child_id) {
521492 throw subprocess_exception (ECHILD, " child does not exist" );
522493 }
523-
524494 int rc = ::kill (child_id, _signal);
525495
526496 if (rc < 0 ) {
@@ -580,7 +550,7 @@ int main (int argc, char ** argv)
580550 handle.spawn (" /bin/bash" , args, env, NULL , process_handle_t ::TERMINATION_GROUP);
581551
582552 process_write (&handle, " echo A\n " , 7 );
583-
553+
584554 /* read is non-blocking so the child needs time to produce output */
585555 sleep (1 );
586556 process_read (handle, PIPE_STDOUT, TIMEOUT_INFINITE);
0 commit comments