How to identify a designated endpoint device for interactive work

I am writing a pspg pager. There I have to solve the following problem. After reading from stdin I need to reassign stdin from the previous read from the pipe to read from the terminal.

I used

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

But this does not work when the pager was used from a command that was not executed directly

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

In this case, I received an error message: There is no such device or address .

I found a workaround - now the code looks like this:

 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); } } 

What is the correct way to detect the assigned terminal device in this case?

But this workaround is wrong. He fixed one question, but the next one goes. When someuser is different from the current user, reopening fails with a Permission denied error. Therefore, this workaround cannot be used for my purposes.

+5
source share
2 answers

What less does in this situation is returning to fd 2 (stderr). If stderr was redirected from tty, it refuses to try keyboard input and simply prints the entire input stream without paging.

The su construct does nothing better. The new user runs a command on tty owned by the original user, and this unpleasant fact cannot be completely hidden.

Here is a good replacement for su that does not have this problem:

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

Of course, he has a bit more overhead.

+2
source

In the end, I used a template that I found in less pager, but modified for use with ncurses:

First, I try to open stdin again for some tty-related device:

 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; 

If I don’t have tty and cannot use stdin , then I use the newterm functions, which allow you to specify the input stream:

 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(); 
+1
source

Source: https://habr.com/ru/post/1273622/


All Articles