pipeln

Pipeline creation tool
git clone https://git.sinitax.com/sinitax/pipeln
Log | Files | Refs | README | LICENSE | sfeed.txt

pipeln.c (2618B)


      1 #include <sys/wait.h>
      2 #include <unistd.h>
      3 #include <dirent.h>
      4 #include <wait.h>
      5 #include <errno.h>
      6 #include <stdio.h>
      7 #include <stdarg.h>
      8 #include <string.h>
      9 #include <stdlib.h>
     10 #include <stdbool.h>
     11 
     12 static bool resolve_bin = true;
     13 
     14 static void
     15 __attribute__((noreturn))
     16 die(const char *fmt, ...)
     17 {
     18 	va_list ap;
     19 
     20 	setvbuf(stderr, NULL, _IOLBF, 0);
     21 	fputs("pipeln: ", stderr);
     22 	va_start(ap, fmt);
     23 	vfprintf(stderr, fmt, ap);
     24 	va_end(ap);
     25 	if (*fmt && fmt[strlen(fmt)-1] == ':') {
     26 		fputc(' ', stderr);
     27 		perror(NULL);
     28 	} else {
     29 		fputc('\n', stderr);
     30 	}
     31 
     32 	exit(1);
     33 }
     34 
     35 static void
     36 usage(int rc, FILE *file)
     37 {
     38 	fprintf(file, "Usage: pipeln [-h] [-R] [--] [CMD |].. CMD\n");
     39 	exit(rc);
     40 }
     41 
     42 static pid_t
     43 run(int *in, int *out, const char **argv)
     44 {
     45 	pid_t child;
     46 
     47 	if (!argv[0]) die("run: empty command");
     48 
     49 	child = fork();
     50 	if (child < 0) die("fork:");
     51 
     52 	if (!child) {
     53 		if (in) {
     54 			if (dup2(in[0], 0) < 0)
     55 				die("dup2 %i:", errno);
     56 			if (close(in[0]) < 0)
     57 				die("close:"); 
     58 		}
     59 		if (out) {
     60 			if (dup2(out[1], 1) < 0)
     61 				die("dup2:");
     62 			if (close(out[0]) < 0 || close(out[1]) < 0)
     63 				die("close:"); 
     64 		}
     65 		if (resolve_bin) {
     66 			execvp(argv[0], (char *const *) argv);
     67 		} else {
     68 			execv(argv[0], (char *const *) argv);
     69 		}
     70 		die("execv %s:", argv[0]);
     71 	}
     72 
     73 	if (in) close(in[0]);
     74 	if (out) close(out[1]);
     75 
     76 	return child;
     77 }
     78 
     79 static void
     80 __attribute__((noreturn))
     81 waitall(pid_t tpid)
     82 {
     83 	int status, exitcode;
     84 	pid_t pid;
     85 
     86 	exitcode = 0;
     87 	do {
     88 		pid = waitpid(-1, &status, 0);
     89 		if (pid < 0 && errno != EINTR && errno != ECHILD)
     90 			die("waitpid:");
     91 		if (pid == tpid) {
     92 			if (!exitcode && WIFEXITED(status))
     93 				exitcode = WEXITSTATUS(status);
     94 			else if (!exitcode && WIFSIGNALED(status))
     95 				exitcode = 128 + WTERMSIG(status);
     96 		}
     97 	} while (pid >= 0 || errno != ECHILD);
     98 
     99 	exit(exitcode);
    100 }
    101 
    102 int
    103 main(int argc, const char **argv)
    104 {
    105 	const char **arg, **cmdargs;
    106 	int pipe1[2], pipe2[2];
    107 	int *in, *out;
    108 	pid_t pid;
    109 
    110 	if (argc < 1) return 1;
    111 
    112 	for (arg = argv + 1; *arg; arg++) {
    113 		if (!strcmp(*arg, "--")) {
    114 			arg++;
    115 			break;
    116 		} else if (!strcmp(*arg, "-h")) {
    117 			usage(1, stderr);
    118 		} else if (!strcmp(*arg, "-R")) {
    119 			resolve_bin = false;
    120 		} else {
    121 			break;
    122 		}
    123 	}
    124 
    125 	// First command from stdin.
    126 	in = NULL;
    127 	out = pipe1;
    128 
    129 	cmdargs = arg;
    130 	for (; ; arg++) {
    131 		if (!*arg || !strcmp(*arg, "|")) {
    132 			// Last command to stdout.
    133 			if (!*arg) out = NULL;
    134 			else if (pipe(out) < 0) die("pipe:");
    135 
    136 			*arg = NULL;
    137 			pid = run(in, out, cmdargs);
    138 			if (!out) waitall(pid);
    139 			cmdargs = arg + 1;
    140 
    141 			in = out;
    142 			out = (out == pipe1) ? pipe2 : pipe1;
    143 		} else if (!strcmp(*arg, "\\|")) {
    144 			*arg = "|";
    145 		}
    146 	}
    147 }