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

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