Запишите вывод дочернего элемента в оболочку и в файл error.log - PullRequest
0 голосов
/ 04 марта 2020

Я пытаюсь передать вывод ребенка в файл и записать его отцом.

#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <stdbool.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>

int prepare_log(void){
    int fd = open("error.log",
        O_CREAT | O_RDWR | O_APPEND,
        S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH );
    return fd;
}

void log_stderr(int pipe_ends[], int outfile){
    close(pipe_ends[1]);
    dup2(pipe_ends[0],outfile);
    close(pipe_ends[0]);

}
void child(int pipe_ends[], char* argv[]){
    close(pipe_ends[0]);
    dup2(pipe_ends[1],1);
    close(pipe_ends[1]);
    execvp(argv[0], argv);
}

void bury(){
    int status;
    wait(0);

}

int main(){
    int fd = prepare_log();
    char* argv[] = {"seq", "10", NULL};

    int pipe1[2];
    if(pipe(pipe1) == -1){
        printf("Dont' create Pipe\n");
        exit(EXIT_FAILURE);
    }


    pid_t pid = fork();
    if(pid < 0){
        perror("ERROR");
        exit(EXIT_FAILURE);
    } else if(pid > 0){
        log_stderr(pipe1, fd);
        bury();
    } else if (pid == 0){
        child(pipe1,argv);
    }
}

В данный момент я пытаюсь только передать выходные данные child отцу, а затем записать их в файл. Моей конечной целью также является отображение на терминале. Моя идея состоит в том, чтобы использовать 3 канала и перенаправить первый канал, который мы видим в коде, в качестве входных данных для 2-го и 3-го каналов. Затем перенаправьте вывод 2-го канала с dup2 (pipe2 [1], file1) и 3-го с dup2 (pipe2 [1], stdout).

1 Ответ

1 голос
/ 04 марта 2020

Дизайн программы

Родительский процесс должен будет прочитать ответ от потомка и организовать запись этой информации дважды, один раз в файл журнала и один раз в терминал. Или вам нужно будет организовать вывод от дочернего элемента до go в программу наподобие tee, которая записывает копии своего ввода в несколько мест назначения.

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

Закрытие дескрипторов файлов конвейера

Вы не закрываете достаточно файловых дескрипторов в дочернем процессе.


Практическое правило : Если вы dup2() на одном конце канала подключены к стандартному вводу или стандартному выводу, закройте оба дескриптора исходного файла, возвращаемые pipe() как можно скорее. В частности, вы должны закрыть их перед использованием любого из семейства функций exec*().

Правило также применяется, если вы дублируете дескрипторы с dup() или fcntl() с F_DUPFD или F_DUPFD_CLOEXEC.


Если родительский процесс не будет связываться ни с одним из своих дочерних элементов через канал, он должен убедиться, что он закрывает оба конца канала достаточно рано (например, перед ожиданием), чтобы его дочерние элементы могли получать показания EOF при чтении (или получать сигналы SIGPIPE или ошибки записи при записи), а не блокировать бесконечно. Даже если родительский канал использует канал без использования dup2(), он обычно должен закрывать по крайней мере один конец канала - программа очень редко может читать и писать на обоих концах одного канала.

Обратите внимание, что опция O_CLOEXEC для open() и опции FD_CLOEXEC и F_DUPFD_CLOEXEC для fcntl() также могут учитывать это обсуждение.

Если вы используете posix_spawn() и его обширное семейство вспомогательных функций (всего 21 функция), вам нужно будет рассмотреть, как закрывать файловые дескрипторы в порожденном процессе (posix_spawn_file_actions_addclose(), et c .).

Обратите внимание, что использование dup2(a, b) безопаснее, чем использование close(b); dup(a); по ряду причин. Во-первых, если вы хотите, чтобы дескриптор файла был больше, чем обычно, dup2() - единственный разумный способ сделать это. Другое состоит в том, что если a совпадает с b (например, оба 0), то dup2() обрабатывает его правильно (он не закрывается b до дублирования a), тогда как отдельный close() и dup() ужасно терпит неудачу. Это маловероятное, но не невозможное обстоятельство.

Достаточно исправленный код

Вот исправленная версия вашей программы. Логика c в log_stderr() странная; единственное, что он не делает, это пишет в стандартную ошибку. Я изменил его, чтобы он читал из канала и записывал как в стандартный вывод, так и в файл журнала. Обратите внимание, что для репликации информации требуется одно чтение, но две записи. Проверка ошибок скудна или не существует. Функция bury() теперь сообщает о смерти ребенка. Код был сохранен в pipe71.c и скомпилирован в pipe71.

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

static
int prepare_log(void)
{
    int fd = open("error.log",
                  O_CREAT | O_RDWR | O_APPEND,
                  S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH );
    return fd;
}

static
void log_stderr(int pipe_ends[], int outfile)
{
    char buffer[512];
    int nbytes;
    close(pipe_ends[1]);
    while ((nbytes = read(pipe_ends[0], buffer, sizeof(buffer))) > 0)
    {
        write(STDOUT_FILENO, buffer, nbytes);
        write(outfile, buffer, nbytes);
    }
    close(pipe_ends[0]);
    close(outfile);
}

static
void child(int pipe_ends[], char *argv[])
{
    dup2(pipe_ends[1], 1);
    close(pipe_ends[0]);
    close(pipe_ends[1]);
    execvp(argv[0], argv);
    fprintf(stderr, "failed to execute %s\n", argv[0]);
    exit(EXIT_FAILURE);
}

static
void bury(void)
{
    int status;
    int corpse;
    while ((corpse = wait(&status)) > 0)
        printf("%d: child %d exited with status 0x%.4X\n", (int)getpid(), corpse, status);
}

int main(void)
{
    int fd = prepare_log();
    char *argv[] = {"seq", "10", NULL};

    int pipe1[2];
    pipe(pipe1);

    pid_t pid = fork();

    if (pid > 0)
    {
        log_stderr(pipe1, fd);
        bury();
    }
    else if (pid == 0)
    {
        child(pipe1, argv);
    }
    return 0;
}

Однако он работает:

$ rm error.log
$ ./pipe71
1
2
3
4
5
6
7
8
9
10
99989: child 99990 exited with status 0x0000
$ cat error.log
1
2
3
4
5
6
7
8
9
10
$ 
...