3 * - By John Hodge (thePowersGang)
17 #define OUTPUT_RINGBUFFER_LEN 1024 // Number of bytes in output queue before client blocks
18 #define INPUT_RINGBUFFER_LEN 256 // Number of bytes in input queue before being dropped
19 #define INPUT_LINE_LEN 256
29 tPTY_OutputFcn OutputFcn;
38 char InputData[INPUT_RINGBUFFER_LEN];
41 char LineData[INPUT_LINE_LEN];
46 char OutputData[OUTPUT_RINGBUFFER_LEN];
52 // TODO: Maintain list of client PIDs
56 int PTY_Install(char **Arguments);
57 int PTY_ReadDir(tVFS_Node *Node, int Pos, char Name[FILENAME_MAX]);
58 tVFS_Node *PTY_FindDir(tVFS_Node *Node, const char *Name, Uint Flags);
60 size_t _rb_write(void *buf, size_t buflen, int *rd, int *wr, const void *data, size_t len);
61 size_t _rb_read(void *buf, size_t buflen, int *rd, int *wr, void *data, size_t len);
62 size_t PTY_int_WriteInput(tPTY *PTY, const char *Input, size_t Length);
63 size_t PTY_int_SendInput(tPTY *PTY, const char *Input, size_t Length);
65 size_t PTY_ReadClient(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer, Uint Flags);
66 size_t PTY_WriteClient(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer, Uint Flags);
67 int PTY_IOCtlClient(tVFS_Node *Node, int ID, void *Arg);
68 void PTY_ReferenceClient(tVFS_Node *Node);
69 void PTY_CloseClient(tVFS_Node *Node);
70 size_t PTY_ReadServer(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer, Uint Flags);
71 size_t PTY_WriteServer(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer, Uint Flags);
72 int PTY_IOCtlServer(tVFS_Node *Node, int ID, void *Arg);
73 void PTY_CloseServer(tVFS_Node *Node);
76 MODULE_DEFINE(0, 0x100, PTY, PTY_Install, NULL, NULL);
77 tVFS_NodeType gPTY_NodeType_Root = {
78 .TypeName = "PTY-Root",
79 .ReadDir = PTY_ReadDir,
80 .FindDir = PTY_FindDir
82 tVFS_NodeType gPTY_NodeType_Client = {
83 .TypeName = "PTY-Client",
84 .Read = PTY_ReadClient,
85 .Write = PTY_WriteClient,
86 .IOCtl = PTY_IOCtlClient,
87 .Reference = PTY_ReferenceClient,
88 .Close = PTY_CloseClient
90 tVFS_NodeType gPTY_NodeType_Server = {
91 .TypeName = "PTY-Server",
92 .Read = PTY_ReadServer,
93 .Write = PTY_WriteServer,
94 .IOCtl = PTY_IOCtlServer,
95 .Close = PTY_CloseServer
98 tRWLock glPTY_NumPTYs;
99 tPTY *gpPTY_FirstNumPTY;
100 int giPTY_NamedCount;
101 tRWLock glPTY_NamedPTYs;
102 tPTY *gpPTY_FirstNamedPTY;
105 int PTY_Install(char **Arguments)
107 return MODULE_ERR_OK;
110 // --- Management ---
111 tPTY *PTY_Create(const char *Name, void *Handle, tPTY_OutputFcn Output)
113 tPTY **prev_np = NULL;
116 if( Name && Name[0] )
118 prev_np = &gpPTY_FirstNamedPTY;
120 // Check the name isn't decimal
122 if( strtol(Name, &end, 10) != 0 && *end == '\0' ) {
127 RWLock_AcquireWrite(&glPTY_NamedPTYs);
129 for( tPTY *pty = gpPTY_FirstNamedPTY; pty; prev_np = &pty->Next, pty = pty->Next )
131 int cmp = strcmp(pty->Name, Name);
135 RWLock_Release(&glPTY_NamedPTYs);
141 namelen = strlen(Name);
146 RWLock_AcquireWrite(&glPTY_NumPTYs);
147 // Get a pty ID if Name==NULL
148 prev_np = &gpPTY_FirstNumPTY;
149 for( tPTY *pty = gpPTY_FirstNumPTY; pty; prev_np = &pty->Next, pty = pty->Next )
151 if( pty->NumericName > idx )
158 tPTY *ret = calloc(sizeof(tPTY) + namelen + 1, 1);
160 // - List maintainance
161 ret->Next = *prev_np;
163 // - PTY Name (Used by VT)
164 ret->Name = (char*)(ret + 1);
166 strcpy(ret->Name, Name);
169 ret->NumericName = idx;
170 // - Output function and handle (same again)
171 ret->OutputHandle = Handle;
172 ret->OutputFcn = Output;
174 ret->ServerNode.ImplPtr = ret;
175 ret->ServerNode.Type = &gPTY_NodeType_Server;
176 ret->ServerNode.UID = Threads_GetUID();
177 ret->ServerNode.GID = Threads_GetGID();
178 ret->ServerNode.NumACLs = 1;
179 ret->ServerNode.ACLs = &ret->OwnerRW;
180 ret->ServerNode.ReferenceCount = (Output ? 1 : 0); // Prevents a userland open/close killing a kernel pty
182 ret->ClientNode.ImplPtr = ret;
183 ret->ClientNode.Type = &gPTY_NodeType_Client;
184 ret->ClientNode.UID = Threads_GetUID();
185 ret->ClientNode.GID = Threads_GetGID();
186 ret->ClientNode.NumACLs = 1;
187 ret->ClientNode.ACLs = &ret->OwnerRW;
188 // - Owner Read-Write ACL
189 ret->OwnerRW.Ent.ID = Threads_GetUID();
190 ret->OwnerRW.Perm.Perms = -1;
192 if( Name && Name[0] ) {
194 RWLock_Release(&glPTY_NamedPTYs);
198 RWLock_Release(&glPTY_NumPTYs);
204 void PTY_SetAttrib(tPTY *PTY, const struct ptydims *Dims, const struct ptymode *Mode, int WasClient)
208 if( !WasClient && !PTY->OutputFcn )
210 Log_Warning("PTY", "TODO: Need to stop client output until modeset has been ACKed");
211 // Block write until acked
212 // ACK by server doing GETMODE
221 // SIGWINSZ to client
226 void PTY_Close(tPTY *PTY)
231 size_t _rb_write(void *buf, size_t buflen, int *rd, int *wr, const void *data, size_t len)
233 size_t space = (*rd - *wr + buflen) % buflen - 1;
234 len = MIN(space, len);
235 if(*wr + len >= buflen) {
236 size_t prelen = buflen - *wr;
237 memcpy((char*)buf + *wr, data, prelen);
238 memcpy(buf, (char*)data + prelen, len - prelen);
242 memcpy((char*)buf + *wr, data, len);
247 size_t _rb_read(void *buf, size_t buflen, int *rd, int *wr, void *data, size_t len)
249 size_t space = (*wr - *rd + buflen) % buflen;
250 len = MIN(space, len);
251 if(*rd + len >= buflen) {
252 size_t prelen = buflen - *rd;
253 memcpy(data, (char*)buf + *rd, prelen);
254 memcpy((char*)data + prelen, buf, len - prelen);
258 memcpy(data, (char*)buf + *rd, len);
264 size_t PTY_int_WriteInput(tPTY *PTY, const char *Input, size_t Length)
268 Mutex_Acquire(&PTY->InputMutex);
270 ret = _rb_write(PTY->InputData, INPUT_RINGBUFFER_LEN, &PTY->InputReadPos, &PTY->InputWritePos,
273 Mutex_Release(&PTY->InputMutex);
275 VFS_MarkAvaliable(&PTY->ClientNode, 1);
277 VFS_MarkFull(&PTY->ServerNode, 1);
282 size_t PTY_int_SendInput(tPTY *PTY, const char *Input, size_t Length)
284 size_t ret = 1, print = 1;
286 // Only counts for text input modes
287 if( (PTY->Mode.OutputMode & PTYOMODE_BUFFMT) == PTYBUFFMT_TEXT )
288 return PTY_int_WriteInput(PTY, Input, Length);
289 // If in raw mode, flush directlr
290 if( (PTY->Mode.InputMode & PTYIMODE_RAW) )
291 return PTY_int_WriteInput(PTY, Input, Length);
293 if( PTY->Mode.InputMode & PTYIMODE_CANON )
295 const char char_bs = '\b';
303 PTY_int_WriteInput(PTY, PTY->LineData, PTY->LineLength);
304 PTY->HasHitEOF = (PTY->LineLength == 0);
309 if(PTY->LineLength != 0)
312 case 'w'-'a': // Word erase
313 while(PTY->LineLength != 0 && isalnum(PTY->LineData[--PTY->LineLength]))
314 PTY_WriteClient(&PTY->ClientNode, 0, 1, &char_bs, 0);
317 case 'u'-'a': // Kill
318 while(PTY->LineLength > 0)
319 PTY_WriteClient(&PTY->ClientNode, 0, 1, &char_bs, 0);
329 if(PTY->LineLength == INPUT_LINE_LEN) {
330 PTY_int_WriteInput(PTY, PTY->LineData, PTY->LineLength);
333 PTY->LineData[PTY->LineLength++] = '\n';
334 PTY_int_WriteInput(PTY, PTY->LineData, PTY->LineLength);
337 // TODO: Handle ^[[D and ^[[C for in-line editing, also ^[[1~/^[[4~ (home/end)
342 if(PTY->LineLength == INPUT_LINE_LEN) {
343 PTY_int_WriteInput(PTY, PTY->LineData, PTY->LineLength);
346 PTY->LineData[PTY->LineLength++] = Input[0];
352 ret = PTY_int_WriteInput(PTY, Input, Length);
356 if( PTY->Mode.InputMode & PTYIMODE_ECHO )
358 PTY_WriteClient(&PTY->ClientNode, 0, print, Input, 0);
364 size_t PTY_SendInput(tPTY *PTY, const char *Input, size_t Length)
367 while( ret < Length && !PTY->ClientNode.BufferFull )
369 // TODO: Detect blocking?
370 ret += PTY_int_SendInput(PTY, Input + ret, Length - ret);
376 int PTY_ReadDir(tVFS_Node *Node, int Pos, char Name[FILENAME_MAX])
379 if( Pos < giPTY_NumCount * 2 )
381 RWLock_AcquireRead(&glPTY_NumPTYs);
382 for( pty = gpPTY_FirstNumPTY; pty; pty = pty->Next )
388 RWLock_Release(&glPTY_NumPTYs);
390 else if( Pos < (giPTY_NumCount + giPTY_NamedCount) * 2 )
392 RWLock_AcquireRead(&glPTY_NamedPTYs);
393 for( pty = gpPTY_FirstNamedPTY; pty; pty = pty->Next )
399 RWLock_Release(&glPTY_NamedPTYs);
407 snprintf(Name, 255, "%s%c", pty->Name, (Pos == 0 ? 'c' : 's'));
409 snprintf(Name, 255, "%i%c", pty->NumericName, (Pos == 0 ? 'c' : 's'));
413 tVFS_Node *PTY_FindDir(tVFS_Node *Node, const char *Name, Uint Flags)
416 int num = strtol(Name, &end, 10);
418 if( Name[0] == '\0' || Name[1] == '\0' )
421 size_t len = strlen(Name);
422 if( Name[len-1] != 'c' && Name[len-1] != 's' )
424 int isServer = (Name[len-1] != 'c');
427 if( num && (end[0] == 'c' || end[0] == 's') && end[1] == '\0' )
430 RWLock_AcquireRead(&glPTY_NumPTYs);
431 for( tPTY *pty = gpPTY_FirstNumPTY; pty; pty = pty->Next )
433 if( pty->NumericName > num )
435 if( pty->NumericName == num ) {
440 RWLock_Release(&glPTY_NumPTYs);
445 RWLock_AcquireRead(&glPTY_NamedPTYs);
446 for( tPTY *pty = gpPTY_FirstNamedPTY; pty; pty = pty->Next )
448 int cmp = strncmp(pty->Name, Name, len-1);
451 if(cmp == 0 && pty->Name[len-1] == '\0' ) {
456 RWLock_Release(&glPTY_NamedPTYs);
459 return (isServer ? &ret->ServerNode : &ret->ClientNode);
464 //\! Read from the client's input
465 size_t PTY_ReadClient(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer, Uint Flags)
467 tPTY *pty = Node->ImplPtr;
469 // Read from flushed queue
470 tTime timeout_z = 0, *timeout = (Flags & VFS_IOFLAG_NOBLOCK) ? &timeout_z : NULL;
473 rv = VFS_SelectNode(Node, VFS_SELECT_READ, timeout, "PTY_ReadClient");
475 errno = (timeout ? EWOULDBLOCK : EINTR);
479 Mutex_Acquire(&pty->InputMutex);
480 Length = _rb_read(pty->InputData, INPUT_RINGBUFFER_LEN, &pty->InputReadPos, &pty->InputWritePos,
482 Mutex_Release(&pty->InputMutex);
484 if(Length == 0 && !pty->HasHitEOF) {
492 //\! Write to the client's output
493 size_t PTY_WriteClient(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer, Uint Flags)
495 tPTY *pty = Node->ImplPtr;
497 // Write to either FIFO or directly to output function
500 pty->OutputFcn(pty->OutputHandle, Buffer, Length, &pty->Dims);
504 // Write to output ringbuffer
505 Length = _rb_write(pty->OutputData, OUTPUT_RINGBUFFER_LEN,
506 &pty->OutputReadPos, &pty->OutputWritePos,
508 VFS_MarkAvaliable(&pty->ServerNode, 1);
514 int PTY_IOCtlClient(tVFS_Node *Node, int ID, void *Data)
516 tPTY *pty = Node->ImplPtr;
517 struct ptymode *mode = Data;
518 struct ptydims *dims = Data;
521 case DRV_IOCTL_TYPE: return DRV_TYPE_TERMINAL;
522 case DRV_IOCTL_IDENT: memcpy(Data, "PTY\0", 4); return 0;
523 case DRV_IOCTL_VER: return 0x100;
524 case DRV_IOCTL_LOOKUP: return 0;
526 case PTY_IOCTL_GETMODE:
527 if( !CheckMem(Data, sizeof(*mode)) ) { errno = EINVAL; return -1; }
530 case PTY_IOCTL_SETMODE:
531 if( !CheckMem(Data, sizeof(*mode)) ) { errno = EINVAL; return -1; }
532 PTY_SetAttrib(pty, NULL, mode, 1);
534 case PTY_IOCTL_GETDIMS:
535 if( !CheckMem(Data, sizeof(*dims)) ) { errno = EINVAL; return -1; }
538 case PTY_IOCTL_SETDIMS:
539 if( !CheckMem(Data, sizeof(*dims)) ) { errno = EINVAL; return -1; }
540 PTY_SetAttrib(pty, dims, NULL, 1);
541 // Inform the server?
548 void PTY_ReferenceClient(tVFS_Node *Node)
550 Node->ReferenceCount ++;
551 // TODO: Add PID to list of client PIDs
552 Log_Notice("PTY", "TODO: List of client PIDs");
555 void PTY_CloseClient(tVFS_Node *Node)
557 tPTY *pty = Node->ImplPtr;
558 Node->ReferenceCount --;
560 // Remove PID from list
562 // Free structure if this was the last open handle
563 if( Node->ReferenceCount == 0 && pty->ServerNode.ReferenceCount == 0 )
565 // Free the structure! (Should be off the PTY list now)
570 //\! Read from the client's output
571 size_t PTY_ReadServer(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer, Uint Flags)
573 tPTY *pty = Node->ImplPtr;
575 // TODO: Prevent two servers fighting over client's output
578 // Kernel-land PTYs can't be read from userland
582 // Read back from fifo
583 tTime timeout_z = 0, *timeout = (Flags & VFS_IOFLAG_NOBLOCK) ? &timeout_z : NULL;
584 int rv = VFS_SelectNode(Node, VFS_SELECT_READ, timeout, "PTY_ReadServer");
586 errno = (timeout ? EWOULDBLOCK : EINTR);
590 Length = _rb_read(pty->OutputData, OUTPUT_RINGBUFFER_LEN,
591 &pty->OutputReadPos, &pty->OutputWritePos,
593 if( pty->OutputReadPos == pty->OutputWritePos )
594 VFS_MarkAvaliable(Node, 0);
595 VFS_MarkFull(&pty->ClientNode, 0);
600 //\! Write to the client's input
601 size_t PTY_WriteServer(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer, Uint Flags)
603 // Write to current line buffer, flushing on unknown character or newline
605 // Echo back if instructed
606 PTY_SendInput(Node->ImplPtr, Buffer, Length);
610 int PTY_IOCtlServer(tVFS_Node *Node, int ID, void *Data)
612 tPTY *pty = Node->ImplPtr;
613 struct ptymode *mode = Data;
614 struct ptydims *dims = Data;
617 case DRV_IOCTL_TYPE: return DRV_TYPE_TERMINAL;
618 case DRV_IOCTL_IDENT: memcpy(Data, "PTY\0", 4); return 0;
619 case DRV_IOCTL_VER: return 0x100;
620 case DRV_IOCTL_LOOKUP: return 0;
622 case PTY_IOCTL_GETMODE:
623 if( !CheckMem(Data, sizeof(*mode)) ) { errno = EINVAL; return -1; }
625 // ACK client's SETMODE
627 case PTY_IOCTL_SETMODE:
628 if( !CheckMem(Data, sizeof(*mode)) ) { errno = EINVAL; return -1; }
629 PTY_SetAttrib(pty, NULL, mode, 0);
631 case PTY_IOCTL_GETDIMS:
632 if( !CheckMem(Data, sizeof(*dims)) ) { errno = EINVAL; return -1; }
635 case PTY_IOCTL_SETDIMS:
636 if( !CheckMem(Data, sizeof(*dims)) ) { errno = EINVAL; return -1; }
637 PTY_SetAttrib(pty, dims, NULL, 0);
644 void PTY_CloseServer(tVFS_Node *Node)
647 Node->ReferenceCount --;
648 // If reference count == 0, remove from main list and SIGPIPE all clients when they write