Kernel - Misc fixes and debug in VTerm/PTY, AxWin3 starts again
[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   1
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                 if( WasClient )
234                 {
235                         if( PTY->ModeSet && PTY->ModeSet(PTY->OutputHandle, Mode) )
236                         {
237                                 errno = EINVAL;
238                                 return -1;
239                         }
240                         else if( !PTY->OutputFcn )
241                         {
242                                 Log_Warning("PTY", "TODO: Need to stop client output until modeset has been ACKed");
243                                 // Block write until acked
244                                 // ACK by server doing GETMODE
245                         }
246                 }
247                 else
248                 {
249                         // Should the client be informed that the server just twiddled the modes?
250                 }
251                 LOG("PTY %p mode set to {0%o, 0%o}", PTY, Mode->InputMode, Mode->OutputMode);
252                 PTY->Mode = *Mode;
253         }
254         if( Dims )
255         {
256                 if( WasClient )
257                 {
258                         // Poke the server?
259                         if( PTY->ReqResize && PTY->ReqResize(PTY->OutputHandle, Dims) )
260                         {
261                                 errno = EINVAL;
262                                 return -1;
263                         }
264                         else if( !PTY->OutputFcn )
265                         {
266                                 // Inform server process... somehow
267                         }
268                 }
269                 else
270                 {
271                         // SIGWINSZ to client
272                 }
273                 LOG("PTY %p dims set to %ix%i", PTY, Dims->W, Dims->H);
274                 PTY->Dims = *Dims;
275         }
276         return 0;
277 }
278
279 void PTY_Close(tPTY *PTY)
280 {
281         
282 }
283
284 size_t _rb_write(void *buf, size_t buflen, int *rd, int *wr, const void *data, size_t len)
285 {
286         size_t space = (*rd - *wr + buflen - 1) % buflen;
287         ENTER("pbuf ibuflen prd pwr pdata ilen", buf, buflen, rd, wr, data, len);
288         len = MIN(space, len);
289         LOG("space = %i, *rd = %i, *wr = %i", space, *rd, *wr);
290         if(*wr + len >= buflen) {
291                 size_t prelen = buflen - *wr;
292                 memcpy((char*)buf + *wr, data, prelen);
293                 memcpy(buf, (char*)data + prelen, len - prelen);
294                 *wr = len - prelen;
295         }
296         else {
297                 memcpy((char*)buf + *wr, data, len);
298                 *wr += len;
299         }
300         LEAVE('i', len);
301         return len;
302 }
303 size_t _rb_read(void *buf, size_t buflen, int *rd, int *wr, void *data, size_t len)
304 {
305         size_t space = (*wr - *rd + buflen) % buflen;
306         len = MIN(space, len);
307         if(*rd + len >= buflen) {
308                 size_t prelen = buflen - *rd;
309                 memcpy(data, (char*)buf + *rd, prelen);
310                 memcpy((char*)data + prelen, buf, len - prelen);
311                 *rd = len - prelen;
312         }
313         else {
314                 memcpy(data, (char*)buf + *rd, len);
315                 *rd += len;
316         }
317         return len;
318 }
319
320 size_t PTY_int_WriteInput(tPTY *PTY, const char *Input, size_t Length)
321 {
322         size_t  ret;
323
324         Mutex_Acquire(&PTY->InputMutex);        
325
326         ret = _rb_write(PTY->InputData, INPUT_RINGBUFFER_LEN, &PTY->InputReadPos, &PTY->InputWritePos,
327                 Input, Length);
328         
329         Mutex_Release(&PTY->InputMutex);
330
331         VFS_MarkAvaliable(&PTY->ClientNode, 1);
332         if(ret < Length)
333                 VFS_MarkFull(&PTY->ServerNode, 1);      
334
335         return ret;
336 }
337
338 size_t PTY_int_SendInput(tPTY *PTY, const char *Input, size_t Length)
339 {
340         size_t  ret = 1, print = 1;
341         
342         // Input mode stuff only counts for text output mode
343         // - Any other is Uint32 keypresses
344         if( (PTY->Mode.OutputMode & PTYOMODE_BUFFMT) != PTYBUFFMT_TEXT )
345                 return PTY_int_WriteInput(PTY, Input, Length);
346         // If in raw mode, flush directlr
347         if( (PTY->Mode.InputMode & PTYIMODE_RAW) )
348                 return PTY_int_WriteInput(PTY, Input, Length);
349         
350         if( PTY->Mode.InputMode & PTYIMODE_CANON )
351         {
352                 const char      char_bs = '\b';
353                 switch(Input[0])
354                 {
355                 case 3: // INTR - ^C
356                         // TODO: Send SIGINT
357                         // Threads_PostSignalExt(PTY->ClientThreads, SIGINT);
358                         print = 0;
359                         break;
360                 case 4: // EOF - ^D
361                         PTY_int_WriteInput(PTY, PTY->LineData, PTY->LineLength);
362                         PTY->HasHitEOF = (PTY->LineLength == 0);
363                         PTY->LineLength = 0;
364                         print = 0;
365                         break;
366                 case 8: // Backspace
367                         if(PTY->LineLength != 0)
368                                 PTY->LineLength --;
369                         break;
370                 case 'w'-'a':   // Word erase
371                         while(PTY->LineLength != 0 && isalnum(PTY->LineData[--PTY->LineLength]))
372                                 PTY_WriteClient(&PTY->ClientNode, 0, 1, &char_bs, 0);
373                         print = 0;
374                         break;
375                 case 'u'-'a':   // Kill
376                         while(PTY->LineLength > 0)
377                                 PTY_WriteClient(&PTY->ClientNode, 0, 1, &char_bs, 0);
378                         print = 0;
379                         break;
380                 case 'v'-'a':
381                         Input ++;
382                         Length --;
383                         ret ++;
384                         goto _default;
385                 case '\0':
386                 case '\n':
387                         if(PTY->LineLength == INPUT_LINE_LEN) {
388                                 PTY_int_WriteInput(PTY, PTY->LineData, PTY->LineLength);
389                                 PTY->LineLength = 0;
390                         }
391                         PTY->LineData[PTY->LineLength++] = '\n';
392                         PTY_int_WriteInput(PTY, PTY->LineData, PTY->LineLength);
393                         PTY->LineLength = 0;
394                         break;
395                 // TODO: Handle ^[[D and ^[[C for in-line editing, also ^[[1~/^[[4~ (home/end)
396                 //case 0x1B:
397                 //      break;
398                 default:
399                 _default:
400                         if(PTY->LineLength == INPUT_LINE_LEN) {
401                                 PTY_int_WriteInput(PTY, PTY->LineData, PTY->LineLength);
402                                 PTY->LineLength = 0;
403                         }
404                         PTY->LineData[PTY->LineLength++] = Input[0];
405                         break;
406                 }
407         }
408         else
409         {
410                 ret = PTY_int_WriteInput(PTY, Input, Length);
411         }
412         
413         // Echo if requested
414         if( PTY->Mode.InputMode & PTYIMODE_ECHO )
415         {
416                 PTY_WriteClient(&PTY->ClientNode, 0, print, Input, 0);
417         }
418         
419         return ret;
420 }
421
422 size_t PTY_SendInput(tPTY *PTY, const char *Input, size_t Length)
423 {
424         size_t ret = 0;
425         while( ret < Length && !PTY->ClientNode.BufferFull )
426         {
427                 // TODO: Detect blocking?
428                 ret += PTY_int_SendInput(PTY, Input + ret, Length - ret);
429         }
430         return ret;
431 }
432
433 // --- VFS ---
434 int PTY_ReadDir(tVFS_Node *Node, int Pos, char Name[FILENAME_MAX])
435 {
436         tPTY    *pty = NULL;
437         if( Pos < giPTY_NumCount * 2 )
438         {
439                 RWLock_AcquireRead(&glPTY_NumPTYs);
440                 for( pty = gpPTY_FirstNumPTY; pty; pty = pty->Next )
441                 {
442                         if( Pos < 2 )
443                                 break;
444                         Pos -= 2;
445                 }
446                 RWLock_Release(&glPTY_NumPTYs);
447         }
448         else if( Pos < (giPTY_NumCount + giPTY_NamedCount) * 2 )
449         {
450                 RWLock_AcquireRead(&glPTY_NamedPTYs);
451                 for( pty = gpPTY_FirstNamedPTY; pty; pty = pty->Next )
452                 {
453                         if( Pos < 2 )
454                                 break;
455                         Pos -= 2;
456                 }
457                 RWLock_Release(&glPTY_NamedPTYs);
458         }
459         
460
461         if( !pty )
462                 return -1;
463         
464         if( pty->Name[0] )
465                 snprintf(Name, FILENAME_MAX, "%s%c", pty->Name, (Pos == 0 ? 'c' : 's'));
466         else
467                 snprintf(Name, FILENAME_MAX, "%i%c", pty->NumericName, (Pos == 0 ? 'c' : 's'));
468         return 0;
469 }
470
471 tVFS_Node *PTY_FindDir(tVFS_Node *Node, const char *Name, Uint Flags)
472 {
473         char    *end;
474          int    num = strtol(Name, &end, 10);
475         
476         if( Name[0] == '\0' || Name[1] == '\0' )
477                 return NULL;
478         
479         size_t  len = strlen(Name);
480         if( Name[len-1] != 'c' && Name[len-1] != 's' )
481                 return NULL;
482          int    isServer = (Name[len-1] != 'c');
483         
484         tPTY    *ret = NULL;
485         if( num && (end[0] == 'c' || end[0] == 's') && end[1] == '\0' )
486         {
487                 // Numeric name
488                 RWLock_AcquireRead(&glPTY_NumPTYs);
489                 for( tPTY *pty = gpPTY_FirstNumPTY; pty; pty = pty->Next )
490                 {
491                         if( pty->NumericName > num )
492                                 break;
493                         if( pty->NumericName == num ) {
494                                 ret = pty;
495                                 break;
496                         }
497                 }
498                 RWLock_Release(&glPTY_NumPTYs);
499         }
500         else
501         {
502                 // String name
503                 RWLock_AcquireRead(&glPTY_NamedPTYs);
504                 for( tPTY *pty = gpPTY_FirstNamedPTY; pty; pty = pty->Next )
505                 {
506                         int cmp = strncmp(pty->Name, Name, len-1);
507                         if(cmp > 0)
508                                 break;
509                         if(cmp == 0 && pty->Name[len-1] == '\0' ) {
510                                 ret = pty;
511                                 break;
512                         }
513                 }
514                 RWLock_Release(&glPTY_NamedPTYs);
515         }
516         if( ret )
517                 return (isServer ? &ret->ServerNode : &ret->ClientNode);
518         else
519                 return NULL;
520 }
521
522 tVFS_Node *PTY_MkNod(tVFS_Node *Node, const char *Name, Uint Mode)
523 {
524         // zero-length name means a numbered pty has been requested
525         if( Name[0] == '\0' || (Name[0] == '#' && Name[1] == '\0') )
526         {
527                 tPTY    *ret = PTY_Create(NULL, NULL, NULL, NULL, NULL);
528                 if( !ret )
529                         return NULL;
530                 return &ret->ServerNode;
531         }
532         
533         // Otherwise return a named PTY
534         // TODO: Should the request be for '<name>s' or just '<name>'   
535
536         tPTY    *ret = PTY_Create(Name, NULL, NULL, NULL, NULL);
537         if(!ret)
538                 return NULL;
539         return &ret->ServerNode;
540 }
541
542 //\! Read from the client's input
543 size_t PTY_ReadClient(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer, Uint Flags)
544 {
545         tPTY *pty = Node->ImplPtr;
546         
547         // Read from flushed queue
548         tTime   timeout_z = 0, *timeout = (Flags & VFS_IOFLAG_NOBLOCK) ? &timeout_z : NULL;
549          int    rv;
550 _select:
551         // If server has disconnected, return EIO
552         if( pty->ServerNode.ReferenceCount == 0 ) {
553                 //Threads_PostSignal(SIGPIPE);
554                 errno = EIO;
555                 return -1;
556         }
557         // Wait for data to be ready
558         rv = VFS_SelectNode(Node, VFS_SELECT_READ, timeout, "PTY_ReadClient");
559         if(!rv) {
560                 errno = (timeout ? EWOULDBLOCK : EINTR);
561                 return -1;
562         }
563
564         Mutex_Acquire(&pty->InputMutex);
565         Length = _rb_read(pty->InputData, INPUT_RINGBUFFER_LEN, &pty->InputReadPos, &pty->InputWritePos,
566                 Buffer, Length);
567         Mutex_Release(&pty->InputMutex);
568
569         if(pty->InputReadPos == pty->InputWritePos)
570                 VFS_MarkAvaliable(Node, 0);
571
572         if(Length == 0 && !pty->HasHitEOF) {
573                 goto _select;
574         }
575         pty->HasHitEOF = 0;
576
577         return Length;
578 }
579
580 //\! Write to the client's output
581 size_t PTY_WriteClient(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer, Uint Flags)
582 {
583         tPTY *pty = Node->ImplPtr;
584
585         // If the server has terminated, send SIGPIPE
586         if( pty->ServerNode.ReferenceCount == 0 )
587         {
588                 //Threads_PostSignal(SIGPIPE);
589                 errno = EIO;
590                 return -1;
591         }       
592
593         // Write to either FIFO or directly to output function
594         if( pty->OutputFcn )
595         {
596                 pty->OutputFcn(pty->OutputHandle, Length, Buffer);
597         }
598         else
599         {
600                 // Write to output ringbuffer
601                 Length = _rb_write(pty->OutputData, OUTPUT_RINGBUFFER_LEN,
602                         &pty->OutputReadPos, &pty->OutputWritePos,
603                         Buffer, Length);
604                 VFS_MarkAvaliable(&pty->ServerNode, 1);
605         }
606         
607         return Length;
608 }
609
610 int PTY_IOCtlClient(tVFS_Node *Node, int ID, void *Data)
611 {
612         tPTY    *pty = Node->ImplPtr;
613         struct ptymode  *mode = Data;
614         struct ptydims  *dims = Data;
615         switch(ID)
616         {
617         case DRV_IOCTL_TYPE:    return DRV_TYPE_TERMINAL;
618         case DRV_IOCTL_IDENT:   memcpy(Data, "PTY\0", 4);       return 0;
619         case DRV_IOCTL_VERSION: return 0x100;
620         case DRV_IOCTL_LOOKUP:  return 0;
621         
622         case PTY_IOCTL_GETMODE:
623                 if( !CheckMem(Data, sizeof(*mode)) ) { errno = EINVAL; return -1; }
624                 *mode = pty->Mode;
625                 return 0;
626         case PTY_IOCTL_SETMODE:
627                 if( !CheckMem(Data, sizeof(*mode)) ) { errno = EINVAL; return -1; }
628                 return PTY_SetAttrib(pty, NULL, mode, 1);
629         case PTY_IOCTL_GETDIMS:
630                 if( !CheckMem(Data, sizeof(*dims)) ) { errno = EINVAL; return -1; }
631                 *dims = pty->Dims;
632                 return 0;
633         case PTY_IOCTL_SETDIMS:
634                 if( !CheckMem(Data, sizeof(*dims)) ) { errno = EINVAL; return -1; }
635                 return PTY_SetAttrib(pty, dims, NULL, 1);
636         }
637         errno = ENOSYS;
638         return -1;
639 }
640
641 void PTY_ReferenceClient(tVFS_Node *Node)
642 {
643         Node->ReferenceCount ++;
644         // TODO: Add PID to list of client PIDs
645 //      Log_Notice("PTY", "ReferenceClient: TODO - List of client PIDs");
646 }
647
648 void PTY_CloseClient(tVFS_Node *Node)
649 {
650         tPTY    *pty = Node->ImplPtr;
651         Node->ReferenceCount --;
652
653         // Remove PID from list
654         // TODO: Maintain list of client processes
655
656         // Free structure if this was the last open handle
657         if( Node->ReferenceCount == 0 && pty->ServerNode.ReferenceCount == 0 )
658         {
659                 // Free the structure! (Should be off the PTY list now)
660                 free(pty);
661         }
662 }
663
664 //\! Read from the client's output
665 size_t PTY_ReadServer(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer, Uint Flags)
666 {
667         tPTY *pty = Node->ImplPtr;
668
669         // TODO: Prevent two servers fighting over client's output      
670         if( pty->OutputFcn )
671         {
672                 // Kernel-land PTYs can't be read from userland
673                 return 0;
674         }
675         
676         // Read back from fifo
677         tTime   timeout_z = 0, *timeout = (Flags & VFS_IOFLAG_NOBLOCK) ? &timeout_z : NULL;
678         int rv = VFS_SelectNode(Node, VFS_SELECT_READ, timeout, "PTY_ReadServer");
679         if(!rv) {
680                 errno = (timeout ? EWOULDBLOCK : EINTR);
681                 return -1;
682         }
683         
684         Length = _rb_read(pty->OutputData, OUTPUT_RINGBUFFER_LEN,
685                 &pty->OutputReadPos, &pty->OutputWritePos,
686                 Buffer, Length);
687         if( pty->OutputReadPos == pty->OutputWritePos )
688                 VFS_MarkAvaliable(Node, 0);
689         VFS_MarkFull(&pty->ClientNode, 0);
690         
691         return Length;
692 }
693
694 //\! Write to the client's input
695 size_t PTY_WriteServer(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer, Uint Flags)
696 {
697         return PTY_SendInput(Node->ImplPtr, Buffer, Length);
698 }
699
700 int PTY_IOCtlServer(tVFS_Node *Node, int ID, void *Data)
701 {
702         tPTY    *pty = Node->ImplPtr;
703         struct ptymode  *mode = Data;
704         struct ptydims  *dims = Data;
705         switch(ID)
706         {
707         case DRV_IOCTL_TYPE:    return DRV_TYPE_TERMINAL;
708         case DRV_IOCTL_IDENT:   memcpy(Data, "PTY\0", 4);       return 0;
709         case DRV_IOCTL_VERSION: return 0x100;
710         case DRV_IOCTL_LOOKUP:  return 0;
711         
712         case PTY_IOCTL_GETMODE:
713                 if( !CheckMem(Data, sizeof(*mode)) ) { errno = EINVAL; return -1; }
714                 *mode = pty->Mode;
715                 // ACK client's SETMODE
716                 return 0;
717         case PTY_IOCTL_SETMODE:
718                 if( !CheckMem(Data, sizeof(*mode)) ) { errno = EINVAL; return -1; }
719                 PTY_SetAttrib(pty, NULL, mode, 0);
720                 return 0;
721         case PTY_IOCTL_GETDIMS:
722                 if( !CheckMem(Data, sizeof(*dims)) ) { errno = EINVAL; return -1; }
723                 *dims = pty->Dims;
724                 return 0;
725         case PTY_IOCTL_SETDIMS:
726                 if( !CheckMem(Data, sizeof(*dims)) ) { errno = EINVAL; return -1; }
727                 PTY_SetAttrib(pty, dims, NULL, 0);
728                 break;
729         case PTY_IOCTL_GETID:
730                 return pty->NumericName;
731         }
732         errno = ENOSYS;
733         return -1;
734 }
735
736 void PTY_CloseServer(tVFS_Node *Node)
737 {
738         tPTY    *pty = Node->ImplPtr;
739         // Dereference node
740         Node->ReferenceCount --;
741         // If reference count == 0, remove from main list
742         if( Node->ReferenceCount > 0 )
743                 return ;
744         
745         // Locate on list and remove
746         tPTY    **prev_np;
747         if( pty->Name[0] ) {
748                 RWLock_AcquireWrite(&glPTY_NamedPTYs);
749                 prev_np = &gpPTY_FirstNamedPTY;
750         }
751         else {
752                 RWLock_AcquireWrite(&glPTY_NumPTYs);
753                 prev_np = &gpPTY_FirstNumPTY;
754         }
755
756         // Search list until *prev_np is equal to pty   
757         for( tPTY *tmp = *prev_np; *prev_np != pty && tmp; prev_np = &tmp->Next, tmp = tmp->Next )
758                 ;
759         
760         // Remove
761         if( *prev_np != pty ) {
762                 Log_Error("PTY", "PTY %p(%i/%s) not on list at deletion time", pty, pty->NumericName, pty->Name);
763         }
764         else {
765                 *prev_np = pty->Next;
766         }
767         
768         // Clean up lock
769         if( pty->Name[0] ) {
770                 RWLock_Release(&glPTY_NamedPTYs);
771                 giPTY_NamedCount --;
772         }
773         else {
774                 RWLock_Release(&glPTY_NumPTYs);
775                 giPTY_NumCount --;
776         }
777
778         // If there are no open children, we can safely free this PTY
779         if( pty->ClientNode.ReferenceCount == 0 )
780                 free(pty);
781 }
782

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