Мульти трубы в C висят
Я пытаюсь реализовать программу, которая будет запускать несколько цепочек команд оболочки:
| --> cmd3 --> cmd4 -->
cmd2-->|
| --> cmd5 --> cmd6 -->|--> cmd7
|
|--> cmd8
и так далее...
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
#include <stdarg.h>
#include <sys/types.h>
typedef struct command {
char* name;
char** argv;
} command;
command parsecmd(char* cmd) {
command c;
char delimiter[] = " ";
char* buf = malloc(sizeof(char) * strlen(cmd));
strcpy(buf, cmd);
char **args = malloc(sizeof(char*));
char* token = strtok(buf, delimiter);
int i = 0;
while (token != NULL) {
if (i == 0) {
c.name = token;
}
args[i] = token;
token = strtok(NULL, delimiter);
++i;
}
args[i] = NULL;
c.argv = args;
return c;
}
int mkproc(char *cmd, int outfd)
{
command c = parsecmd(cmd);
int pipeleft[2];
pipe(pipeleft);
if(!fork()){
close(pipeleft[1]);
dup2(pipeleft[0], 0);
dup2(outfd, 1);
execvp(c.name, c.argv);
}
close(pipeleft[0]);
return pipeleft[1];
}
int mktree(char *cmd, int ofd0, ...)
{
int piperight[2];
pipe(piperight);
int cmdin = mkproc(cmd, piperight[1]);
close(piperight[1]);
if(!fork()){
uchar buf[4096];
int n;
while((n=read(piperight[0], buf, sizeof buf))>0){
va_list ap;
int fd;
va_start(ap, ofd0);
for(fd=ofd0; fd!=-1; fd=va_arg(ap, int)){
write(fd, buf, n);
}
va_end(ap);
}
}
return cmdin;
}
int main(int argc, char* argv[]) {
// THIS WORK
int chain_in = mkproc("cat foo.txt", mkproc("sort", mkproc("wc -l", 1)));
// THIS WORK
int tree_in1 = mktree("cat /tmp/test.log", mkproc("grep a", 1), mkproc("wc -l", 2), -1);
// NOT WORK -> HANG!
int tree_in2 = mktree("cat /tmp/test.log",
mktree("grep test",
mkproc("uniq", mkproc("wc -l", 1)),
mkproc("wc -l", 2), -1),
mkproc("sort", 2), -1);
}
при запуске strace на подпроцессах он зависает при чтении из канала, при запуске starce в главном процессе он также зависает при чтении... Буфер канала составляет 64 КБ, и я записываю только 4 КБ сразу для каждого канала
В ЧЕМ ДЕЛО?!
СПАСИБО!!!
2 ответа
Вы не выделяете достаточно памяти для аргументов вашей программы. В parsecmd
Вы выделяете место только для одного указателя в char **args = malloc(sizeof(char*))
и вы впоследствии сохраняете в нем более одного указателя, не перераспределяя его, что приводит к переполнению буфера. Точно так же вы выделяете на один байт меньше, чем нужно в char* buf = malloc(sizeof(char) * strlen(cmd))
- вам нужно добавить один к этому, чтобы иметь место для завершающего NUL строки. Также, sizeof(char)
по стандарту C гарантированно равен 1, поэтому нет необходимости указывать это в вызове malloc
,
Другие проблемы с вашим кодом:
- У тебя утечка памяти. Все ваши звонки
malloc
нужен соответствующий звонокfree
чтобы избежать утечки памяти. - Добавьте больше комментариев о том, что делает ваш код
strtok
не безопасно использовать в многопоточном коде, так как он использует общее глобальное состояние. Если этот код должен стать поточно-ориентированным, подумайте о его замене наstrtok_r(3)
если есть, или какая-то другая замена.- Вы не справляетесь с ошибками, если
fork
,execvp
, или жеpipe
терпит неудачу
Исправьте эти проблемы и посмотрите, решит ли это ваше зависание.
Я вижу по крайней мере две проблемы с вашим кодом:
char* buf = malloc(sizeof(char) * strlen(cmd));
Вам нужно выделить на одну больше длины cmd
, для 0
терминатор. поскольку sizeof(char)
это 1 по определению, я бы написал выше, как:
char *buf = malloc(strlen(cmd)+1);
Также для:
char **args = malloc(sizeof(char*));
Вам нужно выделить место для столько аргументов, сколько необходимо:
char **args = malloc(n * sizeof *args);
где n
количество аргументов