diff options
Diffstat (limited to 'src/libutil')
-rw-r--r-- | src/libutil/util.cc | 84 | ||||
-rw-r--r-- | src/libutil/util.hh | 14 |
2 files changed, 98 insertions, 0 deletions
diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 9e3e0bae20df..a8ad3fe4807b 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -13,6 +13,10 @@ #include <fcntl.h> #include <signal.h> +#ifdef __CYGWIN__ +#include <windows.h> +#endif + #include "util.hh" @@ -434,6 +438,23 @@ void writeFull(int fd, const unsigned char * buf, size_t count) } +string drainFD(int fd) +{ + string result; + unsigned char buffer[4096]; + while (1) { + ssize_t rd = read(fd, buffer, sizeof buffer); + if (rd == -1) { + if (errno != EINTR) + throw SysError("reading from file"); + } + else if (rd == 0) break; + else result.append((char *) buffer, rd); + } + return result; +} + + ////////////////////////////////////////////////////////////////////// @@ -646,6 +667,69 @@ void Pid::setSeparatePG(bool separatePG) ////////////////////////////////////////////////////////////////////// +string runProgram(Path program) +{ + /* Create a pipe. */ + Pipe pipe; + pipe.create(); + + /* Fork. */ + Pid pid; + pid = fork(); + switch (pid) { + + case -1: + throw SysError("unable to fork"); + + case 0: /* child */ + try { + pipe.readSide.close(); + + if (dup2(pipe.writeSide, STDOUT_FILENO) == -1) + throw SysError("dupping from-hook write side"); + + execl(program.c_str(), program.c_str(), (char *) 0); + throw SysError(format("executing `%1%'") % program); + + } catch (exception & e) { + cerr << "error: " << e.what() << endl; + } + quickExit(1); + } + + /* Parent. */ + + pipe.writeSide.close(); + + string result = drainFD(pipe.readSide); + + /* Wait for the child to finish. */ + int status = pid.wait(true); + if (!statusOk(status)) + throw Error(format("program `%1% %2%") + % program % statusToString(status)); + + return result; +} + + +void quickExit(int status) +{ +#ifdef __CYGWIN__ + /* Hack for Cygwin: _exit() doesn't seem to work quite right, + since some Berkeley DB code appears to be called when a child + exits through _exit() (e.g., because execve() failed). So call + the Windows API directly. */ + ExitProcess(status); +#else + _exit(status); +#endif +} + + +////////////////////////////////////////////////////////////////////// + + volatile sig_atomic_t _isInterrupted = 0; void _interrupted() diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 92bdf50d31f7..fcf995af8a30 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -185,6 +185,11 @@ void readFull(int fd, unsigned char * buf, size_t count); void writeFull(int fd, const unsigned char * buf, size_t count); +/* Read a file descriptor until EOF occurs. */ +string drainFD(int fd); + + + /* Automatic cleanup of resources. */ class AutoDelete @@ -249,6 +254,15 @@ public: }; +/* Run a program and return its stdout in a string (i.e., like the + shell backtick operator). */ +string runProgram(Path program); + +/* Wrapper around _exit() on Unix and ExitProcess() on Windows. (On + Cygwin, _exit() doesn't seem to do the right thing.) */ +void quickExit(int status); + + /* User interruption. */ extern volatile sig_atomic_t _isInterrupted; |