URL: https://www.opennet.me/cgi-bin/openforum/vsluhboard.cgi
Форум: vsluhforumID9
Нить номер: 9419
[ Назад ]

Исходное сообщение
"Обмен данными с утилитой на c++"

Отправлено Kvest , 23-Мрт-12 17:40 
Доброго времени суток!
Есть некая утилита. При общении с ней из консоли она задает вопросы, ты на них отвечаешь, и она в конце выдает результат своей работы. Ну вобщем обычный сценарий работы.
У меня стала необходимость поработать с этой утилитой из моего приложения на с++. Как это фактически делается: создаем два пайпа, форкаемся и в дочернем процессе связываем один пайп со стандартным потоком ввода, второй - с потоком вывода. Далее в дочернем процессе вызываем exec...() с указанием нужной утилиты и параметров, а в родительском через пайпы читаем-пишем данные. Проблема заключается в том, что у меня не получается все это нормально собрать в кучу, что то я не так делаю с пайпами.
Может есть у кого рабочий код, позволяющий вызвать утилиту и общаться в ней в обоих направлениях(читать что вывела утилита и отвечать ей определенными данными)? Не обязательно по той схеме, которую описал я, принимаются любые решения(только без каких либо внешних скриптов, все должно делаться из кода моего приложения).  

Содержание

Сообщения в этом обсуждении
"Обмен данными с утилитой на c++"
Отправлено cryo , 23-Мрт-12 19:07 
Вы бы текущий свой код привели здесь, было б проще подсказать.

"Обмен данными с утилитой на c++"
Отправлено Kvest , 25-Мрт-12 14:16 
> Вы бы текущий свой код привели здесь, было б проще подсказать.

   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 process

        close(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].


"Обмен данными с утилитой на c++"
Отправлено oleg_3 , 25-Мрт-12 16:34 
Вместо этого:

        //this is parent process
        close(pipesin[1]);       //Hroblem is here              
        close(pipesout[1]);

Должно это:

        //this is parent process
        close(pipesin[0]);          
        close(pipesout[1]);


"Обмен данными с утилитой на c++"
Отправлено oleg_3 , 25-Мрт-12 17:16 
Ещё вот эта строка не правтльная:

if (pipe(pipesin) == 0 || pipe(pipesout) == 0){

Должно так:

if (pipe(pipesin) == 0 && pipe(pipesout) == 0){


"Обмен данными с утилитой на c++"
Отправлено Kvest , 25-Мрт-12 19:55 
> Ещё вот эта строка не правтльная:
>  if (pipe(pipesin) == 0 || pipe(pipesout) == 0){
> Должно так:
>  if (pipe(pipesin) == 0 && pipe(pipesout) == 0){

Насчет этого согласен. Я когда копировал сюда почему то поменял, не глянув. По коду у меня && стоит.

А вот касательно
> Должно это:
> //this is parent process
> close(pipesin[0]);          
> close(pipesout[1]);

по моему я пробовал так делать - и тоже зависает. Завтра с утра еще раз перепроверю. Но велики подозрения что я останусь висеть на чтении.



"Обмен данными с утилитой на c++"
Отправлено oleg_3 , 26-Мрт-12 02:37 
/*
Ниже небольшая тестовая программа на СИ. Она в принципе работает.
И заглушка утилиты.
Замечу следующее.
Запись в 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 process

    signal(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);
}


"Обмен данными с утилитой на c++"
Отправлено Kvest , 26-Мрт-12 11:23 
>[оверквотинг удален]
>     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 чего не так творит?


"Обмен данными с утилитой на c++"
Отправлено oleg_3 , 26-Мрт-12 12:58 
А Вы не пробовали запустить программу, которою я привел, с моей же заглушкой?
У меня эта пара работает (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".



"Обмен данными с утилитой на c++"
Отправлено oleg_3 , 26-Мрт-12 13:24 
Или утилита должна сразу после запуска выдать вопрос?

"Обмен данными с утилитой на c++"
Отправлено Kvest , 26-Мрт-12 13:51 
>[оверквотинг удален]
> 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. Это утилита для шифрования данных в контейнер.


"Обмен данными с утилитой на c++"
Отправлено Olej , 11-Апр-12 02:01 
> Может есть у кого рабочий код, позволяющий вызвать утилиту и общаться в
> ней в обоих направлениях(читать что вывела утилита и отвечать ей определенными
> данными)? Не обязательно по той схеме, которую описал я, принимаются любые
> решения(только без каких либо внешних скриптов, все должно делаться из кода
> моего приложения).

http://rus-linux.net/nlib.php?name=/MyLDP/BOOKS/Linux-tools/...
пример e5.cc (там же есть полный архив работающих примеров).