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

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