M README.md => README.md +5 -1
@@ 11,11 11,15 @@ after-the-fact via a Unix socket. Useful when trying to obtain data about a
## Usage
- <program> 2>&1 | blackbox
+ blackbox -- <program>
`<program>` can be used normally for extended periods of time. blackbox will
print instructions that can be used to grab logs when the bug is reproduced.
+blackbox can also read from the standard input:
+
+ <program> 2>&1 | blackbox
+
## License
AGPLv3, see LICENSE.
M main.c => main.c +62 -2
@@ 6,6 6,7 @@
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include <sys/wait.h>
#include <unistd.h>
struct ring_buffer {
@@ 76,12 77,71 @@ int main(int argc, char *argv[]) {
// TODO: add more useful options
switch (opt) {
default:
- fprintf(stderr, "usage: blackbox\n");
+ fprintf(stderr, "usage: blackbox [command...]\n");
return 1;
}
}
- int in_fd = STDIN_FILENO;
+ int in_fd;
+ if (optind < argc) {
+ char **cmd = &argv[optind];
+
+ int child_pipe[2];
+ if (pipe(child_pipe) != 0) {
+ perror("pipe failed");
+ return 1;
+ }
+
+ pid_t pid = fork();
+ if (pid < 0) {
+ perror("fork failed");
+ return 1;
+ } else if (pid == 0) {
+ close(child_pipe[0]);
+
+ // Redirect stdout to the pipe
+ if (dup2(child_pipe[1], STDOUT_FILENO) < 0) {
+ perror("dup2 failed");
+ _exit(1);
+ }
+ close(child_pipe[1]);
+
+ // Then redirect stderr to stdout
+ if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0) {
+ perror("dup2 failed");
+ _exit(1);
+ }
+
+ // Fork again to avoid SIGCHLD
+ pid = fork();
+ if (pid < 0) {
+ perror("fork failed");
+ _exit(1);
+ } else if (pid == 0) {
+ execvp(cmd[0], cmd);
+ perror("execvp failed");
+ _exit(1);
+ }
+
+ _exit(0);
+ }
+
+ close(child_pipe[1]);
+ in_fd = child_pipe[0];
+
+ int stat;
+ if (waitpid(pid, &stat, 0) < 0) {
+ perror("waitpid failed");
+ return 1;
+ }
+ if (stat != 0) {
+ fprintf(stderr, "failed to spawn child process\n");
+ return 1;
+ }
+ } else {
+ in_fd = STDIN_FILENO;
+ }
+
if (set_nonblock(in_fd) != 0) {
return 1;
}