2017-11-24 277 views
5

piszę pager pspg. Tam muszę rozwiązać następujący problem. Po przeczytaniu z stdin powinienem ponownie przydzielić stdin z poprzedniego czytania z rury do czytania z terminalu.Jak wykryć urządzenia terminala przypisanego do pracy interaktywnej

użyłem

freopen("/dev/tty", "r", stdin) 

Ale to nie działa, gdy pager był używany z poleceniem, co nie zostało zrealizowane bezpośrednio

su - someuser -c 'export PAGER=pspg psql somedb' 

W tym przypadku, mam błąd: Nie ma takiego urządzenie lub adres.

znalazłem obejście - Teraz kod wygląda następująco:

if (freopen("/dev/tty", "r", stdin) == NULL) 
{ 
    /* 
    * try to reopen pty. 
    * Workaround from: 
    * https://cboard.cprogramming.com/c-programming/172533-how-read-pipe-while-keeping-interactive-keyboard-c.html 
    */ 
    if (freopen(ttyname(fileno(stdout)), "r", stdin) == NULL) 
    { 
     fprintf(stderr, "cannot to reopen stdin: %s\n", strerror(errno)); 
     exit(1); 
    } 
} 

Co to jest poprawny sposób, aby wykryć przypisany urządzenie końcowe w tym przypadku?

Ale to rozwiązanie nie jest poprawne. Naprawiono jedną kwestię, ale kolejna nadchodzi. Kiedy someuser jest inny niż bieżący użytkownik, a następnie otworzyć ponownie nie powiedzie się z błąd Permission denied. Więc tego obejścia nie można wykorzystać do moich celów.

+0

Nie rozumiem problemu 'su - -c użytkownika cat'' przeczytać mój terminalu wejście i wyjście w moim terminalu. "poprzednie czytanie z rury do czytania z terminalu.", które nie ma dla mnie sensu. Mówię ci, że ponieważ podejrzewam problem XY, to, co próbujesz zrobić, jest bardzo dziwne. – Stargateur

+0

pager odczytuje dane ze standardowego wejścia, ale musi przełączać się na/dev/tty, aby był interaktywny. Przełącznik to problem. Zwykła aplikacja tego nie robi i używa stdin, stdout przygotowanego przez środowisko macierzyste. Pager jest inny - odczytuje dane ze standardowego wejścia, ale musi też czytać klawiaturę. –

Odpowiedz

2

Co less robi w tej sytuacji jest spadek do fd 2 (stderr). Jeśli stderr został przekierowany z tty, przestaje próbować uzyskać dostęp do klawiatury i po prostu drukuje cały strumień wejściowy bez stronicowania.

Konstrukcja su nie pozwala na nic lepszego. Nowy użytkownik uruchamia polecenie na teście należącym do oryginalnego użytkownika i ten nieprzyjemny fakt nie może być całkowicie ukryty.

Oto miły substytutem su że nie ma tego problemu:

ssh -t localhost -l username sh -c 'command' 

Ma trochę więcej narzut, oczywiście.

+0

less ma bezpośredni odczyt z stdout, bez ponownego otwierania do stdin, co robię i co nie działa.Prawdopodobnie tylko jeden możliwy sposób to ta sama konfiguracja. –

+0

To jest najbardziej zbliżone do mojego rozwiązania –

1

Na koniec użyłem wzoru, który znalazłem w less pager, ale zmodyfikowane do korzystania z ncurses:

Najpierw spróbuj ponownie otworzyć stdin do pewnego tty powiązanego urządzenia:

if (!isatty(fileno(stdin))) 
{ 
    if (freopen("/dev/tty", "r", stdin) != NULL) 
     noatty = false; 
    /* when tty is not accessible, try to get tty from stdout */ 
    else if (freopen(ttyname(fileno(stdout)), "r", stdin) != NULL) 
     noatty = false; 
    else 
    { 
     /* 
     * just ensure stderr is joined to tty, usually when reopen 
     * of fileno(stdout) fails - probably due permissions. 
     */ 
     if (!isatty(fileno(stderr))) 
     { 
      fprintf(stderr, "missing a access to terminal device\n"); 
      exit(1); 
     } 
     noatty = true; 
     fclose(stdin); 
    } 
}     
else 
    noatty = false; 

Gdy nie mam i nie mogę używać stdin, używam funkcji newterm, która pozwala określić strumień wejściowy:

if (noatty) 
    /* use stderr like stdin. This is fallback solution used by less */ 
    newterm(termname(), stdout, stderr); 
else 
    /* stdin is joined with tty, then use usual initialization */ 
    initscr();