summaryrefslogtreecommitdiff
path: root/mispipe.c
diff options
context:
space:
mode:
authorjoeyh <joeyh>2006-09-08 00:33:43 +0000
committerjoeyh <joeyh>2006-09-08 00:33:43 +0000
commit7b6c6008e0e3f2a88c974cf8bea0545037626d2c (patch)
treef51ed58d764704a56c9da05c73508d0e90378c05 /mispipe.c
parent79881487962d274e73b475458b6a1847789ca562 (diff)
downloadmoreutils-7b6c6008e0e3f2a88c974cf8bea0545037626d2c.tar.gz
* Add mispipe, contributed by Nathanael Nerode. Pipes together two commands,
returning the exit status of the first.
Diffstat (limited to 'mispipe.c')
-rw-r--r--mispipe.c179
1 files changed, 179 insertions, 0 deletions
diff --git a/mispipe.c b/mispipe.c
new file mode 100644
index 0000000..43ba76a
--- /dev/null
+++ b/mispipe.c
@@ -0,0 +1,179 @@
+/*
+ * mispipe: written by Nathanael Nerode.
+ *
+ * Copyright 2004 Nathanael Nerode.
+ *
+ * Licensed under the GPL version 2 or above, and dual-licensed under the
+ * following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/*
+ * Usage: mispipe <command1> <command2>
+ * Run <command1> | <command2>, but return with the exit status of <command1>.
+ *
+ * This is designed for a very specific purpose: logging.
+ * "foo | logger -t foo"
+ * will return with the exit status of logger, not that of foo.
+ * "mispipe foo 'logger -t foo'"
+ * will return with the exit status of foo.
+ */
+
+/*
+ * To do:
+ * Make this into a fancy, internationalized, option-handling hellbeast.
+ * (But why bother? It does its job quite well.)
+ */
+
+#include <errno.h> /* errno */
+#include <sys/types.h>
+#include <unistd.h> /* pipe(), fork(),... */
+#include <stdlib.h> /* system() */
+#include <sys/wait.h> /* waitpid(), etc. */
+#include <stdio.h>
+#include <stdarg.h> /* va_list, for error() */
+
+static const char* progname; /* Stores argv[0] */
+static int filedes[2]; /* Stores pipe file descriptors */
+
+/* Subroutine for 'warning' and 'error' which prefixes progname */
+static void warning_prefix(void) {
+ fputs(progname, stderr);
+ fputs(": ", stderr);
+}
+
+/* Issue a warning, then die */
+__attribute__(( noreturn, format (printf, 1, 2) ))
+static void error(const char* formatstr, ...) {
+ va_list ap;
+ va_start(ap, formatstr);
+ warning_prefix();
+ vfprintf(stderr, formatstr, ap);
+ va_end(ap);
+ exit(1);
+}
+
+/* Issue a warning, then die, with errno */
+__attribute__(( noreturn ))
+static void error_with_errno(const char* message) {
+ int saved_errno;
+ saved_errno=errno;
+ warning_prefix();
+ fputs(message, stderr);
+ fputs(": error number ", stderr);
+ fprintf(stderr, "%i\n", saved_errno);
+ exit(1);
+}
+
+/* Convert 'wait'/'system'-style exit status to 'exit'-style exit status */
+__attribute__(( const ))
+static int shorten_status(int status_big) {
+ if (WIFEXITED(status_big))
+ return WEXITSTATUS(status_big);
+ if (WIFSIGNALED(status_big))
+ return 128+WTERMSIG(status_big);
+ if (WIFSTOPPED(status_big))
+ return 128+WSTOPSIG(status_big);
+#ifdef WCOREDUMP
+ if (WCOREDUMP(status_big))
+ error("Ow, somebody dumped core!");
+#endif
+ error("shorten_status got an invalid status (?!)");
+}
+
+/* All the work for command 2. */
+__attribute__(( noreturn ))
+static void subprocess2(const char* cmd) {
+ /* Close the old standard input. */
+ if (close(0))
+ error_with_errno("Failed (in child) closing standard input");
+ /* Make the reading end of the pipe the new standard input. */
+ if (dup2(filedes[0], 0) == -1)
+ error_with_errno("Failed (in child) redefining standard input");
+ /* Close the other end of the pipe. */
+ if (close(filedes[1]))
+ error_with_errno("Failed (in child) closing filedes[1]");
+ /* Do the second command, and throw away the exit status. */
+ system(cmd);
+ /* Close all the file descriptors. */
+ if (close(filedes[0]))
+ error_with_errno("Failed (in child) closing filedes[0]"
+ " (while cleaning up)");
+ if (close(0))
+ error_with_errno("Failed (in child) closing standard output "
+ " (while cleaning up)");
+ exit(0);
+}
+
+int main (int argc, const char ** argv) {
+ int status_big; /* Exit status, 'wait' and 'system' style */
+ pid_t child2_pid;
+ pid_t dead_pid;
+
+ /* Set progname */
+ progname = argv[0];
+
+ /* Verify arguments */
+ if (argc != 3)
+ error("Wrong number of args, aborting\n");
+ /* Open a new pipe */
+ if (pipe(filedes))
+ error_with_errno("pipe() failed");
+
+ /* Fork to run second command */
+ child2_pid = fork();
+ if (child2_pid == 0)
+ subprocess2(argv[2]);
+ if (child2_pid == -1)
+ error_with_errno("fork() failed");
+
+ /* Run first command inline (seriously!) */
+ /* Close standard output. */
+ if (close(1))
+ error_with_errno("Failed closing standard output");
+ /* Make the writing end of the pipe the new standard output. */
+ if (dup2(filedes[1], 1) == -1)
+ error_with_errno("Failed redefining standard output");
+ /* Close the other end of the pipe. */
+ if (close(filedes[0]))
+ error_with_errno("Failed closing filedes[0]");
+ /* Do the first command, and (crucially) get the status. */
+ status_big = system(argv[1]);
+
+ /* Close the pipe "standard output". */
+ if (close(filedes[1]))
+ error_with_errno("Failed closing filedes[1] (while cleaning up)");
+ /* Close standard output. */
+ if (close(1))
+ error_with_errno("Failed closing standard output (while cleaning up)");
+
+ /* Wait for the other process to exit. */
+ /* We don't care about the status. */
+ dead_pid = waitpid(child2_pid, NULL, WUNTRACED);
+ if (dead_pid == -1) {
+ error_with_errno("waitpid() failed");
+ }
+ else if (dead_pid != child2_pid) {
+ error("waitpid(): Who died? %i\n", dead_pid);
+ }
+
+ /* Return the desired exit status. */
+ return shorten_status(status_big);
+}