Kernel/PTYs - Added support for sending signals to controlling PGID
[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         tTime   timeout_z, *timeout = (Flags & VFS_IOFLAG_NOBLOCK) ? &timeout_z : NULL;
568          int    rv;
569         
570         rv = VFS_SelectNode(Node, VFS_SELECT_WRITE, timeout, "PTY_WriteClient");
571         if(!rv) {
572                 errno = (timeout ? EWOULDBLOCK : EINTR);
573                 return -1;
574         }
575         
576         // Write to output ringbuffer
577         Length = _rb_write(pty->OutputData, OUTPUT_RINGBUFFER_LEN,
578                 &pty->OutputReadPos, &pty->OutputWritePos,
579                 Buffer, Length);
580         VFS_MarkAvaliable(pty->ServerNode, 1);
581         if( (pty->OutputWritePos + 1) % OUTPUT_RINGBUFFER_LEN == pty->OutputReadPos )
582                 VFS_MarkFull(Node, 1);
583         
584         return Length;
585 }
586
587 void PTY_ReferenceClient(tVFS_Node *Node)
588 {
589         Node->ReferenceCount ++;
590         // TODO: Add PID to list of client PIDs
591 //      Log_Notice("PTY", "ReferenceClient: TODO - List of client PIDs");
592 }
593
594 void PTY_CloseClient(tVFS_Node *Node)
595 {
596         tPTY    *pty = Node->ImplPtr;
597         Node->ReferenceCount --;
598
599         // Remove PID from list
600         // TODO: Maintain list of client processes
601
602         // Free structure if this was the last open handle
603         if( Node->ReferenceCount > 0 )
604                 return ;
605         if( pty->ServerNode && pty->ServerNode->ReferenceCount == 0 )
606         {
607                 // Free the structure! (Should be off the PTY list now)
608                 free(pty->ServerNode);
609                 free(pty);
610         }
611 }
612
613 //\! Read from the client's output
614 size_t PTY_ReadServer(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer, Uint Flags)
615 {
616         tPTY *pty = Node->ImplPtr;
617         if( !pty ) {
618                 errno = EIO;
619                 return -1;
620         }
621
622         // TODO: Prevent two servers fighting over client's output      
623         if( pty->OutputFcn )
624         {
625                 // Kernel-land PTYs can't be read from userland
626                 return 0;
627         }
628         
629         // Read back from fifo
630         tTime   timeout_z = 0, *timeout = (Flags & VFS_IOFLAG_NOBLOCK) ? &timeout_z : NULL;
631         int rv = VFS_SelectNode(Node, VFS_SELECT_READ, timeout, "PTY_ReadServer");
632         if(!rv) {
633                 errno = (timeout ? EWOULDBLOCK : EINTR);
634                 return -1;
635         }
636         
637         Length = _rb_read(pty->OutputData, OUTPUT_RINGBUFFER_LEN,
638                 &pty->OutputReadPos, &pty->OutputWritePos,
639                 Buffer, Length);
640         if( pty->OutputReadPos == pty->OutputWritePos )
641                 VFS_MarkAvaliable(Node, 0);
642         VFS_MarkFull(&pty->ClientNode, 0);
643         
644         return Length;
645 }
646
647 //\! Write to the client's input
648 size_t PTY_WriteServer(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer, Uint Flags)
649 {
650         tPTY    *pty = Node->ImplPtr;
651         if( !pty ) {
652                 errno = EIO;
653                 return -1;
654         }
655
656         tTime   timeout_z = 0, *timeout = (Flags & VFS_IOFLAG_NOBLOCK) ? &timeout_z : NULL;
657         int rv = VFS_SelectNode(Node, VFS_SELECT_WRITE, timeout, "PTY_WriteServer");
658         if(!rv) {
659                 errno = (timeout ? EWOULDBLOCK : EINTR);
660                 return -1;
661         }
662         size_t  used = 0;
663         do {
664                 used += PTY_SendInput(Node->ImplPtr, Buffer, Length);
665         } while( used < Length && !(Flags & VFS_IOFLAG_NOBLOCK) );
666         
667         if( (pty->InputWritePos+1)%INPUT_RINGBUFFER_LEN == pty->InputReadPos )
668                 VFS_MarkFull(Node, 1);
669         return used;
670 }
671
672 void PTY_CloseServer(tVFS_Node *Node)
673 {
674         tPTY    *pty = Node->ImplPtr;
675         // Dereference node
676         Node->ReferenceCount --;
677         // If reference count == 0, remove from main list
678         if( Node->ReferenceCount > 0 )
679                 return ;
680         
681         // Locate on list and remove
682         tPTY    **prev_np;
683         if( pty->NumericName == -1 ) {
684                 RWLock_AcquireWrite(&glPTY_NamedPTYs);
685                 prev_np = &gpPTY_FirstNamedPTY;
686         }
687         else {
688                 RWLock_AcquireWrite(&glPTY_NumPTYs);
689                 prev_np = &gpPTY_FirstNumPTY;
690         }
691
692         // Search list until *prev_np is equal to pty   
693         for( tPTY *tmp = *prev_np; *prev_np != pty && tmp; prev_np = &tmp->Next, tmp = tmp->Next )
694                 ;
695         
696         // Remove
697         if( *prev_np != pty ) {
698                 Log_Error("PTY", "PTY %p(%i/%s) not on list at deletion time", pty, pty->NumericName, pty->Name);
699         }
700         else {
701                 *prev_np = pty->Next;
702         }
703         
704         // Clean up lock
705         if( pty->NumericName == -1 ) {
706                 RWLock_Release(&glPTY_NamedPTYs);
707                 giPTY_NamedCount --;
708         }
709         else {
710                 RWLock_Release(&glPTY_NumPTYs);
711                 giPTY_NumCount --;
712         }
713
714         // Send SIGHUP to controling PGID
715         if( pty->ControllingProcGroup > 0 ) {
716                 Threads_SignalGroup(pty->ControllingProcGroup, SIGHUP);
717         }
718
719         // If there are no open children, we can safely free this PTY
720         if( pty->ClientNode.ReferenceCount == 0 ) {
721                 free(Node);
722                 free(pty);
723         }
724 }
725
726 int PTY_IOCtl(tVFS_Node *Node, int ID, void *Data)
727 {
728         tPTY    *pty = Node->ImplPtr;
729         struct ptymode  *mode = Data;
730         struct ptydims  *dims = Data;
731         
732         int     is_server = !pty || Node == pty->ServerNode;
733
734         switch(ID)
735         {
736         case DRV_IOCTL_TYPE:    return DRV_TYPE_TERMINAL;
737         case DRV_IOCTL_IDENT:   memcpy(Data, "PTY\0", 4);       return 0;
738         case DRV_IOCTL_VERSION: return 0x100;
739         case DRV_IOCTL_LOOKUP:  return 0;
740         
741         case PTY_IOCTL_GETMODE:
742                 if( !pty )      return 0;
743                 if( !CheckMem(Data, sizeof(*mode)) ) { errno = EINVAL; return -1; }
744                 *mode = pty->Mode;
745                 // TODO: ACK client's SETMODE
746                 return 0;
747         case PTY_IOCTL_SETMODE:
748                 if( !pty )      return 0;
749                 if( !CheckMem(Data, sizeof(*mode)) ) { errno = EINVAL; return -1; }
750                 PTY_SetAttrib(pty, NULL, mode, !is_server);
751                 return 0;
752         case PTY_IOCTL_GETDIMS:
753                 if( !pty )      return 0;
754                 if( !CheckMem(Data, sizeof(*dims)) ) { errno = EINVAL; return -1; }
755                 *dims = pty->Dims;
756                 return 0;
757         case PTY_IOCTL_SETDIMS:
758                 if( !pty )      return 0;
759                 if( !CheckMem(Data, sizeof(*dims)) ) { errno = EINVAL; return -1; }
760                 PTY_SetAttrib(pty, dims, NULL, !is_server);
761                 return 0;
762         case PTY_IOCTL_GETID:
763                 if( pty )
764                 {
765                         size_t  len = strlen(pty->Name)+1;
766                         if( Data )
767                         {
768                                 if( !CheckMem(Data, len) ) { errno = EINVAL; return -1; }
769                                 strcpy(Data, pty->Name);
770                         }
771                         return len;
772                 }
773                 return 0;
774         case PTY_IOCTL_SETID:
775                 if( Data && !CheckString(Data) ) { errno = EINVAL; return -1; }
776                 if( pty )       return EALREADY;
777                 pty = PTY_Create(Data, NULL, NULL,NULL, NULL);
778                 if(pty == NULL)
779                         return 1;
780                 Node->ImplPtr = pty;
781                 pty->ServerNode = Node;
782                 return 0;
783         case PTY_IOCTL_SETPGRP:
784                 // TODO: Should this only be done by client?
785                 if( Data )
786                 {
787                         if( !CheckMem(Data, sizeof(tPGID)) ) { errno = EINVAL; return -1; }
788                         pty->ControllingProcGroup = *(tPGID*)Data;
789                         Log_Debug("PTY", "Set controlling PGID to %i", pty->ControllingProcGroup);
790                 }
791                 return pty->ControllingProcGroup;
792         }
793         errno = ENOSYS;
794         return -1;
795 }
796

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