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

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