Usermode/telnetd - Update to proper PTY use
[tpg/acess2.git] / Usermode / Applications / telnetd_src / main.c
1 /*
2  * Acess2 Telnet Server (TCP server test case)
3  * - By John Hodge (thePowersGang)
4  *
5  * main.c
6  * - All
7  */
8 #include <stddef.h>
9 #include <net.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <acess/sys.h>
13 #include <assert.h>
14 #include <errno.h>
15 #include <acess/devices/pty.h>
16
17 // === TYPES ===
18 enum eTelnetMode
19 {
20         MODE_DATA,
21         MODE_IAC,
22         MODE_WILL,
23         MODE_WONT,
24         MODE_DO,
25         MODE_DONT
26 };
27
28 typedef struct sClient
29 {
30         enum eTelnetMode        Mode;
31          int    Socket;
32          int    pty;
33 } tClient;
34
35 // === PROTOTYPES ===
36 void    EventLoop(void);
37 void    Server_NewClient(int FD);
38 void    HandleServerBoundData(tClient *Client);
39 void    HandleClientBoundData(tClient *Client);
40
41 // === GLOBALS ===
42 // --- Configuration ---
43  int    giConfig_MaxClients = 5;
44 // --- State ---
45  int    giServerFD;
46 tClient *gaClients;
47  int    giNumClients;
48
49 // === CODE ===
50 int main(int argc, char *argv[])
51 {
52         // TODO: Configure
53
54         // Initialise
55         gaClients = calloc(giConfig_MaxClients, sizeof(tClient));
56
57         // Open server
58         {
59                 uint8_t data[16];
60                  int    addrtype = Net_ParseAddress("10.0.2.10", data);
61                  int    port = 23;
62                 giServerFD = Net_OpenSocket(addrtype, data, "tcps");
63                 _SysIOCtl(giServerFD, 4, &port);        // Set port and start listening
64         }
65
66         // Event loop
67         EventLoop();
68         
69         return 0;
70 }
71
72 static void FD_SET_MAX(fd_set *set, int fd, int *maxfd)
73 {
74         FD_SET(fd, set);
75         if(*maxfd < fd) *maxfd = fd;
76 }
77
78 void EventLoop(void)
79 {
80         fd_set  fds;
81          int    maxfd;
82
83         for( ;; )
84         {
85                 FD_ZERO(&fds);
86                 maxfd = 0;
87                 // Fill select
88                 FD_SET_MAX(&fds, giServerFD, &maxfd);
89                 for( int i = 0; i < giConfig_MaxClients; i ++ )
90                 {
91                         if( gaClients[i].Socket == 0 )  continue ;
92                         FD_SET_MAX(&fds, gaClients[i].Socket, &maxfd);
93                         FD_SET_MAX(&fds, gaClients[i].pty,  &maxfd);
94                 }
95                 
96                 // Select!
97                 _SysSelect( maxfd+1, &fds, NULL, NULL, NULL, 0 );
98                 
99                 // Check events
100                 if( FD_ISSET(giServerFD, &fds) )
101                 {
102                         Server_NewClient(giServerFD);
103                 }
104                 for( int i = 0; i < giConfig_MaxClients; i ++ )
105                 {
106                         if( FD_ISSET(gaClients[i].Socket, &fds) )
107                         {
108                                 // Handle client data
109                                 HandleServerBoundData(&gaClients[i]);
110                         }
111                         if( FD_ISSET(gaClients[i].pty, &fds) )
112                         {
113                                 // Handle output from terminal
114                                 HandleClientBoundData(&gaClients[i]);
115                         }
116                 }
117         }
118 }
119
120 void Server_NewClient(int FD)
121 {
122         tClient *clt = NULL;
123         
124         // TODO: Is this done in the IPStack?
125         if( giNumClients == giConfig_MaxClients )
126         {
127                 // Open, reject
128                 _SysClose( _SysOpenChild(FD, "", OPENFLAG_READ) );
129                 return ;
130         }
131         
132         // Allocate client structure and open socket
133         for( int i = 0; i < giConfig_MaxClients; i ++ )
134         {
135                 if( gaClients[i].Socket == 0 ) {
136                         clt = &gaClients[i];
137                         break;
138                 }
139         }
140         assert(clt);
141         // Accept the connection
142         clt->Socket = _SysOpenChild(FD, "", OPENFLAG_READ|OPENFLAG_WRITE);
143         giNumClients ++;
144         
145         // Create PTY
146         // TODO: Use PTYs
147         clt->pty = _SysOpen("/Devices/pts/ptmx", OPENFLAG_READ|OPENFLAG_WRITE);
148         if( clt->pty < 0 ) {
149                 perror("Unable to create/open PTY");
150                 _SysDebug("Unable to create/open PTY: %s", strerror(errno));
151                 _SysClose(clt->Socket);
152                 clt->Socket = 0;
153                 return ;
154         }
155         // - Initialise
156         {
157                 _SysIOCtl(clt->pty, PTY_IOCTL_SETID, "telnetd0");
158                 struct ptymode  mode = {.InputMode = 0, .OutputMode=0};
159                 struct ptydims  dims = {.W = 80, .H = 25};
160                 _SysIOCtl(clt->pty, PTY_IOCTL_SETMODE, &mode);
161                 _SysIOCtl(clt->pty, PTY_IOCTL_SETDIMS, &dims);
162         }
163         
164         // TODO: Arguments and envp
165         {
166                  int    clientfd = _SysOpen("/Devices/pts/telnetd0", OPENFLAG_READ|OPENFLAG_WRITE);
167                 if(clientfd < 0) {
168                         perror("Unable to open login PTY");
169                         _SysClose(clt->Socket);
170                         _SysClose(clt->pty);
171                         clt->Socket = 0;
172                         return ;
173                 }
174                 int fds[3] = {clientfd, clientfd, clientfd};
175                 const char      *argv[] = {"login", NULL};
176                 _SysSpawn("/Acess/SBin/login", argv, argv, 3, fds, NULL);
177         }
178 }
179
180 void HandleServerBoundData(tClient *Client)
181 {
182         uint8_t buf[BUFSIZ];
183         size_t  len;
184         
185         len = _SysRead(Client->Socket, buf, BUFSIZ);
186         if( len == 0 )  return ;
187         if( len == -1 ) {
188                 return ;
189         }
190         // handle options
191          int    last_flush = 0;
192         for( int i = 0; i < len; i ++ )
193         {
194                 switch(Client->Mode)
195                 {
196                 case MODE_IAC:
197                         Client->Mode = MODE_DATA;
198                         switch(buf[i])
199                         {
200                         case 240:       // End of subnegotiation parameters
201                                 _SysDebug("End Subnegotiation");
202                                 break;
203                         case 241:       // Nop
204                                 break;
205                         case 242:       // SYNCH
206                         case 243:       // NVT Break
207                         case 244:       // Function 'IP' (Ctrl-C)
208                                 
209                                 break;
210                         case 245:       // Function 'AO'
211                         case 246:       // Function 'AYT'
212                         case 247:       // Function 'EC'
213                         case 248:       // Function 'EL'
214                         case 249:       // GA Signal
215                                 break;
216                         case 250:       // Subnegotation
217                                 _SysDebug("Subnegotiation");
218                                 break;
219                         case 251:       // WILL
220                                 Client->Mode = MODE_WILL;
221                                 break;
222                         case 252:       // WONT
223                                 Client->Mode = MODE_WONT;
224                                 break;
225                         case 253:       // DO
226                                 Client->Mode = MODE_DO;
227                                 break;
228                         case 254:       // DONT
229                                 Client->Mode = MODE_DONT;
230                                 break;
231                         case 255:       // Literal 255
232                                 Client->Mode = MODE_DATA;
233                                 i --;   // hacky!
234                                 break;
235                         }
236                         break;
237                 case MODE_WILL:
238                         _SysDebug("Option %i WILL", buf[i]);
239                         Client->Mode = MODE_DATA;
240                         break;
241                 case MODE_WONT:
242                         _SysDebug("Option %i WONT", buf[i]);
243                         Client->Mode = MODE_DATA;
244                         break;
245                 case MODE_DO:
246                         _SysDebug("Option %i DO", buf[i]);
247                         Client->Mode = MODE_DATA;
248                         break;
249                 case MODE_DONT:
250                         _SysDebug("Option %i DONT", buf[i]);
251                         Client->Mode = MODE_DATA;
252                         break;
253                 case MODE_DATA:
254                         if( buf[i] == 255 )
255                                 Client->Mode = MODE_IAC;
256                         else
257                                 _SysWrite(Client->pty, buf+i, 1);
258                         break;
259                 }
260         }
261 }
262
263 void HandleClientBoundData(tClient *Client)
264 {
265         char    buf[BUFSIZ];
266          int    len;
267         
268         len = _SysRead(Client->pty, buf, BUFSIZ);
269         if( len <= 0 )  return ;
270         _SysWrite(Client->Socket, buf, len);
271 }
272

UCC git Repository :: git.ucc.asn.au