read () выводит команду execl и печатает ее, используя только syscall write () - PullRequest
0 голосов
/ 06 ноября 2019

Я пытаюсь выполнить /bin/tar -tf в tar-файле с execl(). Я хотел бы, чтобы он прочитал его содержимое и только распечатал его на экране терминала в моей функции main() с помощью системного вызова write().

Мой код ниже каким-то образом считывает содержимое и записывает его в терминал нав то же время без необходимости прибегать к системному вызову write ().

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>

int pfd[2];

// Problem 1:
// ERROR : read() function reads and writes to the terminal.
// I would like to write the contents of the 'buffer' variable with the
// write(fd, buffer, strlen(buffer)) syscall to the terminal preferably
// in the main() function.
// Problem 2:
// is the 'status' variable along with the _exit function useful here?
// I am not sure if a pipe is needed?
int show_tar(pid_t *pid, char *buffer, int *bytes_read, char **tar_name)
{
    int status = 0;
    pipe(pfd);
    if ((*pid = fork()) == 0) {
        dup2(0, pfd[0]);
        execl("/bin/tar", "tar", "-tf", tar_name[1], (char *)NULL);
    } else {
        close(pfd[1]);
        *bytes_read = read(pfd[0], buffer, sizeof(buffer));
        wait(NULL);
    }
    _exit(status);
    return status;
}

int main(int argc, char **argv)
{
    char buffer[1024];
    int bytes_read = 0;
    pid_t pid;
    int status = show_tar(&pid, buffer, &bytes_read, argv);
    wait(&status);
    // write contents of 'buffer' to the terminal with the
    // write(fd, buffer, strlen(buffer)) function like so:
    // int ch;
    // while (1) {
    //     if ((ch = getchar()) == 27) // Press <Escape> to quit program
    //         break;
    //     else  
    //         write(1, buffer, strlen(buffer));
    // }
    return 0;
}

Скомпилировано и выполнено в Linux, где argv[1] является тарболом:

gcc buffer.c -o buffer && ./buffer "$HOME/<your tarball>.tar.gz"

Вывод показываетвсе содержимое архива без обращения к системному вызову write().

1 Ответ

1 голос
/ 06 ноября 2019

РЕДАКТИРОВАТЬ: Это потребовало огромной работы, и это все еще не на самом деле, но это должно начать вас.

Я думаю, что ваши трубы немного запутались: они односторонниеканал и все, что написано на pfd[1], можно прочитать на pfd[0], поэтому они не используют канал должным образом.

Родитель и потомок всегда должны закрывать «другой» конец канала, которым они не являютсяиспользуя, и вы должны организовать так, чтобы канал записи был буквально номером 1 (stdout).

int show_tar(pid_t *pid, char *buffer, int *bytes_read, char **tar_name)
{
    int status = 0;
    int buffer_size = *bytes_read;  // pass IN the buffer size
    int pfd[2];
    pipe(pfd);
    if ((*pid = fork()) == 0) {
        close(pfd[0]);  // child doesn't need read pipe
        dup2(pfd[1], 1); // insure write pipe is at stdout (fd#1)
        dup2(pfd[1], 2); // stderr goes to the pipe also (optional)
        close(pfd[1]);  // child doesn't need write pipe any more
        execl("/bin/tar", "tar", "-tf", tar_name[1], (char *)NULL);
        _exit(1);
    } else {
        close(pfd[1]);  // parent doesn't need write pipe
        *bytes_read = read(pfd[0], buffer, buffer_size);

        // insure NUL byte at the end
        if (*bytes_read >= 0) buffer[*bytes_read] = 0;

        wait(NULL);
    }
//  _exit(status);    // we don't need this!
    return status;
}

int main(int argc, char **argv)
{
    char buffer[1024];
    int bytes_read = sizeof buffer;
    pid_t pid;

    int status = show_tar(&pid, buffer, &bytes_read, argv);
    wait(&status);

    // print the value here
    return 0;
}

Это несколько упрощает управление дескриптором канала, потому что, если это выполняется в среде, где stdin /stdout уже закрыты, тогда канал может фактически использовать fd # 0 и fd # 1.

В конце вам иногда приходится делать хитрые вещи, проверяя дескрипторы файлов, чтобы убедиться, что они уже соответствуют вашим требованиям,и dup вокруг столкновений. Но если запустить его из сеанса терминала, это хорошо для начала.

ТАКЖЕ: имейте в виду, что стандартный поток ошибок (fd # 2) все еще подключен к исходному терминалу, поэтому такие вещи, как сообщения об ошибках от tar не будет захвачен этим механизмом. Опять же, более сложное управление дескриптором канала.

РЕДАКТИРОВАТЬ: Я только что заметил, что все пути в show_tar() проходят через _exit(), поэтому он никогда не вернется.

EDIT: перенести определениеint pfd[2]; до внутри show_tar() - он не должен находиться в области видимости файла.

РЕДАКТИРОВАТЬ: только что понял, что sizeof(buffer) в show_tar() не делает то, что вы думаете;размер указателя всегда 4 или 8 (в зависимости от платформы), а не количество доступных байтов - это должно быть передано в качестве параметра. Я перегрузил параметр *bytes_read, чтобы передать в число доступных байтов, и вы уже используете его для передачи назад прочитанного числа.

родитель также должен зацикливаться, читая байты из канала, пока не получит конец файла, потому что маловероятно, что все из tar пойдет в одно чтение.

...