| 248 | |
| 249 | |
| 250 | Future<Nothing> redirect( |
| 251 | int_fd from, |
| 252 | Option<int_fd> to, |
| 253 | size_t chunk, |
| 254 | const vector<lambda::function<void(const string&)>>& hooks) |
| 255 | { |
| 256 | // Make sure we've got "valid" file descriptors. |
| 257 | if (from < 0 || (to.isSome() && to.get() < 0)) { |
| 258 | return Failure(os::strerror(EBADF)); |
| 259 | } |
| 260 | |
| 261 | if (to.isNone()) { |
| 262 | // Open up /dev/null that we can splice into. |
| 263 | Try<int_fd> open = os::open(os::DEV_NULL, O_WRONLY | O_CLOEXEC); |
| 264 | |
| 265 | if (open.isError()) { |
| 266 | return Failure("Failed to open /dev/null for writing: " + open.error()); |
| 267 | } |
| 268 | |
| 269 | to = open.get(); |
| 270 | } else { |
| 271 | // Duplicate 'to' so that we're in control of its lifetime. |
| 272 | Try<int_fd> dup = os::dup(to.get()); |
| 273 | if (dup.isError()) { |
| 274 | return Failure(dup.error()); |
| 275 | } |
| 276 | |
| 277 | to = dup.get(); |
| 278 | } |
| 279 | |
| 280 | CHECK_SOME(to); |
| 281 | |
| 282 | // Duplicate 'from' so that we're in control of its lifetime. |
| 283 | Try<int_fd> dup = os::dup(from); |
| 284 | if (dup.isError()) { |
| 285 | os::close(to.get()); |
| 286 | return Failure(ErrnoError("Failed to duplicate 'from' file descriptor")); |
| 287 | } |
| 288 | |
| 289 | from = dup.get(); |
| 290 | |
| 291 | // Set the close-on-exec flag (no-op if already set). |
| 292 | Try<Nothing> cloexec = os::cloexec(from); |
| 293 | if (cloexec.isError()) { |
| 294 | os::close(from); |
| 295 | os::close(to.get()); |
| 296 | return Failure("Failed to set close-on-exec on 'from': " + cloexec.error()); |
| 297 | } |
| 298 | |
| 299 | cloexec = os::cloexec(to.get()); |
| 300 | if (cloexec.isError()) { |
| 301 | os::close(from); |
| 302 | os::close(to.get()); |
| 303 | return Failure("Failed to set close-on-exec on 'to': " + cloexec.error()); |
| 304 | } |
| 305 | |
| 306 | Try<Nothing> async = prepare_async(from); |
| 307 | if (async.isError()) { |