Kernel - Integrated PTY with VTerm, userland currently broken
[tpg/acess2.git] / KernelLand / Kernel / drv / pty.c
1 /*
2  * Acess2 Kernel
3  * - By John Hodge (thePowersGang)
4  *
5  * drv/pty.c
6  * - Pseudo Terminals
7  */
8 #define DEBUG   0
9 #include <acess.h>
10 #include <vfs.h>
11 #include <fs_devfs.h>
12 #include <drv_pty.h>
13 #include <modules.h>
14 #include <rwlock.h>
15 #include <mutex.h>
16
17 // === CONSTANTS ===
18 #define OUTPUT_RINGBUFFER_LEN   1024    // Number of bytes in output queue before client blocks
19 #define INPUT_RINGBUFFER_LEN    256     // Number of bytes in input queue before being dropped
20 #define INPUT_LINE_LEN  256
21
22 // === TYPES ===
23 struct sPTY
24 {
25         tPTY    *Next;
26         
27         char    *Name;
28          int    NumericName;
29         
30         void    *OutputHandle;
31         tPTY_OutputFcn  OutputFcn;
32         tPTY_ReqResize  ReqResize;
33         tPTY_ModeSet    ModeSet;
34
35         struct ptymode  Mode;
36         struct ptydims  Dims;
37
38          int    HasHitEOF;      
39         tMutex  InputMutex;
40          int    InputWritePos;
41          int    InputReadPos;
42         char    InputData[INPUT_RINGBUFFER_LEN];
43         
44          int    LineLength;
45         char    LineData[INPUT_LINE_LEN];
46
47         tMutex  OutputMutex;    
48          int    OutputWritePos;
49          int    OutputReadPos;
50         char    OutputData[OUTPUT_RINGBUFFER_LEN];
51         
52         tVFS_Node       ClientNode;
53         tVFS_Node       ServerNode;
54         tVFS_ACL        OwnerRW;
55         
56         // TODO: Maintain list of client PIDs
57 };
58
59 // === PROTOTYPES ===
60  int    PTY_Install(char **Arguments);
61  int    PTY_ReadDir(tVFS_Node *Node, int Pos, char Name[FILENAME_MAX]);
62 tVFS_Node       *PTY_FindDir(tVFS_Node *Node, const char *Name, Uint Flags);
63 tVFS_Node       *PTY_MkNod(tVFS_Node *Node, const char *Name, Uint Mode);
64
65 size_t  _rb_write(void *buf, size_t buflen, int *rd, int *wr, const void *data, size_t len);
66 size_t  _rb_read(void *buf, size_t buflen, int *rd, int *wr, void *data, size_t len);
67 size_t  PTY_int_WriteInput(tPTY *PTY, const char *Input, size_t Length);
68 size_t  PTY_int_SendInput(tPTY *PTY, const char *Input, size_t Length);
69 // PTY_SendInput
70 size_t  PTY_ReadClient(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer, Uint Flags);
71 size_t  PTY_WriteClient(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer, Uint Flags);
72  int    PTY_IOCtlClient(tVFS_Node *Node, int ID, void *Arg);
73 void    PTY_ReferenceClient(tVFS_Node *Node);
74 void    PTY_CloseClient(tVFS_Node *Node);
75 size_t  PTY_ReadServer(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer, Uint Flags);
76 size_t  PTY_WriteServer(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer, Uint Flags);
77  int    PTY_IOCtlServer(tVFS_Node *Node, int ID, void *Arg);
78 void    PTY_CloseServer(tVFS_Node *Node);
79
80 // === GLOBALS ===
81 MODULE_DEFINE(0, 0x100, PTY, PTY_Install, NULL, NULL);
82 tVFS_NodeType   gPTY_NodeType_Root = {
83         .TypeName = "PTY-Root",
84         .ReadDir = PTY_ReadDir,
85         .FindDir = PTY_FindDir,
86         .MkNod = PTY_MkNod
87 };
88 tVFS_NodeType   gPTY_NodeType_Client = {
89         .TypeName = "PTY-Client",
90         .Read = PTY_ReadClient,
91         .Write = PTY_WriteClient,
92         .IOCtl = PTY_IOCtlClient,
93         .Reference = PTY_ReferenceClient,
94         .Close = PTY_CloseClient
95 };
96 tVFS_NodeType   gPTY_NodeType_Server = {
97         .TypeName = "PTY-Server",
98         .Read = PTY_ReadServer,
99         .Write = PTY_WriteServer,
100         .IOCtl = PTY_IOCtlServer,
101         .Close = PTY_CloseServer
102 };
103 tDevFS_Driver   gPTY_Driver = {
104         .Name = "pts",
105         .RootNode = {
106                 .Flags = VFS_FFLAG_DIRECTORY,
107                 .Type = &gPTY_NodeType_Root
108         }
109 };
110  int    giPTY_NumCount;
111 tRWLock glPTY_NumPTYs;
112 tPTY    *gpPTY_FirstNumPTY;
113  int    giPTY_NamedCount;
114 tRWLock glPTY_NamedPTYs;
115 tPTY    *gpPTY_FirstNamedPTY;
116
117 // === CODE ===
118 int PTY_Install(char **Arguments)
119 {
120         DevFS_AddDevice(&gPTY_Driver);
121         return MODULE_ERR_OK;
122 }
123
124 // --- Management ---
125 tPTY *PTY_Create(const char *Name, void *Handle, tPTY_OutputFcn Output, tPTY_ReqResize ReqResize, tPTY_ModeSet ModeSet)
126 {
127         tPTY    **prev_np = NULL;
128         size_t  namelen;
129          int    idx = 1;
130         if( Name && Name[0] )
131         {
132                 prev_np = &gpPTY_FirstNamedPTY;
133                 
134                 // Check the name isn't decimal
135                 char *end;
136                 if( strtol(Name, &end, 10) != 0 && *end == '\0' ) {
137                         errno = EINVAL;
138                         return NULL;
139                 }
140                 
141                 RWLock_AcquireWrite(&glPTY_NamedPTYs);
142                 // Detect duplicates
143                 for( tPTY *pty = gpPTY_FirstNamedPTY; pty; prev_np = &pty->Next, pty = pty->Next )
144                 {
145                          int    cmp = strcmp(pty->Name, Name);
146                         if( cmp < 0 )
147                                 continue;
148                         if( cmp == 0 ) {
149                                 RWLock_Release(&glPTY_NamedPTYs);
150                                 errno = EEXIST;
151                                 return NULL;
152                         }
153                         break;
154                 }
155                 namelen = strlen(Name);
156                 idx = -1;
157         }
158         else
159         {
160                 RWLock_AcquireWrite(&glPTY_NumPTYs);
161                 // Get a pty ID if Name==NULL
162                 prev_np = &gpPTY_FirstNumPTY;
163                 for( tPTY *pty = gpPTY_FirstNumPTY; pty; prev_np = &pty->Next, pty = pty->Next )
164                 {
165                         if( pty->NumericName > idx )
166                                 break;
167                         idx ++;
168                 }
169                 namelen = 0;
170         }
171         
172         tPTY *ret = calloc(sizeof(tPTY) + namelen + 1, 1);
173         if(!ret) {
174                 errno = ENOMEM;
175                 return NULL;
176         }
177         
178         // - List maintainance
179         ret->Next = *prev_np;
180         *prev_np = ret;
181         // - PTY Name (Used by VT)
182         ret->Name = (char*)(ret + 1);
183         if(Name)
184                 strcpy(ret->Name, Name);
185         else
186                 ret->Name[0] = 0;
187         ret->NumericName = idx;
188         // - Output function and handle (same again)
189         ret->OutputHandle = Handle;
190         ret->OutputFcn = Output;
191         ret->ReqResize = ReqResize;
192         ret->ModeSet = ModeSet;
193         // - Server node
194         ret->ServerNode.ImplPtr = ret;
195         ret->ServerNode.Type = &gPTY_NodeType_Server;
196         ret->ServerNode.UID = Threads_GetUID();
197         ret->ServerNode.GID = Threads_GetGID();
198         ret->ServerNode.NumACLs = 1;
199         ret->ServerNode.ACLs = &ret->OwnerRW;
200         ret->ServerNode.ReferenceCount = (Output ? 1 : 0);      // Prevent a userland close killing a kernel pty
201         // - Client node
202         ret->ClientNode.ImplPtr = ret;
203         ret->ClientNode.Type = &gPTY_NodeType_Client;
204         ret->ClientNode.UID = Threads_GetUID();
205         ret->ClientNode.GID = Threads_GetGID();
206         ret->ClientNode.NumACLs = 1;
207         ret->ClientNode.ACLs = &ret->OwnerRW;
208         // - Owner Read-Write ACL
209         ret->OwnerRW.Ent.ID = Threads_GetUID();
210         ret->OwnerRW.Perm.Perms = -1;
211
212         if( Name && Name[0] ) {
213                 giPTY_NamedCount ++;
214                 RWLock_Release(&glPTY_NamedPTYs);
215         }
216         else {
217                 giPTY_NumCount ++;
218                 RWLock_Release(&glPTY_NumPTYs); 
219         }
220
221         return ret;
222 }
223
224 int PTY_SetAttrib(tPTY *PTY, const struct ptydims *Dims, const struct ptymode *Mode, int WasClient)
225 {
226         if( Mode )
227         {
228                 // (for now) userland terminals can't be put into framebuffer mode
229                 if( !PTY->OutputFcn && (Mode->OutputMode & PTYOMODE_BUFFMT) == PTYBUFFMT_FB ) {
230                         errno = EINVAL;
231                         return -1;
232                 }
233                 PTY->Mode = *Mode;
234                 if( !WasClient )
235                 {
236                         if( PTY->ModeSet && PTY->ModeSet(PTY->OutputHandle, Mode) )
237                         {
238                                 errno = EINVAL;
239                                 return -1;
240                         }
241                         else if( !PTY->OutputFcn )
242                         {
243                                 Log_Warning("PTY", "TODO: Need to stop client output until modeset has been ACKed");
244                                 // Block write until acked
245                                 // ACK by server doing GETMODE
246                         }
247                 }
248         }
249         if( Dims )
250         {
251                 if( WasClient ) {
252                         // Poke the server?
253                         if( PTY->ReqResize && PTY->ReqResize(PTY->OutputHandle, Dims) )
254                         {
255                                 errno = EINVAL;
256                                 return -1;
257                         }
258                         else if( !PTY->OutputFcn )
259                         {
260                                 // Inform server process... somehow
261                         }
262                 }
263                 else {
264                         // SIGWINSZ to client
265                 }
266                 PTY->Dims = *Dims;
267         }
268         return 0;
269 }
270
271 void PTY_Close(tPTY *PTY)
272 {
273         
274 }
275
276 size_t _rb_write(void *buf, size_t buflen, int *rd, int *wr, const void *data, size_t len)
277 {
278         size_t space = (*rd - *wr + buflen - 1) % buflen;
279         ENTER("pbuf ibuflen prd pwr pdata ilen", buf, buflen, rd, wr, data, len);
280         len = MIN(space, len);
281         LOG("space = %i, *rd = %i, *wr = %i", space, *rd, *wr);
282         if(*wr + len >= buflen) {
283                 size_t prelen = buflen - *wr;
284                 memcpy((char*)buf + *wr, data, prelen);
285                 memcpy(buf, (char*)data + prelen, len - prelen);
286                 *wr = len - prelen;
287         }
288         else {
289                 memcpy((char*)buf + *wr, data, len);
290                 *wr += len;
291         }
292         LEAVE('i', len);
293         return len;
294 }
295 size_t _rb_read(void *buf, size_t buflen, int *rd, int *wr, void *data, size_t len)
296 {
297         size_t space = (*wr - *rd + buflen) % buflen;
298         len = MIN(space, len);
299         if(*rd + len >= buflen) {
300                 size_t prelen = buflen - *rd;
301                 memcpy(data, (char*)buf + *rd, prelen);
302                 memcpy((char*)data + prelen, buf, len - prelen);
303                 *rd = len - prelen;
304         }
305         else {
306                 memcpy(data, (char*)buf + *rd, len);
307                 *rd += len;
308         }
309         return len;
310 }
311
312 size_t PTY_int_WriteInput(tPTY *PTY, const char *Input, size_t Length)
313 {
314         size_t  ret;
315
316         Mutex_Acquire(&PTY->InputMutex);        
317
318         ret = _rb_write(PTY->InputData, INPUT_RINGBUFFER_LEN, &PTY->InputReadPos, &PTY->InputWritePos,
319                 Input, Length);
320         
321         Mutex_Release(&PTY->InputMutex);
322
323         VFS_MarkAvaliable(&PTY->ClientNode, 1);
324         if(ret < Length)
325                 VFS_MarkFull(&PTY->ServerNode, 1);      
326
327         return ret;
328 }
329
330 size_t PTY_int_SendInput(tPTY *PTY, const char *Input, size_t Length)
331 {
332         size_t  ret = 1, print = 1;
333         
334         // Input mode stuff only counts for text output mode
335         // - Any other is Uint32 keypresses
336         if( (PTY->Mode.OutputMode & PTYOMODE_BUFFMT) != PTYBUFFMT_TEXT )
337                 return PTY_int_WriteInput(PTY, Input, Length);
338         // If in raw mode, flush directlr
339         if( (PTY->Mode.InputMode & PTYIMODE_RAW) )
340                 return PTY_int_WriteInput(PTY, Input, Length);
341         
342         if( PTY->Mode.InputMode & PTYIMODE_CANON )
343         {
344                 const char      char_bs = '\b';
345                 switch(Input[0])
346                 {
347                 case 3: // INTR - ^C
348                         // TODO: Send SIGINT
349                         // Threads_PostSignalExt(PTY->ClientThreads, SIGINT);
350                         print = 0;
351                         break;
352                 case 4: // EOF - ^D
353                         PTY_int_WriteInput(PTY, PTY->LineData, PTY->LineLength);
354                         PTY->HasHitEOF = (PTY->LineLength == 0);
355                         PTY->LineLength = 0;
356                         print = 0;
357                         break;
358                 case 8: // Backspace
359                         if(PTY->LineLength != 0)
360                                 PTY->LineLength --;
361                         break;
362                 case 'w'-'a':   // Word erase
363                         while(PTY->LineLength != 0 && isalnum(PTY->LineData[--PTY->LineLength]))
364                                 PTY_WriteClient(&PTY->ClientNode, 0, 1, &char_bs, 0);
365                         print = 0;
366                         break;
367                 case 'u'-'a':   // Kill
368                         while(PTY->LineLength > 0)
369                                 PTY_WriteClient(&PTY->ClientNode, 0, 1, &char_bs, 0);
370                         print = 0;
371                         break;
372                 case 'v'-'a':
373                         Input ++;
374                         Length --;
375                         ret ++;
376                         goto _default;
377                 case '\0':
378                 case '\n':
379                         if(PTY->LineLength == INPUT_LINE_LEN) {
380                                 PTY_int_WriteInput(PTY, PTY->LineData, PTY->LineLength);
381                                 PTY->LineLength = 0;
382                         }
383                         PTY->LineData[PTY->LineLength++] = '\n';
384                         PTY_int_WriteInput(PTY, PTY->LineData, PTY->LineLength);
385                         PTY->LineLength = 0;
386                         break;
387                 // TODO: Handle ^[[D and ^[[C for in-line editing, also ^[[1~/^[[4~ (home/end)
388                 //case 0x1B:
389                 //      break;
390                 default:
391                 _default:
392                         if(PTY->LineLength == INPUT_LINE_LEN) {
393                                 PTY_int_WriteInput(PTY, PTY->LineData, PTY->LineLength);
394                                 PTY->LineLength = 0;
395                         }
396                         PTY->LineData[PTY->LineLength++] = Input[0];
397                         break;
398                 }
399         }
400         else
401         {
402                 ret = PTY_int_WriteInput(PTY, Input, Length);
403         }
404         
405         // Echo if requested
406         if( PTY->Mode.InputMode & PTYIMODE_ECHO )
407         {
408                 PTY_WriteClient(&PTY->ClientNode, 0, print, Input, 0);
409         }
410         
411         return ret;
412 }
413
414 size_t PTY_SendInput(tPTY *PTY, const char *Input, size_t Length)
415 {
416         size_t ret = 0;
417         while( ret < Length && !PTY->ClientNode.BufferFull )
418         {
419                 // TODO: Detect blocking?
420                 ret += PTY_int_SendInput(PTY, Input + ret, Length - ret);
421         }
422         return ret;
423 }
424
425 // --- VFS ---
426 int PTY_ReadDir(tVFS_Node *Node, int Pos, char Name[FILENAME_MAX])
427 {
428         tPTY    *pty = NULL;
429         if( Pos < giPTY_NumCount * 2 )
430         {
431                 RWLock_AcquireRead(&glPTY_NumPTYs);
432                 for( pty = gpPTY_FirstNumPTY; pty; pty = pty->Next )
433                 {
434                         if( Pos < 2 )
435                                 break;
436                         Pos -= 2;
437                 }
438                 RWLock_Release(&glPTY_NumPTYs);
439         }
440         else if( Pos < (giPTY_NumCount + giPTY_NamedCount) * 2 )
441         {
442                 RWLock_AcquireRead(&glPTY_NamedPTYs);
443                 for( pty = gpPTY_FirstNamedPTY; pty; pty = pty->Next )
444                 {
445                         if( Pos < 2 )
446                                 break;
447                         Pos -= 2;
448                 }
449                 RWLock_Release(&glPTY_NamedPTYs);
450         }
451         
452
453         if( !pty )
454                 return -1;
455         
456         if( pty->Name[0] )
457                 snprintf(Name, FILENAME_MAX, "%s%c", pty->Name, (Pos == 0 ? 'c' : 's'));
458         else
459                 snprintf(Name, FILENAME_MAX, "%i%c", pty->NumericName, (Pos == 0 ? 'c' : 's'));
460         return 0;
461 }
462
463 tVFS_Node *PTY_FindDir(tVFS_Node *Node, const char *Name, Uint Flags)
464 {
465         char    *end;
466          int    num = strtol(Name, &end, 10);
467         
468         if( Name[0] == '\0' || Name[1] == '\0' )
469                 return NULL;
470         
471         size_t  len = strlen(Name);
472         if( Name[len-1] != 'c' && Name[len-1] != 's' )
473                 return NULL;
474          int    isServer = (Name[len-1] != 'c');
475         
476         tPTY    *ret = NULL;
477         if( num && (end[0] == 'c' || end[0] == 's') && end[1] == '\0' )
478         {
479                 // Numeric name
480                 RWLock_AcquireRead(&glPTY_NumPTYs);
481                 for( tPTY *pty = gpPTY_FirstNumPTY; pty; pty = pty->Next )
482                 {
483                         if( pty->NumericName > num )
484                                 break;
485                         if( pty->NumericName == num ) {
486                                 ret = pty;
487                                 break;
488                         }
489                 }
490                 RWLock_Release(&glPTY_NumPTYs);
491         }
492         else
493         {
494                 // String name
495                 RWLock_AcquireRead(&glPTY_NamedPTYs);
496                 for( tPTY *pty = gpPTY_FirstNamedPTY; pty; pty = pty->Next )
497                 {
498                         int cmp = strncmp(pty->Name, Name, len-1);
499                         if(cmp > 0)
500                                 break;
501                         if(cmp == 0 && pty->Name[len-1] == '\0' ) {
502                                 ret = pty;
503                                 break;
504                         }
505                 }
506                 RWLock_Release(&glPTY_NamedPTYs);
507         }
508         if( ret )
509                 return (isServer ? &ret->ServerNode : &ret->ClientNode);
510         else
511                 return NULL;
512 }
513
514 tVFS_Node *PTY_MkNod(tVFS_Node *Node, const char *Name, Uint Mode)
515 {
516         // zero-length name means a numbered pty has been requested
517         if( Name[0] == '\0' || (Name[0] == '#' && Name[1] == '\0') )
518         {
519                 tPTY    *ret = PTY_Create(NULL, NULL, NULL, NULL, NULL);
520                 if( !ret )
521                         return NULL;
522                 return &ret->ServerNode;
523         }
524         
525         // Otherwise return a named PTY
526         // TODO: Should the request be for '<name>s' or just '<name>'   
527
528         tPTY    *ret = PTY_Create(Name, NULL, NULL, NULL, NULL);
529         if(!ret)
530                 return NULL;
531         return &ret->ServerNode;
532 }
533
534 //\! Read from the client's input
535 size_t PTY_ReadClient(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer, Uint Flags)
536 {
537         tPTY *pty = Node->ImplPtr;
538         
539         // Read from flushed queue
540         tTime   timeout_z = 0, *timeout = (Flags & VFS_IOFLAG_NOBLOCK) ? &timeout_z : NULL;
541          int    rv;
542 _select:
543         // If server has disconnected, return EIO
544         if( pty->ServerNode.ReferenceCount == 0 ) {
545                 //Threads_PostSignal(SIGPIPE);
546                 errno = EIO;
547                 return -1;
548         }
549         // Wait for data to be ready
550         rv = VFS_SelectNode(Node, VFS_SELECT_READ, timeout, "PTY_ReadClient");
551         if(!rv) {
552                 errno = (timeout ? EWOULDBLOCK : EINTR);
553                 return -1;
554         }
555
556         Mutex_Acquire(&pty->InputMutex);
557         Length = _rb_read(pty->InputData, INPUT_RINGBUFFER_LEN, &pty->InputReadPos, &pty->InputWritePos,
558                 Buffer, Length);
559         Mutex_Release(&pty->InputMutex);
560
561         if(pty->InputReadPos == pty->InputWritePos)
562                 VFS_MarkAvaliable(Node, 0);
563
564         if(Length == 0 && !pty->HasHitEOF) {
565                 goto _select;
566         }
567         pty->HasHitEOF = 0;
568
569         return Length;
570 }
571
572 //\! Write to the client's output
573 size_t PTY_WriteClient(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer, Uint Flags)
574 {
575         tPTY *pty = Node->ImplPtr;
576
577         // If the server has terminated, send SIGPIPE
578         if( pty->ServerNode.ReferenceCount == 0 )
579         {
580                 //Threads_PostSignal(SIGPIPE);
581                 errno = EIO;
582                 return -1;
583         }       
584
585         // Write to either FIFO or directly to output function
586         if( pty->OutputFcn )
587         {
588                 pty->OutputFcn(pty->OutputHandle, Length, Buffer);
589         }
590         else
591         {
592                 // Write to output ringbuffer
593                 Length = _rb_write(pty->OutputData, OUTPUT_RINGBUFFER_LEN,
594                         &pty->OutputReadPos, &pty->OutputWritePos,
595                         Buffer, Length);
596                 VFS_MarkAvaliable(&pty->ServerNode, 1);
597         }
598         
599         return Length;
600 }
601
602 int PTY_IOCtlClient(tVFS_Node *Node, int ID, void *Data)
603 {
604         tPTY    *pty = Node->ImplPtr;
605         struct ptymode  *mode = Data;
606         struct ptydims  *dims = Data;
607         switch(ID)
608         {
609         case DRV_IOCTL_TYPE:    return DRV_TYPE_TERMINAL;
610         case DRV_IOCTL_IDENT:   memcpy(Data, "PTY\0", 4);       return 0;
611         case DRV_IOCTL_VERSION: return 0x100;
612         case DRV_IOCTL_LOOKUP:  return 0;
613         
614         case PTY_IOCTL_GETMODE:
615                 if( !CheckMem(Data, sizeof(*mode)) ) { errno = EINVAL; return -1; }
616                 *mode = pty->Mode;
617                 return 0;
618         case PTY_IOCTL_SETMODE:
619                 if( !CheckMem(Data, sizeof(*mode)) ) { errno = EINVAL; return -1; }
620                 return PTY_SetAttrib(pty, NULL, mode, 1);
621         case PTY_IOCTL_GETDIMS:
622                 if( !CheckMem(Data, sizeof(*dims)) ) { errno = EINVAL; return -1; }
623                 *dims = pty->Dims;
624                 return 0;
625         case PTY_IOCTL_SETDIMS:
626                 if( !CheckMem(Data, sizeof(*dims)) ) { errno = EINVAL; return -1; }
627                 return PTY_SetAttrib(pty, dims, NULL, 1);
628         }
629         errno = ENOSYS;
630         return -1;
631 }
632
633 void PTY_ReferenceClient(tVFS_Node *Node)
634 {
635         Node->ReferenceCount ++;
636         // TODO: Add PID to list of client PIDs
637 //      Log_Notice("PTY", "ReferenceClient: TODO - List of client PIDs");
638 }
639
640 void PTY_CloseClient(tVFS_Node *Node)
641 {
642         tPTY    *pty = Node->ImplPtr;
643         Node->ReferenceCount --;
644
645         // Remove PID from list
646         // TODO: Maintain list of client processes
647
648         // Free structure if this was the last open handle
649         if( Node->ReferenceCount == 0 && pty->ServerNode.ReferenceCount == 0 )
650         {
651                 // Free the structure! (Should be off the PTY list now)
652                 free(pty);
653         }
654 }
655
656 //\! Read from the client's output
657 size_t PTY_ReadServer(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer, Uint Flags)
658 {
659         tPTY *pty = Node->ImplPtr;
660
661         // TODO: Prevent two servers fighting over client's output      
662         if( pty->OutputFcn )
663         {
664                 // Kernel-land PTYs can't be read from userland
665                 return 0;
666         }
667         
668         // Read back from fifo
669         tTime   timeout_z = 0, *timeout = (Flags & VFS_IOFLAG_NOBLOCK) ? &timeout_z : NULL;
670         int rv = VFS_SelectNode(Node, VFS_SELECT_READ, timeout, "PTY_ReadServer");
671         if(!rv) {
672                 errno = (timeout ? EWOULDBLOCK : EINTR);
673                 return -1;
674         }
675         
676         Length = _rb_read(pty->OutputData, OUTPUT_RINGBUFFER_LEN,
677                 &pty->OutputReadPos, &pty->OutputWritePos,
678                 Buffer, Length);
679         if( pty->OutputReadPos == pty->OutputWritePos )
680                 VFS_MarkAvaliable(Node, 0);
681         VFS_MarkFull(&pty->ClientNode, 0);
682         
683         return Length;
684 }
685
686 //\! Write to the client's input
687 size_t PTY_WriteServer(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer, Uint Flags)
688 {
689         return PTY_SendInput(Node->ImplPtr, Buffer, Length);
690 }
691
692 int PTY_IOCtlServer(tVFS_Node *Node, int ID, void *Data)
693 {
694         tPTY    *pty = Node->ImplPtr;
695         struct ptymode  *mode = Data;
696         struct ptydims  *dims = Data;
697         switch(ID)
698         {
699         case DRV_IOCTL_TYPE:    return DRV_TYPE_TERMINAL;
700         case DRV_IOCTL_IDENT:   memcpy(Data, "PTY\0", 4);       return 0;
701         case DRV_IOCTL_VERSION: return 0x100;
702         case DRV_IOCTL_LOOKUP:  return 0;
703         
704         case PTY_IOCTL_GETMODE:
705                 if( !CheckMem(Data, sizeof(*mode)) ) { errno = EINVAL; return -1; }
706                 *mode = pty->Mode;
707                 // ACK client's SETMODE
708                 return 0;
709         case PTY_IOCTL_SETMODE:
710                 if( !CheckMem(Data, sizeof(*mode)) ) { errno = EINVAL; return -1; }
711                 PTY_SetAttrib(pty, NULL, mode, 0);
712                 return 0;
713         case PTY_IOCTL_GETDIMS:
714                 if( !CheckMem(Data, sizeof(*dims)) ) { errno = EINVAL; return -1; }
715                 *dims = pty->Dims;
716                 return 0;
717         case PTY_IOCTL_SETDIMS:
718                 if( !CheckMem(Data, sizeof(*dims)) ) { errno = EINVAL; return -1; }
719                 PTY_SetAttrib(pty, dims, NULL, 0);
720                 break;
721         case PTY_IOCTL_GETID:
722                 return pty->NumericName;
723         }
724         errno = ENOSYS;
725         return -1;
726 }
727
728 void PTY_CloseServer(tVFS_Node *Node)
729 {
730         tPTY    *pty = Node->ImplPtr;
731         // Dereference node
732         Node->ReferenceCount --;
733         // If reference count == 0, remove from main list
734         if( Node->ReferenceCount > 0 )
735                 return ;
736         
737         // Locate on list and remove
738         tPTY    **prev_np;
739         if( pty->Name[0] ) {
740                 RWLock_AcquireWrite(&glPTY_NamedPTYs);
741                 prev_np = &gpPTY_FirstNamedPTY;
742         }
743         else {
744                 RWLock_AcquireWrite(&glPTY_NumPTYs);
745                 prev_np = &gpPTY_FirstNumPTY;
746         }
747
748         // Search list until *prev_np is equal to pty   
749         for( tPTY *tmp = *prev_np; *prev_np != pty && tmp; prev_np = &tmp->Next, tmp = tmp->Next )
750                 ;
751         
752         // Remove
753         if( *prev_np != pty ) {
754                 Log_Error("PTY", "PTY %p(%i/%s) not on list at deletion time", pty, pty->NumericName, pty->Name);
755         }
756         else {
757                 *prev_np = pty->Next;
758         }
759         
760         // Clean up lock
761         if( pty->Name[0] ) {
762                 RWLock_Release(&glPTY_NamedPTYs);
763                 giPTY_NamedCount --;
764         }
765         else {
766                 RWLock_Release(&glPTY_NumPTYs);
767                 giPTY_NumCount --;
768         }
769
770         // If there are no open children, we can safely free this PTY
771         if( pty->ClientNode.ReferenceCount == 0 )
772                 free(pty);
773 }
774

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