Сканирование через трубный замок

#c

Вопрос:

У меня есть упражнение, в котором мне нужно взаимодействовать с программой на языке Си через канал.

У меня есть следующий источник, который я не могу изменить.

 #include <stdio.h>
#include <stdlib.h>

int main()
{
    int number;
    int answer;

    number = rand() % 100;
    printf("Print the double of the number %dn", number);
    scanf("%d", amp;answer);
    if(number * 2 == answer)
        printf("Successn");
    else
        printf("Errorn");
}
 

Я попытался взаимодействовать с этой программой с помощью этого кода

 #include <unistd.h>
#include <stdio.h>

int main(int argc, char **argv, char **env)
{
        int STDIN_PIPE[2];
        int STDOUT_PIPE[2];

        pipe(STDIN_PIPE);
        pipe(STDOUT_PIPE);
        pid_t pid = fork();
        if(pid == 0)
        {
                char *path = "/path/to/binary";
                char *args[2];
                args[0] = path;
                args[1] = NULL;
                close(STDIN_PIPE[1]);
                close(STDOUT_PIPE[0]);
                dup2(STDIN_PIPE[0], STDIN_FILENO);
                dup2(STDOUT_PIPE[1], STDOUT_FILENO);
                execve(path, args, env);
        }
        else
        {
                char buf[128];
                close(STDIN_PIPE[0]);                                                                                                   
                close(STDOUT_PIPE[1]);                                                                                                  
                while(read(STDOUT_PIPE[0], buf, 1))                                                                                         
                    write(1, buf, 1);                                                                                           
        }                                                                                                               
}  
 

Но когда я запускаю его, он попадает в бесконечный цикл, ничего не печатая.

Комментарии:

1. Ваша программа должна прочитать первый канал, чтобы получить вопрос от другой программы, и записать правильный ответ в другой канал, а затем снова прочитать первый канал, чтобы получить успех или неудачу от другой программы.

2. Да, я знаю, но для того, чтобы знать, я даже не могу прочитать ни одного символа.

3. Вы должны добавить некоторые проверки, чтобы убедиться, что трубы созданы правильно.

Ответ №1:

Я исправил ряд проблем в вашем коде, добавил множество проверок на ошибки и выполнил их так, чтобы конечная цель была достигнута.

В дочернем процессе srand() необходимо вызвать для инициализации генератор случайных чисел, иначе вы всегда получите одно и то же значение.

В дочернем процессе вы должны flush(stdout) после печати вопроса сделать так, чтобы он действительно был записан в канал.

И, наконец, scanf() необходимо проверить возвращаемое значение.

В основном процессе я добавил множество проверок на ошибки. И я пишу readLine функцию, чтобы — угадайте, что — прочитать строку из трубы. Строка заканчивается символом конца строки n .

Еще есть место для некоторых улучшений…

Я протестировал свой код с помощью кода Visual Studio, настроенного для gcc и работающего под Ubuntu 20.04.

Вот источник дочернего процесса:

 #include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main()
{
    int number;
    int answer;
    time_t t;

    srand((unsigned)time(amp;t));
    number = rand() % 100;
    printf("Print the double of the number %dn", number);
    fflush(stdout);
    int n = scanf("%d", amp;answer);
    if (n != 1) {
        printf("Invalid inputn");
        return 1;
    }
    if ((number * 2) == answer) {
        printf("Successn");
        return 0;
    }
    printf("Error %d is not 2 * %dn", answer, number);
    return 1;
}
 

И вот основной источник процесса:

 #include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>

int readLine(int fd, char *buf, int bufSize);

int main(int argc, char **argv, char **env)
{
        int STDIN_PIPE[2];
        int STDOUT_PIPE[2];

        if (pipe(STDIN_PIPE))
        {
                perror("pipe(STDIN_PIPE)");
                return 1;
        }
        if (pipe(STDOUT_PIPE)) {
                perror("pipe(STDOUT_PIPE)");
                return 1;
        }
        pid_t pid = fork();
        if (pid == 0)
        {
                char *path = "../Child/Child"; // Path to child process, adapt to your environment
                char *args[2];
                args[0] = path;
                args[1] = NULL;
                if (dup2(STDIN_PIPE[0], STDIN_FILENO) == -1) {
                        perror("dup2(STDIN) failed");
                        return 1;
                }
                if (dup2(STDOUT_PIPE[1], STDOUT_FILENO) == -1) {
                        perror("dup2(STDIN) failed");
                        return 1;
                }
                // Close all pipe ends
                close(STDIN_PIPE[0]);    // Close read end of STDIN_PIPE
                close(STDIN_PIPE[1]);    // Write end of STDIN_PIPE
                close(STDOUT_PIPE[0]);   // Read end of STDOUT_PIPE
                close(STDOUT_PIPE[1]);   // Close write end of STDOUT_PIPE

                if (execve(path, args, env) == -1) {
                        perror("execve failed");
                        return 1;
                }
        }
        else
        {
                char buf[128];
                int bufSize = sizeof(buf) / sizeof(buf[0]);
                int i;

                // Read the question asked by child process
                if (readLine(STDOUT_PIPE[0], buf, bufSize) < 0) {
                        printf("readLine failed.n");
                        return 1;
                }

                // We receive something like "Print the double of the number 83"
                printf("Child process question is "%s".n", buf);

                // Extract the number at end of string
                i = strlen(buf) - 1;
                while ((i >= 0) amp;amp; isdigit(buf[i]))
                        i--;
                int value = atoi(buf   i   1);

                // Write our answer to write end of STDIN_PIPE
                char answer[128];
                int answerSize = sizeof(answer) / sizeof(answer[0]);
                int answerLen = snprintf(answer, answerSize, "%dn", value * 2);

                printf("Our answer is "%d".n", value * 2);

                if (write(STDIN_PIPE[1], answer, answerLen) != answerLen) {
                        printf("write failed.n");
                        return 1;
                }

                // Read the response (success or failure) sent by child process
                if (readLine(STDOUT_PIPE[0], buf, bufSize) < 0) {
                        printf("readLine failed.n");
                        return 1;
                }

                if (strcasecmp(buf, "Success") == 0)
                        printf("Child process returned success.n");
                else
                        printf("Child process returned failure.n");

                // Close all pipe ends
                close(STDIN_PIPE[0]);    // Close read end of STDIN_PIPE
                close(STDIN_PIPE[1]);    // Write end of STDIN_PIPE
                close(STDOUT_PIPE[0]);   // Read end of STDOUT_PIPE
                close(STDOUT_PIPE[1]);   // Close write end of STDOUT_PIPE
        }

        return 0;
}

// Read a line from file descriptor
// A line is any characters until n is received or EOF
// n is not kept
// Return the number of characters read or <0 if error:
//    -1 => Input buffer overflow
//    -2 => read() failed and errno has the error 
int readLine(int fd, char *buf, int bufSize)
{
        int i = 0;
        while (1)
        {
                // Check if enough room in the buffer
                if (i >= bufSize) {
                        printf("Input buffer overflown");
                        return -1;
                }
                // Read one character from the pipe
                ssize_t n = read(fd, buf   i, 1);
                if (n == -1)
                {
                        perror("read() failed");
                        return -2;
                }
                if (n == 0)
                {
                        // EOF received, that's OK
                        return i;
                }

                // NUL terminate the buffer
                buf[i   1] = 0;

                // Check for end of line character
                if (buf[i] == 'n') {
                        buf[i] = 0;     // Remove ending n
                        return i;
                }
                i  ;
        }
}