Como posso imprimir a saída enquanto desanexada do terminal de controle?

1

A resposta óbvia é que não posso imprimir a saída para o terminal se eu tiver desconectado dela. O problema é que, na verdade, posso enviar caracteres para um terminal do qual eu desanexei e esses caracteres aparecem no meu terminal.

Esta é realmente uma questão sobre como o unix lida com o terminal de controle, embora contenha uma boa quantidade de código C.

De qualquer forma, o terminal de controle é /dev/tty , e eu certamente posso imprimir a saída para o meu xterm da seguinte forma:

[grochmal@haps term]$ echo yay > /dev/tty
yay

Mas se eu me soltar desse terminal, não poderei mais fazer isso. ou seja, se /dev/tty não existe, é porque o processo atual não possui um terminal de controle. Estou aceitando essa suposição de man 4 tty , que afirma:

TIOCNOTTY
   Detach the calling process from its controlling terminal.

   If  the process is the session leader, then SIGHUP and SIGCONT signals are sent to the foreground process group and
   all processes in the current session lose their controlling tty.

   This ioctl(2) call works only on file descriptors connected to /dev/tty.  It is used by daemon processes when  they
   are  invoked  by  a  user at a terminal.  The process attempts to open /dev/tty.  If the open succeeds, it detaches
   itself from the terminal by using TIOCNOTTY, while if the open fails, it is obviously not attached  to  a  terminal
   and does not need to detach itself.

Agora, para desanexar do terminal, eu uso man 2 setsid , pois uma nova sessão será iniciada sem um terminal de controle. Aqui está o trecho que estou usando:

/* use latest but standard stuff */
#define _XOPEN_SOURCE 700

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>

int
main (int argc, char **argv)
{
    int chk;
    char *def_term = "/dev/tty";

    /* print info to the terminal */
    printf("PID [%ld] PPID [%ld] GRPID [%ld] SESID [%ld]\n"
          , (long) getpid(), (long) getppid()
          , (long) getgid(), (long) getsid(0)
          );

    /* check terminal */
    chk = open(def_term, O_RDONLY);
    if (-1 != chk)
        printf("We have %s\n", def_term);
    else
        printf("NO %s\n", def_term);

    fflush(NULL);  /* flush stdio buffers */

    chk = fork();
    switch(chk) {
        case -1:
            printf("BOOM!");
            exit(1);  /* exit flushing buffers */
            break;

        case 0:
            /* ensure that the parent died, so we are adopted by init */
            sleep(2);

            chk = setsid();
            if (-1 != chk)
                printf("We got a new session.\n");
            else
                printf("Session failed! [%s]\n", strerror(errno));

            /* use the *non-existent!* terminal */
            chk = open(def_term, O_RDONLY);
            if (-1 != chk)
                printf("We have %s\n", def_term);
            else
                printf("NO %s\n", def_term);

            printf("PID [%ld] PPID [%ld] GRPID [%ld] SESID [%ld]\n"
                  , (long) getpid(), (long) getppid()
                  , (long) getgid(), (long) getsid(0)
                  );

            break;

        default:
            _exit(1);  /* do not flush, we have children */
            break;
    }
    return 0;
}

Tudo o que o código acima faz é:

  1. imprima algumas informações;
  2. fork() para garantir que setsid() funcione, pois a criança nunca será líder do grupo de processos;
  3. setsid() , que se destaca do terminal;
  4. esperar que o pai retorne e o filho seja adotado por init , apenas no caso;
  5. verifique se não podemos abrir /dev/tty ;
  6. imprima o material, que deve ser enviado para algum lugar.

Compilar isso e executar produz a saída a seguir (observe o prompt intercalado, já que o pai retornou e o shell imprimiu o prompt).

[grochmal@haps term]$ gcc -Wall -o detach detach.c 
[grochmal@haps term]$ ./detach 
PID [29943] PPID [679] GRPID [100] SESID [679]
We have /dev/tty
[grochmal@haps term]$ We got a new session.
NO /dev/tty
PID [29944] PPID [1] GRPID [100] SESID [29944]

A pergunta é: Por que as três últimas linhas são realmente impressas?

Não tenho terminal de controle, /dev/tty não pôde ser aberto. Como o kernel descobriu que deveria redirecionar a saída do filho para o xterm que eu abri e estou executando? Isso deveria acontecer?

    
por grochmal 15.05.2017 / 23:06

1 resposta

3

Os três descritores de arquivo padrão (entrada, saída e erro padrão) apontam para a linha do terminal (por exemplo, /dev/pts/0 ) por padrão se você iniciar seu programa a partir de um terminal. Você não modifica esses descritores e, portanto, eles ainda se referem a este terminal em todo o seu programa.

Você sempre pode enviar dados para uma linha de terminal se tiver as permissões adequadas. Por exemplo. abra dois emuladores de terminal, em um deles execute tty , digamos que imprima /dev/pts/0 . Então do outro execute algo como echo foo > /dev/pts/0 , ele aparecerá no primeiro.

Isso não tem nada a ver com o terminal controle .

    
por 16.05.2017 / 00:19