Hi Gennadij!
31 Mar 14, Gennadij Pastuhov ==> Valentin Nechayev:
GP> Четверг марта 27 14 14:45 Valentin Nechayev писал к Gennadij Pastuhov:
GP>>>>> Что-то я не соображу, можно ли в линухе сделать такое: есть
GP>>>>> некая программа, она делает свое дело и выдает строки в stdout и
GP>>>>> stderr.
GP>>>>> Мне нужно запустить ее (из С) и прочесть эти строки.
RS>>>> man 3 popen
GP>>> Вроде бы не подходит, мне же два потока данных нужны. Пока дошел
GP>>> до такого способа: делаю 2 пайпа, форкаюсь, цепляю стдоут и стдерр к
GP>>> пайпам и в родителе по селекту жду данные. Предполагается, что
GP>>> потомок будет висеть вечно и слушать сетевой интерфейс, а мне
GP>>> возвращать прочтенное оттуда.
VN>> Hу в общем так оно и есть. Может, где-то есть библиотека для этого,
VN>> я не видел.
GP> Ну вот, оклемался от простуды и написал, работает такой код. Только хорошо
GP> бы, чтобы писатель в stdout почаще делал:
Сделаю типа review, если не возражаешь. :)
GP> int sofd[2]; //pipe для stdout
GP> int sefd[2]; //pipe для stderr
GP> int rc;
GP> ssize_t ro, re;
GP> char child_stdout_buf[BUF_LEN];
GP> char child_stderr_buf[BUF_LEN];
GP> rc = pipe(sofd);
GP> if (rc == -1) {
GP> // ошибка создания пайпа
GP> } else if (rc == 0) {
GP> rc = pipe(sefd);
GP> if (rc == -1) {
GP> // ошибка создания пайпа
GP> } else if (rc == 0) {
GP> pid_t pid = fork();
GP> if (pid == 0) {
GP> rc = dup2(sofd[1], STDOUT_FILENO);
GP> if (rc == -1) {
GP> // ошибка dup2
GP> } else {
GP> rc = dup2(sefd[1], STDERR_FILENO);
GP> if (rc == -1) {
GP> // ошибка dup2
GP> } else {
Здесь хорошо бы закрыть sofd[1] и sefd[1].
А можно заодно и sofd[0] и sefd[0] - они для child тоже не нужны.
GP> rc = execve("child_proc", NULL,
GP> NULL);
GP> }
GP> }
GP> } else {
Здесь тоже надо бы закрыть sofd[1] и sefd[1], иначе завершение потомка не
приведёт к закрытию пайпа - он ведь останется у родителя.
GP> fd_set std;
GP> int nfds = MAX(sofd[0], sefd[0]) + 1;
GP> for (;;) {
GP> FD_ZERO(&std);
GP> FD_SET(sofd[0], &std);
GP> FD_SET(sefd[0], &std);
GP> rc = select(nfds, &std, NULL, NULL, NULL);
Точно не нужен таймаут?
GP> if (rc == 0) {
GP> // данных нет
GP> } else if (rc == -1) {
GP> // ошибка при чтении данных
GP> } else {
GP> if (FD_ISSET(sofd[0], &std)) {
GP> // читаем stdout потомка
GP> ro = read(sofd[0],
GP> child_stdout_buf,
GP> BUF_LEN);
GP> if (ro == -1) {
GP> // ошибка чтения
GP> } else if (ro == 0) {
GP> // нечего читать
Насколько я помню, если select() установил флажок, а read() вернул ноль, это
EOF, т.е. пайп закрыт с той стороны.
Это значит, что нужно похоронить дочерний процесс (waitpid()) и закрыть свой
конец пайпа. В твоём случае - обоих пайпов.
GP> } else {
GP> child_stdout_buf[ro]
GP> = 0;
GP> // в буфере прочитанная
GP> строка
GP> }
Совершенно не факт, что там будет строчная буферизация.
Может быть прочитано полстроки, может быть несколько строк. Может и нулевой
байт быть.
И, что особенно интересно, может быть прочитано ровно BUF_LEN байт, и тогда ты
запишешь нолик за границу массива.
GP> } else if (FD_ISSET(sefd[0],
GP> &std)) {
GP> // есть данные из stderr потомка
GP> ...
GP> }
GP> }
GP> ...
VN>> P.S. Я такое писал, отдать в паблик не могу, но могу ответить на
VN>> вопросы.
GP> Возможно, для спецов код простой, но я со всеми этими функциями первый раз
GP> столкнулся, пару дней думал, что нужно закрыть,
GP> что открыть и в каком порядке.
VN>> Одна из самых больших тонкостей, которые надо предусмотреть
VN>> концептуально - это соотношение между фактами завершения дочернего
VN>> процесса, закрытия потоков с той стороны и таймаута действий.
VN>> В случае интерактивного общения с чем-то на той стороне надо тщательно
VN>> продумать политику в этом вопросе.
GP> Тут да.
Lucky carrier,
Паша
aka ***@gul.kiev.ua