функциональность pty для сокета

Я пишу демон linux и хочу реализовать возможность настройки его параметров через telnet. У меня есть код интерфейса cli, написанный с использованием библиотеки gnu readline с историей и дополнениями, и я хотел бы использовать этот код интерфейса для демона.

Я пытался перенаправить stdin/stdout на сокет, перенаправить rl_instream/rl_outstream на сокет, прочитать/записать на ведущий/подчиненный pty, но безуспешно.

похожий вопрос задан здесь без каких-либо ответы.

Также прочитайте этот вопрос, но у меня нет дочернего процесса.

Мои вопросы:

  1. Как я могу использовать функциональность pty внутри одного процесса?
  2. Нужно ли мне использовать главный и подчиненный pty, если у меня есть только один процесс?

Пример кода (без операций на pty-устройствах), ожидаемый результат — readline работает правильно

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

Большое спасибо.


person Alexey Bychko    schedule 19.08.2012    source источник
comment
Какие функции PTY не работают? Ничего? Вы говорите о таких вещах, как клавиши курсора, похожие вещи?   -  person Some programmer dude    schedule 19.08.2012
comment
да. я имел в виду привязки клавиш терминала. для прокрутки истории в оболочке вы можете использовать клавишу со стрелкой вверх, но в моем тестовом приложении она печатает: telnet localhost 5000 Trying 127.0.0.1... Connected to localhost. Экранирующий символ '^]'. Я: ^[[А Я: Я: ^[[А^[[А^[[А^[[А телнет›   -  person Alexey Bychko    schedule 19.08.2012


Ответы (3)


Протокол telnet в основном представляет собой линейный протокол, поэтому он не может легко обрабатывать одиночные нажатия клавиш или специальные коды. Вы можете попросить подключенного клиента telnet отправить каждый ключ вместо строк, это делается с помощью так называемого согласования telnet.

Чтобы узнать больше об этом, вы должны прочитать RFC для telnet, особенно RFC 854 и < href="http://www.faqs.org/rfcs/rfc855.html" rel="nofollow">RFC 855. Чтобы отключить редактирование на стороне клиента, вам также следует прочитать RFC 1116. Также посетите страницу Википедии, чтобы увидеть список всех RFC, связанных с telnet.

Короче говоря, вы должны послать клиенту серию команд, чтобы попросить его прекратить обработку линейного режима, и надеяться, что клиент ответит, что он прекратит это делать. Это не простые вещи, на самом деле совсем наоборот. Однако реализовать полноценную машину состояний telnet можно. Даже в этом случае вы, возможно, не сможете правильно использовать библиотеку readline, поскольку нажатия клавиш могут не распознаваться как правильные клавиши вверх/вниз, и вам все равно придется выполнять некоторые переводы. На самом деле я рекомендую вам пропустить обычную обработку stdin/stdout и обработку PTY, а также позволить клиенту обрабатывать редактирование, в то время как вы отслеживаете историю, либо используя функциональность внешней библиотеки, такой как readline, либо имея внутреннюю очередь истории.

person Some programmer dude    schedule 19.08.2012
comment
спасибо, я прочитаю эти RFC. я думаю, что редактирование строки не проблема, потому что я могу читать char за char из сокета. например, while(errno != EGAIN){read(sock, &buff, 1);}. мой вопрос был - есть ли способ использовать возможности pty для сокета в одном процессе? я нашел несколько примеров для процессов fork(), но ни одного для одиночных - person Alexey Bychko; 20.08.2012

Пти не нужен. Вам необходимо настроить новые ключи истории для readline, добавив следующие строки в файл /etc/inputrc или ~/.inputrc:

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

Причина этого:

Обычно терминальные программы (читай: клиент telnet) отправляют escape-коды, когда пользователь нажимает клавишу со стрелкой вверх или вниз.

Коды выхода:

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

Поскольку ваша программа печатает: ^[[A^[[A^[[A^[[A , она не пересчитывает управляющую последовательность. Итак, вам нужно сообщить эти управляющие последовательности readline.

См. дополнительную информацию.

person SKi    schedule 21.08.2012

Readline предоставляет тестовый файл rlPtyTest.c, который проверяет readline через PTY. Вы можете использовать его как шаблон для реализации сокетов. https://github.com/alexmac/alcextra/blob/master/readline-6.2/examples/rlptytest.c

person Mike    schedule 30.01.2014