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

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