Доброго времени суток!
Есть некая утилита. При общении с ней из консоли она задает вопросы, ты на них отвечаешь, и она в конце выдает результат своей работы. Ну вобщем обычный сценарий работы.
У меня стала необходимость поработать с этой утилитой из моего приложения на с++. Как это фактически делается: создаем два пайпа, форкаемся и в дочернем процессе связываем один пайп со стандартным потоком ввода, второй - с потоком вывода. Далее в дочернем процессе вызываем exec...() с указанием нужной утилиты и параметров, а в родительском через пайпы читаем-пишем данные. Проблема заключается в том, что у меня не получается все это нормально собрать в кучу, что то я не так делаю с пайпами.
Может есть у кого рабочий код, позволяющий вызвать утилиту и общаться в ней в обоих направлениях(читать что вывела утилита и отвечать ей определенными данными)? Не обязательно по той схеме, которую описал я, принимаются любые решения(только без каких либо внешних скриптов, все должно делаться из кода моего приложения).
Вы бы текущий свой код привели здесь, было б проще подсказать.
> Вы бы текущий свой код привели здесь, было б проще подсказать.int pipesin[2], pipesout[2];
pid_t fork_result;if (pipe(pipesin) == 0 || pipe(pipesout) == 0){
fork_result = fork();
if (fork_result == (pid_t)-1){
//error
return;
}
}
else{
//error
return;
}if (fork_result == (pid_t)0){
//this is child processclose(1);//close stdout
dup(pipesout[1]);close(0); //close stdin
dup(pipesin[0]);close(pipesin[1]);
close(pipesout[0]);char *args[] = {"truecrypt", "-c" ,NULL};
execvp("truecrypt", args);
}
else{
//this is parent process
close(pipesin[1]); //Hroblem is here
close(pipesout[1]);int data_processed;
char buffer[BUFSIZ + 1];data_processed = read(pipesout[0], buffer, BUFSIZ);
printf("Read %d bytes: %s", data_processed, buffer);
..........Ну обработка чтения-записи в пайпы еще не полноценная, это только заглушка. Проблема в строчке "Hroblem is here": если не вызывать закрытие пайпа(close(pipesin[1]);) - не приходит данныех на чтение(мы тупо останавливаемся на строчке data_processed = read(pipesout[0], buffer, BUFSIZ); и ждем данных), а если закрыть - то мне потом некуда писать данные т.к. пайп для записи я закрыл собственноручно.
т.е. в родительском процессе мне надо как то получить полноценные pipesout[0] для чтения данных из консоли и pipesin[1] для отправки данных. Что то у меня это никак не выходит. А дальше я в цикле просто буду читать данные из pipesout[0], анализировать их и отвечать по pipesin[1].
Вместо этого://this is parent process
close(pipesin[1]); //Hroblem is here
close(pipesout[1]);Должно это:
//this is parent process
close(pipesin[0]);
close(pipesout[1]);
Ещё вот эта строка не правтльная:if (pipe(pipesin) == 0 || pipe(pipesout) == 0){
Должно так:
if (pipe(pipesin) == 0 && pipe(pipesout) == 0){
> Ещё вот эта строка не правтльная:
> if (pipe(pipesin) == 0 || pipe(pipesout) == 0){
> Должно так:
> if (pipe(pipesin) == 0 && pipe(pipesout) == 0){Насчет этого согласен. Я когда копировал сюда почему то поменял, не глянув. По коду у меня && стоит.
А вот касательно
> Должно это:
> //this is parent process
> close(pipesin[0]);
> close(pipesout[1]);по моему я пробовал так делать - и тоже зависает. Завтра с утра еще раз перепроверю. Но велики подозрения что я останусь висеть на чтении.
/*
Ниже небольшая тестовая программа на СИ. Она в принципе работает.
И заглушка утилиты.
Замечу следующее.
Запись в pipe делается, если там есть свободное место, если не ошибаюсь 512 байт.
Если в утилите построчная буферизация, то скорее всего и будете получать построчно.
Если там другая буферизация или строки длиннее 512, то будете получать разными
кусками. Тогда к этому нужно быть готовым.
*/// ТЕСТОВАЯ ПРОГРАММА
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
// c:\СИ\all_funk\forum.c
// cd /usr/home/test3test/all_funk/tar/
// gcc forum.c -o forum.cgi
// ./forum.cgi
//------------------------------ call_util --------------------------------int call_util()
{
int k, n;
int pipesin[2], pipesout[2];
pid_t pid, status=0;
char buf[100];pipesin[0] = -1;
pipesin[1] = -1;
pipesout[0] = -1;
pipesout[1] = -1;k=pipe(pipesin);
if(k<0){ printf("k=pipe(pipesin)=%d\n", k); goto ERR;}
k=pipe(pipesout);
if(k<0){ printf("k=pipe(pipesout)=%d\n", k); goto ERR;}pid=fork();
if(pid<0){ printf("pid=fork()=%d\n", (int)pid); goto ERR;}
if(pid==0){
//this is child process
char *args[] = {"forum_util.cgi", "-c" ,NULL};
int in, out;close(pipesin[1]);
close(pipesout[0]);close(1);//close stdout
k=dup(pipesout[1]);
if(k!=1) exit(1);
close(pipesout[1]); // это все так делаютclose(0); //close stdin
k=dup(pipesin[0]);
if(k!=0) exit(1);
close(pipesin[0]); // это все так делаютk=execvp("./forum_util.cgi", args);
printf("k=execvp()=%d: %s\n", k, strerror(errno));
exit(2);
}
//this is parent processsignal(SIGPIPE, SIG_IGN); // это чтоб не завалить главный процесс
close(pipesin[0]);
pipesin[0]=-1;
close(pipesout[1]);
pipesout[1]=-1;n=0;
while(n<4){
snprintf(buf, sizeof(buf), "n=%d\n", n);
k=write(pipesin[1], buf, strlen(buf));
fflush(stdout);
if(k<0){ printf("k=write(pipesin[1])=%d\n", k); goto ERR;}
printf(">>> %s\n", buf);k=read(pipesout[0], buf, sizeof(buf)-1);
if(k<=0){ printf("k=read(pipesout[0])=%d\n", k); goto ERR;}
buf[k]=0;
printf("<<< %s\n", buf);
n++;
}
if(pipesin[0] >= 0) close(pipesin[0]);
if(pipesin[1] >= 0) close(pipesin[1]);
if(pipesout[0] >= 0) close(pipesout[0]);
if(pipesout[1] >= 0) close(pipesout[1]);
wait(&status); // борьба с зомби
printf("status=0xX\n", status);
return(0);ERR:
if(pipesin[0] >= 0) close(pipesin[0]);
if(pipesin[1] >= 0) close(pipesin[1]);
if(pipesout[0] >= 0) close(pipesout[0]);
if(pipesout[1] >= 0) close(pipesout[1]);
wait(&status); // борьба с зомби
printf("status=0xX\n", status);
return(-1);
}//-------------------------- main ---------------------------------
int main()
{
int k;k=call_util();
printf("k=call_util()=%d\n\n", k);
exit(0);
}//=====================================================================
// ЗАГЛУШКА УТИЛИТЫ
#include <stdio.h>
#include <stdlib.h>
#include <string.h>// c:\СИ\all_funk\forum_util.c
// cd /usr/home/test3test/all_funk/tar/
// gcc forum_util.c -o forum_util.cgi
// ./forum_util.cgi//----------------- main ------------------
int main(int argc, char *argv[])
{
char *ptr, buf[100];alarm(10);
while(1){
ptr=fgets(buf, sizeof(buf), stdin);
if(ptr==NULL) exit(9);
printf("%s", buf);
fflush(stdout);
}
exit(10);
}
>[оверквотинг удален]
> char *ptr, buf[100];
> alarm(10);
> while(1){
> ptr=fgets(buf, sizeof(buf), stdin);
> if(ptr==NULL) exit(9);
> printf("%s", buf);
> fflush(stdout);
> }
> exit(10);
> }Родительский процесс повисает на k=read(pipesout[0], buf, sizeof(buf)-1); , а если вызвать close(pipesin[1]); вместо close(pipesin[0]);, то не повисает. Та же ситуация и с моим кодом. Может truecrypt чего не так творит?
А Вы не пробовали запустить программу, которою я привел, с моей же заглушкой?
У меня эта пара работает (FreeBsd 7), дает такую выдачу$ ./forum.cgi
>>> n=0<<< n=0
>>> n=1
<<< n=1
>>> n=2
<<< n=2
>>> n=3
<<< n=3
status=0x0900
k=call_util()=0$
Это обязательный тест.
И работает ли Ваша утилита прямо из окна?
И что выдает, если вызвать close(pipesin[1]); вместо close(pipesin[0]);
И когда зависает на k=read(pipesout[0], buf, sizeof(buf)-1);
то что показывает top в стлбце STATE, может быть piperd?
Если так (piperd), то значит утилита ждет еще ввода. Не забудьте завершать
ввод переносом строки как здесь "n=%d\n".
Или утилита должна сразу после запуска выдать вопрос?
>[оверквотинг удален]
> status=0x0900
> k=call_util()=0
> $
> Это обязательный тест.
> И работает ли Ваша утилита прямо из окна?
> И что выдает, если вызвать close(pipesin[1]); вместо close(pipesin[0]);
> И когда зависает на k=read(pipesout[0], buf, sizeof(buf)-1);
> то что показывает top в стлбце STATE, может быть piperd?
> Если так (piperd), то значит утилита ждет еще ввода. Не забудьте завершать
> ввод переносом строки как здесь "n=%d\n".На Вашем тестовом приложении все работает отлично.
Попробовал перед чтением данных записать \n в исходящий от родительского процесса пайп - k(количество записаных байт) стало равным 1(т.е. по идее все нормально записало) и опять повисло на чтении.
Скорее всего утилита чудит чего то. Она кстати есть на оф сайте(http://www.truecrypt.org/), можете себе установить. Я ставил себе консольную версию 7.1a. Это утилита для шифрования данных в контейнер.
> Может есть у кого рабочий код, позволяющий вызвать утилиту и общаться в
> ней в обоих направлениях(читать что вывела утилита и отвечать ей определенными
> данными)? Не обязательно по той схеме, которую описал я, принимаются любые
> решения(только без каких либо внешних скриптов, все должно делаться из кода
> моего приложения).http://rus-linux.net/nlib.php?name=/MyLDP/BOOKS/Linux-tools/...
пример e5.cc (там же есть полный архив работающих примеров).