pty functionality for socket

1.6k Views Asked by At

I'm writing the linux daemon and I want to implement ability to configure its params via telnet. I have a cli interface code, written using gnu readline library with history and completers and I'd like to using that interface code for daemon.

I tried to redirect stdin/stdout to socket, redirected rl_instream/rl_outstream to socket, read/write to master/slave pty, but without success.

similar question asked here without any answers.

Also read this question, but I do not have child process.

My questions are:

  1. How can I use pty functionality inside the single process?
  2. Do I need to use master and slave pty if I have single process only?

Code example (with no operations on pty devices), expected result - readline works properly

char* readline_buff;

int main(void){
int mSock = socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
int socketfd, n, flag = 1;

int addrlen;
daemon(1,1);
setsockopt(mSock, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int));
struct sockaddr_in addr;
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(5000);
addr.sin_addr.s_addr = INADDR_ANY;
bind(mSock, (const sockaddr*)&addr, sizeof(addr));
listen(mSock,SOMAXCONN);
addrlen = sizeof(addrlen);
bzero(&addr, sizeof(addr));

int m_pty = posix_openpt(O_RDWR);
grantpt(m_pty);
unlockpt(m_pty);
string m_ptsname = ptsname(m_pty);
int slave = open(m_ptsname.c_str(), O_RDWR);
//
socketfd = accept(mSock, (struct sockaddr *) &addr, (socklen_t*)&addrlen);
//
close(STDOUT_FILENO);
dup2(socketfd, STDOUT_FILENO);
close(STDIN_FILENO);
dup2(socketfd, STDIN_FILENO);
close(STDERR_FILENO);
dup2(socketfd, STDERR_FILENO);
//
 while(true){   
  readline_buff = readline("ME: ");
  add_history(readline_buff);
  free(readline_buff);
}
return 0;
}

Many thanks.

3

There are 3 best solutions below

1
On

The telnet protocol is basically a line-based protocol, therefore it doesn't handle single key-presses or special codes easily. You can ask the connected telnet client to send every single key instead of lines, this is done by something called telnet negotiation.

To learn more about this, you should read the telnet RFC's, most importantly RFC 854 and RFC 855. For for disabling client-side editing you should also read RFC 1116. Also check the Wikipedia page to see a list of all telnet related RFC's.

In short you have to send a series of commands to the client to ask it to stop doing line-mode handling, and hope the client answers back that it will stop doing it. This is not simple stuff, quite the opposite actually. To implement a complete telnet state-machine can be though. Even then, you might not be able to properly use the readline library, as the keystrokes might not be recognized as proper up/down keys, and you might have to do some translations anyway. I actually recommend you to skip the normal stdin/stdout handling and PTY handling as well, and let the client handle editing, while you keep track of the history, either by using the functionality of an external library like readline, or by having an internal history queue.

0
On

Readline provides a test file rlPtyTest.c that tests the readline via PTY. You can use it as a template for implementing sockets. https://github.com/alexmac/alcextra/blob/master/readline-6.2/examples/rlptytest.c

0
On

Pty is not needed. You need to configure new history keys for readline by adding the following lines to /etc/inputrc or ~/.inputrc file:

"\e[A":history-search-backward
"\e[B":history-search-forward

Reason for that:

Usually terminal programs (read: telnet client) sends escape codes when user press Up or Down arrow key.

Escape codes are:

\33[A   - up arrow 
\33[B   - down arrow

Because your program prints: ^[[A^[[A^[[A^[[A , it does not regonise the escape sequence. So you need to tell those escape sequences to the readline.

See more information