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

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