2016-10-04 2 views
1

Задача

Я пытаюсь передать содержимое из основной процедуры в оболочку execvp'd bash. Я сталкиваюсь с проблемой, когда, когда я пишу «exit» в подоболочку, он не говорит мне, что труба действительно сломана. Должно быть, правда ... правда? Процесс умер, и, таким образом, труба fd также должна вернуть EOF или SIGPIPE. Однако это не так и просто продолжает читать/писать, как обычно.c - порожденная оболочка bash. Shell умерла, но труба не сломалась?

Код

код прилагается здесь:

/************************************************************ 
* Includes: 
* ioctl - useless(?) 
* termios, tcsetattr, tcgetattr - are for setting the 
*  noncanonical, character-at-a-time terminal. 
* fork, exec - creating the child process for part 2. 
* pthread, pipe - creating the pipe process to communicate 
*  with the child shell. 
* kill - to exit the process 
* atexit - does some cleanups. Used in termios, tcsetattr, 
*  tcgetattr. 
************************************************************/ 
#include <sys/ioctl.h> // ioctl 
#include <termios.h> // termios, tcsetattr, tcgetattr 
#include <unistd.h> // fork, exec, pipe 
#include <sys/wait.h> // waitpid 
#include <pthread.h> // pthread 
#include <signal.h> // kill 
#include <stdlib.h> // atexit 
#include <stdio.h> // fprintf and other utility functions 
#include <getopt.h> // getopt 
/********************** 
* GLOBALS 
**********************/ 
pid_t pid; 

/********************** 
* CONSTANTS 
**********************/ 
static const int BUFFER_SIZE = 16; 
static const int STDIN_FD = 0; 
static const int STDOUT_FD = 1; 
static const int STDERR_FD = 2; 

// these attributes are reverted to later 
struct termios saved_attributes; 
// to revert the saved attributes 
void 
reset_input_mode (void) { 
    tcsetattr (STDIN_FILENO, TCSANOW, &saved_attributes); 
} 

// to set the input mode to correct non-canonical mode. 
void 
set_input_mode (void) { 
    struct termios tattr; 

    /* Make sure stdin is a terminal. */ 
    if (!isatty (STDIN_FILENO)) 
    { 
     fprintf (stderr, "Not a terminal.\n"); 
     exit (EXIT_FAILURE); 
    } 

    /* Save the terminal attributes so we can restore them later. */ 
    tcgetattr (STDIN_FILENO, &saved_attributes); 
    atexit (reset_input_mode); 

    /* Set the funny terminal modes. */ 
    tcgetattr (STDIN_FILENO, &tattr); 
    tattr.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */ 
    tattr.c_cc[VMIN] = 1; 
    tattr.c_cc[VTIME] = 0; 
    tcsetattr (STDIN_FILENO, TCSAFLUSH, &tattr); 
} 

// pthread 1 will read from pipe_fd[0], which 
// is really the child's pipe_fd[1](stdout). 
// It then prints out the contents. 
void* thread_read(void* arg){ 
    int* pipe_fd = ((int *) arg); 
    int read_fd = pipe_fd[0]; 
    int write_fd = pipe_fd[1]; 
    char c; 
    while(1){ 
     int bytes_read = read(read_fd, &c, 1); 
     if(bytes_read > 0){ 
      putchar(c); 
     } 
     else{ 
      close(read_fd); 
      close(write_fd); 
      fprintf(stdout, "The read broke."); 
      fflush(stdout); 
      break; 
     } 
    } 
} 

// pthread 2 will write to child_pipe_fd[1], which 
// is really the child's stdin. 
// but in addition to writing to child_pipe_fd[1], 
// we must also print to stdout what our 
// argument was into the terminal. (so pthread 2 
// does extra). 
void* thread_write(void* arg){ 
    set_input_mode(); 
    int* pipe_args = ((int *) arg); 
    int child_read_fd = pipe_args[0]; 
    int child_write_fd = pipe_args[1]; 
    int parent_read_fd = pipe_args[2]; 
    int parent_write_fd = pipe_args[3]; 
    char c; 
    while(1) { 
     int bytes_read = read(STDIN_FD, &c, 1); 
     write(child_write_fd, &c, bytes_read); 
     putchar(c); 
     if(c == 0x04){ 
      // If an EOF has been detected, then 
      // we need to close the pipes. 
      close(child_write_fd); 
      close(child_read_fd); 
      close(parent_write_fd); 
      close(parent_read_fd); 
      kill(pid, SIGHUP); 
      break; 
     } 
    } 
} 

int main(int argc, char* argv[]) { 
    /*************************** 
    * Getopt process here for --shell 
    **************************/ 
    int child_pipe_fd[2]; 
    int parent_pipe_fd[2]; 
    pipe(child_pipe_fd); 
    pipe(parent_pipe_fd); 

    // We need to spawn a subshell. 
    pid = fork(); 
    if(pid < 0){ 
     perror("Forking was unsuccessful. Exiting"); 
     exit(EXIT_FAILURE); 
    } 
    else if(pid == 0){ // is the child. 
     // We dup the fd and close the pipe. 

     close(0); // close stdin. child's pipe should read. 
     dup(child_pipe_fd[0]); // pipe_fd[0] is the read. Make read the stdin. 
     close(child_pipe_fd[0]); 

     close(1); // close stdout 
     dup(parent_pipe_fd[1]); // pipe_fd[1] is the write. Make write the stdout. 
     close(parent_pipe_fd[1]); 

     char* BASH[] = {"/bin/bash", NULL}; 
     execvp(BASH[0], BASH); 
    } 
    else{ // is the parent 
     // We dup the fd and close the pipe. 
     // 
     // create 2 pthreads. 
     // pthread 1 will read from pipe_fd[0], which 
     // is really the child's pipe_fd[1](stdout). 
     // It then prints out the contents. 
     // 
     // pthread 2 will write to pipe_fd[1], which 
     // is really the child's pipe_fd[0](stdin) 
     // but in addition to writing to pipe_fd[1], 
     // we must also print to stdout what our 
     // argument was into the terminal. (so pthread 2 
     // does extra). 
     // 
     // We also need to take care of signal handling: 
     signal(SIGINT, sigint_handler); 
     /*signal(SIGPIPE, sigpipe_handler);*/ 
     int write_args[] = {child_pipe_fd[0], child_pipe_fd[1], 
          parent_pipe_fd[0], parent_pipe_fd[1]}; 

     pthread_t t[2]; 
     pthread_create(t, NULL, thread_read, parent_pipe_fd); 
     pthread_create(t+1, NULL, thread_write, write_args); 

     pthread_join(t[0], NULL); 
     pthread_join(t[1], NULL); 

     int status; 
     if (waitpid(pid, &status, 0) == -1) { 
      perror("Waiting for child failed."); 
      exit(EXIT_FAILURE); 
     } 

     printf("Subshell exited with the error code %d", status); 
     exit(0); 
    }  

    return 0; 
} 

Программа в основном трубы входа с терминала в субоболочке и пытается выполнить их и вернуть выходы. Чтобы написать в трубу, у меня есть pthread, который записывает входные данные stdin в подоболочку. Чтобы прочитать трубку, у меня есть pthread, который читает трубу родительскому. Чтобы обнаружить сломанную трубу через умирающую подоболочку (вызов exit), я обнаруживаю символ EOF из прочитанного потока.

Мои попытки

я добавил проверку на символ 0x04 (EOF), я проверил на read_bytes == 0 или read_bytes < 0. Кажется, что он никогда не получает заметку, если я явно не закрываю трубы на конце записи. Он встречается только с символом EOF, если я отправляю символ^D (который в моем коде обрабатывает путем закрытия всех труб дочернего элемента &).

Любые комментарии будут оценены! Спасибо.

+1

Исправьте в коде (предпочтительно [mcve]) в самом вопросе. Ссылки сторонних производителей неприемлемы, поскольку связанная с ними страница может быть удалена в будущем. –

+0

Несомненно. Я свяжу его здесь после некоторых изменений. – OneRaynyDay

+0

(Кроме того, сторонние ссылки * на pastebin.com * содержат объявления, часто раздражающие анимированные, даже если что-то является дополнительным, а не важной частью вопроса, рассмотрите возможность использования https://gist.github.com/ вместо). –

ответ

1

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

Измените код следующим образом:

else { 
    // pid >0; this is the parent 
    close(child_pipe_fd[0]); // ADD THIS LINE 
    close(parent_pipe_fd[1]); // ADD THIS LINE 
+0

Это исправило это! Большое спасибо.Ваш ответ научил меня отличному уроку - чтобы убедиться, что есть 1 ссылка на каждый fd за раз, если это абсолютно необходимо. – OneRaynyDay

Смежные вопросы