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

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