Нет вывода с использованием системного вызова сплайсинга - PullRequest
3 голосов
/ 01 февраля 2020

У меня есть следующий код:

#define _GNU_SOURCE
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>

int main(int argc, char **argv) {
  if(argc < 2) {
    fputs("Error: You must provide an filename\n", stderr);
    return 1;
  }

  FILE *handle = fopen(argv[1], "r");
  if(!handle) {
    perror("Error opening file:");
    goto error;
  }

  int handle_fd = fileno(handle);

  int filedes[2];
  if(pipe(filedes)) {
    perror("Error creating pipe:");
    goto error;
  }

  while(1) {
    ssize_t rc = splice(handle_fd, NULL, filedes[1], NULL, BUFSIZ, 0);

    if(rc == -1) {
      // Error occurred
      perror("Error copying data:");
      goto error;
    } else if(rc == 0) {
      break;
    }

    splice(filedes[0], NULL, STDOUT_FILENO, NULL, BUFSIZ, 0);
  }

  return 0;

error:
  if(fclose(handle)) {
    perror("Error closing file:");
  }

  return 1;
}

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

Когда я запускаю программу в GDB, она работает, однако ничего не отображается при запуске просто из оболочка

1 Ответ

1 голос
/ 01 февраля 2020

Вы должны убедиться, что STDOUT_FILENO не открывается в режиме добавления, или вторая splice(2) не будет работать, как описано в его manpage :

EINVAL Целевой файл открывается в режиме добавления.

И да, это произойдет, даже если это так, где флаг O_APPEND не должен иметь никакого значения, что очень похоже на ошибка . Или, по крайней мере, неприятность, тем более что splice не волнует, когда в сокете или канале установлен флаг O_APPEND.

Попробуйте запустить вашу программу как

./your_program file >/dev/tty

или #include <fcntl.h> и добавьте это в начале вашей функции main():

if(isatty(1)) fcntl(1, F_SETFL, fcntl(1, F_GETFL) & ~O_APPEND);

Обратите внимание, что флаг O_APPEND может привести к случайному включению на вашем терминале: чтобы проверить, включен ли он, посмотрите на /proc/<pid>/fdinfo/<fd>:

$ grep flags /proc/self/fdinfo/1
flags:  0102002
           ^ here it is

Один любопытный пример программы, которая включает флаг O_APPEND на своем стандартном выводе: GNU make ; -)

$ strace -e trace=fcntl make
fcntl(1, F_GETFL) = 0x48002 (flags O_RDWR|O_LARGEFILE|O_NOATIME)
fcntl(1, F_SETFL, O_RDWR|O_APPEND|O_LARGEFILE|O_NOATIME) = 0
...

Контрольный пример, scat.c:

/*
 * a simple program which copies its input to its output via splice(2)
 * if given a '1' argument, it will turn the O_APPEND flag on stdout
 * if given a '0' argument, it will turn it off
 * otherwise it will leave it as it is
 */
#define _GNU_SOURCE     1
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <err.h>
int main(int ac, char **av){
        ssize_t z; int fl;
        if(ac > 1){
                if((fl = fcntl(1, F_GETFL)) == -1) err(1, "fcntl(1, getfl)");
                if(atoi(av[1])) fl |= O_APPEND;
                else fl &= ~O_APPEND;
                if(fcntl(1, F_SETFL, fl)) err(1, "fcntl(1, setfl, %x)", fl);
        }
        while((z = splice(0, 0, 1, 0, 65536, 0)))
                if(z < 0) err(1, "splice");
}
$ cc -Wall scat.c -o scat

$ echo yup | ./scat
yup
$ echo yup | ./scat 1
scat: splice: Invalid argument
$ echo yup | ./scat
scat: splice: Invalid argument
$ echo yup | ./scat 0
yup
$ echo yup | ./scat
yup

OK с выводом трубы или розетки:

$ echo yup | ./scat 1 | cat
yup
$ nc -l -p 9999 &
[4] 23952
$ echo yup | ./scat 1 | nc -q0 localhost 9999
yup
[4]-  Done                    nc -l -p 9999
...