2012-03-26 1 views
4

Мне нужно создать функцию, которая, помимо прочего, порождает дочерний процесс. Я бы хотел передать этой функции дополнительный список файловых дескрипторов, чтобы в зависимости от потребностей пользователя можно было перенаправить вход/выход дочернего процесса.Переадресация выведенного процесса на NULL

Я видел десятки людей говорить о том, как это сделать с помощью dup2 примерно так:

if(pid < 0) // error forking. 
{ 
//... 
} 
else if(pid != 0) // Parent process. 
{ 
    ret = waitpid(pid, &status, 0); 
    return WEXITSTATUS(status); 
} 
else // Child process. 
{ 
    dup2(fd, STDIN_FILENO); // Clone passed file discriptor. 
    close(fd);   // Close the passed one, since we have already cloned. 
    execvp(arglist[ 0 ], arglist); 
} 

Хорошо. Все это в Интернете. Мой вопрос сейчас в том, как (или как лучше всего) перенаправить на /dev/null?

Должен ли я принуждать пользователя к open(/dev/null) и передать его как fd или есть ли лучший способ?


EDIT:

Это не так хорошо, как я хотел, но я не мог найти лучший способ, так что я в конечном итоге передавая массив имен файлов в везде, где пользователь может перенаправить, соответственно, STDIN, STDOUT и STDERR:

static int do_exec(arglist_t arglist, const char *fio[ 3 ]) 
{ 
DEBUG__(OSU_DEBUG_LEVEL_1, "fio = %p\n", fio); 

    if (fio) 
    { 
     if (fio[ STDIN_FILENO ]) 
     { 
      int fd = open(fio[ STDIN_FILENO ], O_RDONLY); 

      if (-1 < fd) 
      { 
       dup2(fd, STDIN_FILENO); 
       close(fd); 
      } 
     } 

     if (fio[ STDOUT_FILENO ]) 
     { 
      int fd = open(fio[ STDOUT_FILENO ], O_WRONLY | O_CREAT | O_APPEND); 

      if (-1 < fd) 
      { 
       dup2(fd, STDOUT_FILENO); 
       close(fd); 
      } 
     } 

     if (fio[ STDERR_FILENO ]) 
     { 
      int fd = open(fio[ STDERR_FILENO ], O_WRONLY | O_CREAT | O_APPEND); 

      if (-1 < fd) 
      { 
       dup2(fd, STDERR_FILENO); 
       close(fd); 
      } 
     } 
    } 

    return execvp(arglist[ 0 ], arglist); 
} 
  • Я еще до конца не тестировал, так что он может иметь некоторые ошибки.

Действительно благодаря @Zack и @gbulmer.

ответ

4

Вы мог бы иметь его в качестве конвенции вашего API, который проходит в -1 для дескриптора означает использовать /dev/null и сделать

// ... same as you have ... 

else // Child process 
{ 
    if (stdin_fd == -1) 
     stdin_fd = open("/dev/null", O_RDONLY); 
    if (stdin_fd == -1) 
     _exit(127); 
    dup2(stdin_fd, STDIN_FILENO); 
    close(stdin_fd); 

    // similarly for stdout and stderr 

    execvp(arglist[0], arglist); 
    _exit(127); 
} 

(Psst: Каждый раз, когда вы кладете пространства внутри ваших скобок, Бог убивает котенка.)

+0

Я знаю, что это сработает, но задайтесь вопросом, лучше ли это сделать. Я мог бы использовать имена файлов, такие как @gbulmer, предлагаемые ниже, проверить строки для NULL и позволить моему API отвечать за открытие. Это будет работать и без ограничений по любому файлу, но все равно оставит мне вкус, который должен быть лучшим способом. Большое спасибо! – j4x

+0

Каким будет для вас «лучший способ»? – zwol

+0

Я хотел бы увидеть решение, в котором мне не нужно явно тестировать параметры, открывать файл по имени и т. Д., Но я думаю, что это будет невозможно, поэтому я сдаюсь. Я сделаю то, что вы предлагаете (см. Мое редактирование). Благодаря! – j4x

1

Детский процесс должен знать, какие fd использовать в качестве oldfd для dup2 и который fd to dup2 на.

Например, почему fd должен быть dup2 на STDIN_FILENO?

Ребенок нуждается в информации для открытого fd, а fd - в dup2'd.

Чтобы обобщить, чтобы покрыть случаи, когда обрабатываются имена файлов, а не только файлы fd, у вас будет способ сказать «/ dev/null», так как это реальное имя файла.

Итак, у вас есть список структур struct { int oldfd; char* filename; int newfd; }, и задание является регулярным, а/dev/null - не особый случай. Когда oldfd равно -1, откройте имя файла newfd вместо dup2'ing.