2 * mispipe: written by Nathanael Nerode.
4 * Copyright 2004 Nathanael Nerode.
6 * Licensed under the GPL version 2 or above, and dual-licensed under the
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29 * Usage: mispipe <command1> <command2>
30 * Run <command1> | <command2>, but return with the exit status of <command1>.
32 * This is designed for a very specific purpose: logging.
33 * "foo | logger -t foo"
34 * will return with the exit status of logger, not that of foo.
35 * "mispipe foo 'logger -t foo'"
36 * will return with the exit status of foo.
41 * Make this into a fancy, internationalized, option-handling hellbeast.
42 * (But why bother? It does its job quite well.)
45 #include <errno.h> /* errno */
46 #include <sys/types.h>
47 #include <unistd.h> /* pipe(), fork(),... */
48 #include <stdlib.h> /* system() */
49 #include <sys/wait.h> /* waitpid(), etc. */
51 #include <stdarg.h> /* va_list, for error() */
53 static const char* progname
; /* Stores argv[0] */
54 static int filedes
[2]; /* Stores pipe file descriptors */
56 /* Subroutine for 'warning' and 'error' which prefixes progname */
57 static void warning_prefix(void) {
58 fputs(progname
, stderr
);
62 /* Issue a warning, then die */
63 __attribute__(( noreturn
, format (printf
, 1, 2) ))
64 static void error(const char* formatstr
, ...) {
66 va_start(ap
, formatstr
);
68 vfprintf(stderr
, formatstr
, ap
);
73 /* Issue a warning, then die, with errno */
74 __attribute__(( noreturn
))
75 static void error_with_errno(const char* message
) {
79 fputs(message
, stderr
);
80 fputs(": error number ", stderr
);
81 fprintf(stderr
, "%i\n", saved_errno
);
85 /* Convert 'wait'/'system'-style exit status to 'exit'-style exit status */
86 __attribute__(( const ))
87 static int shorten_status(int status_big
) {
88 if (WIFEXITED(status_big
))
89 return WEXITSTATUS(status_big
);
90 if (WIFSIGNALED(status_big
))
91 return 128+WTERMSIG(status_big
);
92 if (WIFSTOPPED(status_big
))
93 return 128+WSTOPSIG(status_big
);
95 if (WCOREDUMP(status_big
))
96 error("Ow, somebody dumped core!");
98 error("shorten_status got an invalid status (?!)");
101 /* All the work for command 2. */
102 __attribute__(( noreturn
))
103 static void subprocess2(const char* cmd
) {
104 /* Close the old standard input. */
106 error_with_errno("Failed (in child) closing standard input");
107 /* Make the reading end of the pipe the new standard input. */
108 if (dup2(filedes
[0], 0) == -1)
109 error_with_errno("Failed (in child) redefining standard input");
110 /* Close the original file descriptor for it */
111 if (close(filedes
[0]))
112 error_with_errno("Failed (in child) closing filedes[0]");
113 /* Close the other end of the pipe. */
114 if (close(filedes
[1]))
115 error_with_errno("Failed (in child) closing filedes[1]");
116 /* Do the second command, and throw away the exit status. */
118 /* Close the standard input. */
120 error_with_errno("Failed (in child) closing standard output "
121 " (while cleaning up)");
125 int main (int argc
, const char ** argv
) {
126 int status_big
; /* Exit status, 'wait' and 'system' style */
133 /* Verify arguments */
135 error("Wrong number of args, aborting\n");
136 /* Open a new pipe */
138 error_with_errno("pipe() failed");
140 /* Fork to run second command */
143 subprocess2(argv
[2]);
144 if (child2_pid
== -1)
145 error_with_errno("fork() failed");
147 /* Run first command inline (seriously!) */
148 /* Close standard output. */
150 error_with_errno("Failed closing standard output");
151 /* Make the writing end of the pipe the new standard output. */
152 if (dup2(filedes
[1], 1) == -1)
153 error_with_errno("Failed redefining standard output");
154 /* Close the original file descriptor for it. */
155 if (close(filedes
[1]))
156 error_with_errno("Failed closing filedes[1]");
157 /* Close the other end of the pipe. */
158 if (close(filedes
[0]))
159 error_with_errno("Failed closing filedes[0]");
160 /* Do the first command, and (crucially) get the status. */
161 status_big
= system(argv
[1]);
163 /* Close standard output. */
165 error_with_errno("Failed closing standard output (while cleaning up)");
167 /* Wait for the other process to exit. */
168 /* We don't care about the status. */
169 dead_pid
= waitpid(child2_pid
, NULL
, WUNTRACED
);
170 if (dead_pid
== -1) {
171 error_with_errno("waitpid() failed");
173 else if (dead_pid
!= child2_pid
) {
174 error("waitpid(): Who died? %i\n", dead_pid
);
177 /* Return the desired exit status. */
178 return shorten_status(status_big
);