Kernel/VTerm,PTY - Replaced VTerm buffers with PTY hooks
[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                 return (isServer ? &ret->ServerNode : &ret->ClientNode);
524         else
525                 return NULL;
526 }
527
528 tVFS_Node *PTY_MkNod(tVFS_Node *Node, const char *Name, Uint Mode)
529 {
530         // zero-length name means a numbered pty has been requested
531         if( Name[0] == '\0' || (Name[0] == '#' && Name[1] == '\0') )
532         {
533                 tPTY    *ret = PTY_Create(NULL, NULL, NULL, NULL, NULL);
534                 if( !ret )
535                         return NULL;
536                 return &ret->ServerNode;
537         }
538         
539         // Otherwise return a named PTY
540         // TODO: Should the request be for '<name>s' or just '<name>'   
541
542         tPTY    *ret = PTY_Create(Name, NULL, NULL, NULL, NULL);
543         if(!ret)
544                 return NULL;
545         return &ret->ServerNode;
546 }
547
548 //\! Read from the client's input
549 size_t PTY_ReadClient(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer, Uint Flags)
550 {
551         tPTY *pty = Node->ImplPtr;
552         
553         // Read from flushed queue
554         tTime   timeout_z = 0, *timeout = (Flags & VFS_IOFLAG_NOBLOCK) ? &timeout_z : NULL;
555          int    rv;
556 _select:
557         // If server has disconnected, return EIO
558         if( pty->ServerNode.ReferenceCount == 0 ) {
559                 //Threads_PostSignal(SIGPIPE);
560                 errno = EIO;
561                 return -1;
562         }
563         // Wait for data to be ready
564         rv = VFS_SelectNode(Node, VFS_SELECT_READ, timeout, "PTY_ReadClient");
565         if(!rv) {
566                 errno = (timeout ? EWOULDBLOCK : EINTR);
567                 return -1;
568         }
569
570         Mutex_Acquire(&pty->InputMutex);
571         Length = _rb_read(pty->InputData, INPUT_RINGBUFFER_LEN, &pty->InputReadPos, &pty->InputWritePos,
572                 Buffer, Length);
573         Mutex_Release(&pty->InputMutex);
574
575         if(pty->InputReadPos == pty->InputWritePos)
576                 VFS_MarkAvaliable(Node, 0);
577
578         if(Length == 0 && !pty->HasHitEOF) {
579                 goto _select;
580         }
581         pty->HasHitEOF = 0;
582
583         return Length;
584 }
585
586 //\! Write to the client's output
587 size_t PTY_WriteClient(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer, Uint Flags)
588 {
589         tPTY *pty = Node->ImplPtr;
590
591         // If the server has terminated, send SIGPIPE
592         if( pty->ServerNode.ReferenceCount == 0 )
593         {
594                 //Threads_PostSignal(SIGPIPE);
595                 errno = EIO;
596                 return -1;
597         }       
598
599         // Write to either FIFO or directly to output function
600         if( pty->OutputFcn )
601         {
602                 pty->OutputFcn(pty->OutputHandle, Length, Buffer);
603         }
604         else
605         {
606                 // Write to output ringbuffer
607                 Length = _rb_write(pty->OutputData, OUTPUT_RINGBUFFER_LEN,
608                         &pty->OutputReadPos, &pty->OutputWritePos,
609                         Buffer, Length);
610                 VFS_MarkAvaliable(&pty->ServerNode, 1);
611         }
612         
613         return Length;
614 }
615
616 int PTY_IOCtlClient(tVFS_Node *Node, int ID, void *Data)
617 {
618         tPTY    *pty = Node->ImplPtr;
619         struct ptymode  *mode = Data;
620         struct ptydims  *dims = Data;
621         switch(ID)
622         {
623         case DRV_IOCTL_TYPE:    return DRV_TYPE_TERMINAL;
624         case DRV_IOCTL_IDENT:   memcpy(Data, "PTY\0", 4);       return 0;
625         case DRV_IOCTL_VERSION: return 0x100;
626         case DRV_IOCTL_LOOKUP:  return 0;
627         
628         case PTY_IOCTL_GETMODE:
629                 if( !CheckMem(Data, sizeof(*mode)) ) { errno = EINVAL; return -1; }
630                 *mode = pty->Mode;
631                 return 0;
632         case PTY_IOCTL_SETMODE:
633                 if( !CheckMem(Data, sizeof(*mode)) ) { errno = EINVAL; return -1; }
634                 return PTY_SetAttrib(pty, NULL, mode, 1);
635         case PTY_IOCTL_GETDIMS:
636                 if( !CheckMem(Data, sizeof(*dims)) ) { errno = EINVAL; return -1; }
637                 *dims = pty->Dims;
638                 return 0;
639         case PTY_IOCTL_SETDIMS:
640                 if( !CheckMem(Data, sizeof(*dims)) ) { errno = EINVAL; return -1; }
641                 return PTY_SetAttrib(pty, dims, NULL, 1);
642         }
643         errno = ENOSYS;
644         return -1;
645 }
646
647 void PTY_ReferenceClient(tVFS_Node *Node)
648 {
649         Node->ReferenceCount ++;
650         // TODO: Add PID to list of client PIDs
651 //      Log_Notice("PTY", "ReferenceClient: TODO - List of client PIDs");
652 }
653
654 void PTY_CloseClient(tVFS_Node *Node)
655 {
656         tPTY    *pty = Node->ImplPtr;
657         Node->ReferenceCount --;
658
659         // Remove PID from list
660         // TODO: Maintain list of client processes
661
662         // Free structure if this was the last open handle
663         if( Node->ReferenceCount == 0 && pty->ServerNode.ReferenceCount == 0 )
664         {
665                 // Free the structure! (Should be off the PTY list now)
666                 free(pty);
667         }
668 }
669
670 //\! Read from the client's output
671 size_t PTY_ReadServer(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer, Uint Flags)
672 {
673         tPTY *pty = Node->ImplPtr;
674
675         // TODO: Prevent two servers fighting over client's output      
676         if( pty->OutputFcn )
677         {
678                 // Kernel-land PTYs can't be read from userland
679                 return 0;
680         }
681         
682         // Read back from fifo
683         tTime   timeout_z = 0, *timeout = (Flags & VFS_IOFLAG_NOBLOCK) ? &timeout_z : NULL;
684         int rv = VFS_SelectNode(Node, VFS_SELECT_READ, timeout, "PTY_ReadServer");
685         if(!rv) {
686                 errno = (timeout ? EWOULDBLOCK : EINTR);
687                 return -1;
688         }
689         
690         Length = _rb_read(pty->OutputData, OUTPUT_RINGBUFFER_LEN,
691                 &pty->OutputReadPos, &pty->OutputWritePos,
692                 Buffer, Length);
693         if( pty->OutputReadPos == pty->OutputWritePos )
694                 VFS_MarkAvaliable(Node, 0);
695         VFS_MarkFull(&pty->ClientNode, 0);
696         
697         return Length;
698 }
699
700 //\! Write to the client's input
701 size_t PTY_WriteServer(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer, Uint Flags)
702 {
703         return PTY_SendInput(Node->ImplPtr, Buffer, Length);
704 }
705
706 int PTY_IOCtlServer(tVFS_Node *Node, int ID, void *Data)
707 {
708         tPTY    *pty = Node->ImplPtr;
709         struct ptymode  *mode = Data;
710         struct ptydims  *dims = Data;
711         switch(ID)
712         {
713         case DRV_IOCTL_TYPE:    return DRV_TYPE_TERMINAL;
714         case DRV_IOCTL_IDENT:   memcpy(Data, "PTY\0", 4);       return 0;
715         case DRV_IOCTL_VERSION: return 0x100;
716         case DRV_IOCTL_LOOKUP:  return 0;
717         
718         case PTY_IOCTL_GETMODE:
719                 if( !CheckMem(Data, sizeof(*mode)) ) { errno = EINVAL; return -1; }
720                 *mode = pty->Mode;
721                 // ACK client's SETMODE
722                 return 0;
723         case PTY_IOCTL_SETMODE:
724                 if( !CheckMem(Data, sizeof(*mode)) ) { errno = EINVAL; return -1; }
725                 PTY_SetAttrib(pty, NULL, mode, 0);
726                 return 0;
727         case PTY_IOCTL_GETDIMS:
728                 if( !CheckMem(Data, sizeof(*dims)) ) { errno = EINVAL; return -1; }
729                 *dims = pty->Dims;
730                 return 0;
731         case PTY_IOCTL_SETDIMS:
732                 if( !CheckMem(Data, sizeof(*dims)) ) { errno = EINVAL; return -1; }
733                 PTY_SetAttrib(pty, dims, NULL, 0);
734                 break;
735         case PTY_IOCTL_GETID:
736                 return pty->NumericName;
737         }
738         errno = ENOSYS;
739         return -1;
740 }
741
742 void PTY_CloseServer(tVFS_Node *Node)
743 {
744         tPTY    *pty = Node->ImplPtr;
745         // Dereference node
746         Node->ReferenceCount --;
747         // If reference count == 0, remove from main list
748         if( Node->ReferenceCount > 0 )
749                 return ;
750         
751         // Locate on list and remove
752         tPTY    **prev_np;
753         if( pty->Name[0] ) {
754                 RWLock_AcquireWrite(&glPTY_NamedPTYs);
755                 prev_np = &gpPTY_FirstNamedPTY;
756         }
757         else {
758                 RWLock_AcquireWrite(&glPTY_NumPTYs);
759                 prev_np = &gpPTY_FirstNumPTY;
760         }
761
762         // Search list until *prev_np is equal to pty   
763         for( tPTY *tmp = *prev_np; *prev_np != pty && tmp; prev_np = &tmp->Next, tmp = tmp->Next )
764                 ;
765         
766         // Remove
767         if( *prev_np != pty ) {
768                 Log_Error("PTY", "PTY %p(%i/%s) not on list at deletion time", pty, pty->NumericName, pty->Name);
769         }
770         else {
771                 *prev_np = pty->Next;
772         }
773         
774         // Clean up lock
775         if( pty->Name[0] ) {
776                 RWLock_Release(&glPTY_NamedPTYs);
777                 giPTY_NamedCount --;
778         }
779         else {
780                 RWLock_Release(&glPTY_NumPTYs);
781                 giPTY_NumCount --;
782         }
783
784         // If there are no open children, we can safely free this PTY
785         if( pty->ClientNode.ReferenceCount == 0 )
786                 free(pty);
787 }
788

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