Это требует небольшого рефакторинга ...
Если есть N этапов конвейера, нам потребуется N-1 отдельных вызовов pipe
. Код имеет только один.
Каждая стадия конвейера может иметь свою / частную stderr
переадресацию (например):
cmd0 | cmd1 2>cmd1_stderr | cmd2 2>cmd2_stderr | cmd3
Но код предполагает все stderr
будет таким же.
И предполагается, что имеет для открытия stderr
[вообще]. Ребенок должен открывать stderr
, только если у нас есть что-то вроде выше. В противном случае каждый дочерний процесс должен наследовать родительский stderr
[и ничего ].
Процесс родительский выполняет то, что только дочерний следует сделать (например): изменить stdin/stdout/stderr
и выполнить команду. В основном это должно просто облегчить конвейер.
Родитель делает wait
в создании / fork l oop [для каждого дочернего элемента]. Это приводит к блокировке родителя на каждом шаге.
Родитель должен делать только wait
за секунду l oop и ждать всех потомков сразу.
Поскольку ваше определение struct
для cmd
было не опубликовано, мне пришлось немного догадаться о намерениях. Но я думаю, вам нужно иметь массив структур, по одному для каждой команды, вместо того, чтобы помещать все аргументы для всех команд в одну структуру.
Я создал две версии кода. Один с аннотациями к ошибкам. Второй, очищенный и реструктурированный. Оба они не тестировались, но должны дать вам некоторые идеи.
Вот аннотированная версия:
// NOTE/BUG: each command in the pipeline can have its own/private
// stderr diversion
#if 0
struct cmd {
int pipes;
char *filev[3];
char **argv[MAXCMD][MAXARG];
};
#else
struct cmd {
char *stderr;
char *argv[100];
};
#endif
// NOTE/BUG: we need separate filev [and argv] for each pipeline stage
// so we need an _array_ of structs
int cmdcnt;
struct cmd cmdlist[30];
void
pipeline(void)
{
int pfd[2];
// NOTE/BUG: this only creates a single pipe -- we need N-1 pipes
#if 0
if (pipe(pfd) < 0)
exit(-1);
#endif
// NOTE/BUG: if child does _not_ have a private stderr it should just
// use the parent's stderr _unchanged_
for (int i = 0; i < cmdcnt; i++) {
pid_t pid;
// NOTE/BUG: here is the correct place to create the pipe
pid = fork();
int fd;
if (pid < 0)
exit(-1);
char **argv = cmd->argv;
char **filev = cmd->filev;
// child process
if (pid == 0) {
close(pfd[0]);
dup2(pfd[1], STDOUT_FILENO);
close(pfd[1]);
// NOTE/BUG: this does _not_ connect the input of cmd[N] to cmd[N-1]
#if 0
if (filev[0] != NULL && i == 0) {
fd = open(filev[0], O_RDONLY, 0);
dup2(fd, STDIN_FILENO);
close(fd);
}
#endif
if (filev[2] != NULL) {
fd = creat(filev[2], 0644);
dup2(fd, STDERR_FILENO);
close(fd);
}
if (execvp(cmd->argv[i][0], cmd->argv[i]) < 0)
levenshtein(cmd->argv[i][0], commands);
}
// parent process
if (pid > 0) {
// NOTE/BUG: parent should _not_ wait in the middle of the creation
// loop
if (cmd->bg > 0)
wait(NULL);
// NOTE/BUG: _parent_ should _not_ change its stdin/stderr/stdout
close(pfd[1]);
dup2(pfd[0], STDIN_FILENO);
close(pfd[0]);
if (filev[1] != NULL && i == (cmd->pipes - 1)) {
fd = creat(filev[1], 0644);
dup2(fd, STDOUT_FILENO);
close(fd);
}
if (filev[2] != NULL) {
fd = creat(filev[2], 0644);
dup2(fd, STDERR_FILENO);
close(fd);
}
// NOTE/BUG: _parent_ should _not_ execute the command
if (execvp(cmd->argv[i][0], cmd->argv[i]) < 0)
levenshtein(cmd->argv[i][0], commands);
}
}
}
Вот обновленная версия:
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
struct cmd {
char *stderr;
char *argv[100];
};
char *filev[3];
#define CLOSEME(_fd) \
do { \
if (_fd < 0) \
break; \
close(_fd); \
_fd = -1; \
} while (0)
int run_in_background;
int cmdcnt;
struct cmd cmdlist[30];
int
pipeline(void)
{
int pfd[2] = { -1, -1 };
struct cmd *cmd;
char *file;
int oldfd;
pid_t pid;
pid_t lastpid = -1;
// open the common stderr
int errfd = -1;
if (filev[2] != NULL)
errfd = open(filev[2],O_APPEND | O_CREAT);
// start all commands
for (int i = 0; i < cmdcnt; i++) {
int iam_first = (i == 0);
int iam_last = (i == (cmdcnt - 1));
cmd = &cmdlist[i];
// get previous stage pipe descriptor
oldfd = pfd[0];
// create the pipe to the next stage
if (! iam_last) {
if (pipe(pfd) < 0)
exit(1);
}
pid = fork();
lastpid = pid;
int fd;
if (pid < 0)
exit(-1);
char **argv = cmd->argv;
// parent process
if (pid > 0) {
CLOSEME(pfd[1]);
continue;
}
// child process ...
// open stdin for _first_ command
fd = -1;
if (iam_first) {
file = filev[0];
if (file != NULL) {
fd = open(file, O_RDONLY, 0);
}
}
// connect stdin to previous stage pipe
else {
fd = oldfd;
oldfd = -1;
}
// connect stdin to correct source
if (fd >= 0) {
dup2(fd, STDIN_FILENO);
close(fd);
}
CLOSEME(oldfd);
// connect to stderr
file = cmd->stderr;
if (file != NULL) {
fd = creat(file, 0644);
dup2(fd, STDERR_FILENO);
close(fd);
}
else {
if (errfd >= 0)
dup2(errfd, STDERR_FILENO);
}
CLOSEME(errfd);
// connect stdout
// NOTE: does _not_ handle ">> outf" [only does "> outf"]
fd = -1;
if (iam_last) {
file = filev[1];
if (file != NULL) {
fd = open(file, O_WRONLY | O_CREAT, 0644);
dup2(fd, STDOUT_FILENO);
close(fd);
}
}
// execute the command
execvp(argv[0], argv);
exit(9);
}
CLOSEME(errfd);
int status;
int last_status = 0;
// parent waits for all pipeline stages to complete
if (! run_in_background) {
while (1) {
pid = wait(&status);
if (pid <= 0)
break;
if (pid == lastpid) {
last_status = status;
break;
}
}
}
return last_status;
}