20#include "pedigree/kernel/LockGuard.h"
21#include "pedigree/kernel/Log.h"
22#include "pedigree/kernel/process/Mutex.h"
23#include "pedigree/kernel/process/TerminationDeferral.h"
24#include "pedigree/kernel/processor/types.h"
25#include "pedigree/kernel/syscallError.h"
26#include "pedigree/kernel/utilities/Buffer.h"
27#include "pedigree/kernel/utilities/String.h"
28#include "pedigree/kernel/utilities/assert.h"
29#include "pedigree/kernel/utilities/utility.h"
32#include "ConsoleDefines.h"
33#include "modules/system/vfs/File.h"
37extern const char defaultControl[MAX_CONTROL_CHAR];
39ConsoleFile::ConsoleFile(
size_t consoleNumber,
String consoleName,
Filesystem* pFs)
40 :
ConsoleFile(consoleNumber, consoleName, pFs, nullptr) {}
42ConsoleFile::ConsoleFile(
size_t consoleNumber,
String consoleName,
Filesystem* pFs,
File* pParent)
43 :
File(consoleName, 0, 0, 0, 0xdeadbeef, pFs, 0, pParent),
45 m_Flags(DEFAULT_FLAGS),
53 m_ConsoleNumber(consoleNumber),
54 m_ConsoleName(consoleName) {
55 MemoryCopy(m_ControlChars, defaultControl, MAX_CONTROL_CHAR);
59 setPermissionsOnly(FILE_UR | FILE_UW | FILE_GR | FILE_GW | FILE_OR | FILE_OW);
64ConsoleIoState::ConsoleIoState()
66 input(PTY_BUFFER_SIZE),
67 output(PTY_BUFFER_SIZE),
72 physicalWake(0, true),
75ConsoleIoState::~ConsoleIoState() {
82bool ConsoleIoState::revoked()
const {
83 return __atomic_load_n(&m_Revoked, __ATOMIC_ACQUIRE);
86void ConsoleIoState::closeAdmission() {
87 __atomic_store_n(&m_Revoked,
true, __ATOMIC_RELEASE);
91void ConsoleIoState::cancelAndDrain() {
101 return isMaster() ?
m_pOther :
this;
107 while (waitForReopen && owner->m_HangingUp)
108 owner->m_IoChanged.waitForCompletion(owner->m_IoLock);
109 if (waitForReopen && !owner->m_IoState)
111 return owner->m_IoState;
118 if (owner->m_HangingUp)
120 owner->m_HangingUp =
true;
121 retired = owner->m_IoState;
123 retired->closeAdmission();
132 retired->cancelAndDrain();
135 assert(owner->m_HangingUp && owner->m_IoState.get() == retired.
get());
136 owner->m_IoState = replacement;
137 owner->m_Flags = DEFAULT_FLAGS;
138 MemoryCopy(owner->m_ControlChars, defaultControl, MAX_CONTROL_CHAR);
139 owner->m_HangingUp =
false;
140 owner->m_IoChanged.broadcast();
148 return owner->m_ControlState;
156 retired = pedigree_std::move(owner->m_ControlState);
157 owner->m_ControlState = state;
162 uintptr_t buffer,
bool canBlock) {
165 if (!epoch || !epoch->operations.tryAcquire(operation) || epoch->revoked())
167 uint64_t amount = readIo(*epoch, size, buffer, canBlock);
168 return epoch->revoked() ? 0 : amount;
172 uintptr_t buffer,
bool canBlock) {
175 if (!epoch || !epoch->operations.tryAcquire(operation) || epoch->revoked()) {
176 SYSCALL_ERROR(IoError);
179 uint64_t amount = writeIo(*epoch, size, buffer, canBlock);
180 if (epoch->revoked() && !amount)
181 SYSCALL_ERROR(IoError);
185void ConsoleFile::changed() {
192 auto state = captureOpenEpoch(
false);
195 Buffer<char>& source = isMaster() ? state->output : state->input;
196 Buffer<char>& destination = isMaster() ? state->input : state->output;
197 return (writing ? destination.
canWrite(timeout > 0) : source.
canRead(timeout > 0)) ? 1 : 0;
202 if (!epoch || epoch->revoked())
203 return ReadyRead | ReadyWrite | ReadyError | ReadyHangup;
208 ReadyMask ready = ReadyNone;
209 Buffer<char>& source = isMaster() ? epoch->output : epoch->input;
210 Buffer<char>& destination = isMaster() ? epoch->input : epoch->output;
211 if (reading && source.
canRead(
false))
213 if (writing && destination.
canWrite(
false))
219 return queryEpoch(captureOpenEpoch(
false), reading, writing);
223 return (isMaster() ? state.output : state.input).getDataSize();
229 result.read = (isMaster() ? epoch->output : epoch->input).readableGeneration();
230 result.write = (isMaster() ? epoch->input : epoch->output).writableGeneration();
231 result.error = result.hangup = epoch->revoked() ? 1 : 0;
237 return epochGenerations(captureOpenEpoch(
false));
242 size_t slaveFlags = flags;
245 if (slaveFlags & (ConsoleManager::OPostProcess)) {
246 const size_t tmpSize = maxSz > len ? maxSz : len;
247 char* tmpBuff =
new char[tmpSize];
251 size_t realSize = len;
254 for (
size_t i = 0, j = 0; j < len; j++) {
258 if (pC[j] ==
'\r' && (slaveFlags & ConsoleManager::OMapCRToNL)) {
264 else if (pC[j] ==
'\n' && (slaveFlags & ConsoleManager::OMapNLToCRNL)) {
265 if (realSize >= maxSz) {
269 "Console ignored an NL -> CRNL conversion due to a "
285 if (pC[j] ==
'\n' && (slaveFlags & ConsoleManager::ONLCausesCR)) {
291 tmpBuff[i++] = pC[j];
295 MemoryCopy(buf, tmpBuff, realSize);
306 size_t realLen = len;
307 for (
size_t i = 0; i < len; i++) {
308 if (m_Flags & ConsoleManager::IStripToSevenBits)
309 pC[i] =
static_cast<uint8_t
>(pC[i]) & 0x7F;
310 if (m_Flags & ConsoleManager::LCookedMode) {
311 if (pC[i] == m_ControlChars[VEOF]) {
318 if (pC[i] ==
'\n' && (m_Flags & ConsoleManager::IMapNLToCR))
320 else if (pC[i] ==
'\r' && (m_Flags & ConsoleManager::IMapCRToNL))
322 else if (pC[i] ==
'\r' && (m_Flags & ConsoleManager::IIgnoreCR)) {
323 MemoryCopy(buf + i, buf + i + 1, len - i - 1);
333 size_t flags,
const char* controlChars) {
335 if (state.revoked()) {
342 size_t slaveFlags = flags;
343 if (controlChars == 0) {
344 controlChars =
m_pOther->m_ControlChars;
346 const char* slaveControlChars = controlChars;
348 size_t localWritten = 0;
351 if (slaveFlags & (ConsoleManager::LCookedMode | ConsoleManager::LEcho)) {
353 bool bAppBufferComplete =
false;
356 char* destBuff =
new char[len];
360 size_t destBuffOffset = 0;
363 while (!bAppBufferComplete) {
364 for (
size_t i = 0; i < len && !state.revoked(); i++) {
365 if (state.lineSize == LINEBUFFER_MAXIMUM) {
366 state.input.
write(state.line, state.lineSize, canBlock);
368 state.firstNewline = ~0UL;
371 bool isCanonical = (slaveFlags & ConsoleManager::LCookedMode);
372 if (isCanonical && (buf[i] == slaveControlChars[VEOF])) {
374 state.input.
write(&buf[i], 1, canBlock);
380 if ((buf[i] ==
'\r') || (isCanonical && (buf[i] == slaveControlChars[VEOL]))) {
383 if ((slaveFlags & ConsoleManager::LEcho) || (slaveFlags & ConsoleManager::LCookedMode)) {
385 state.line[state.lineSize++] =
'\n';
386 if ((slaveFlags & ConsoleManager::LEchoNewline) ||
387 (slaveFlags & ConsoleManager::LEcho)) {
388 char tmp[] = {
'\n', 0};
389 state.output.
write(tmp, 1);
393 if ((slaveFlags & ConsoleManager::LCookedMode) && !bAppBufferComplete) {
395 size_t realSize = state.lineSize;
396 if (state.firstNewline < realSize) {
397 realSize = state.firstNewline;
398 state.firstNewline = ~0UL;
401 state.input.
write(state.line, realSize, canBlock);
405 uint64_t nConsumedBytes = state.lineSize - realSize;
408 MemoryCopy(state.line, &state.line[realSize], nConsumedBytes);
411 state.lineSize -= realSize;
414 bAppBufferComplete =
true;
415 }
else if ((slaveFlags & ConsoleManager::LCookedMode) && (state.firstNewline == ~0UL)) {
418 state.firstNewline = state.lineSize - 1;
419 }
else if (!(slaveFlags & ConsoleManager::LCookedMode)) {
421 destBuff[destBuffOffset++] = buf[i];
425 if (i + 1 < len && buf[i + 1] ==
'\n')
428 }
else if (buf[i] == m_ControlChars[VERASE]) {
429 if (slaveFlags & (ConsoleManager::LCookedMode | ConsoleManager::LEchoErase)) {
430 if ((slaveFlags & ConsoleManager::LCookedMode) && state.lineSize) {
431 char ctl[3] = {
'\x08',
' ',
'\x08'};
432 state.output.
write(ctl, 3);
435 }
else if ((!(slaveFlags & ConsoleManager::LCookedMode)) && destBuffOffset) {
436 char ctl[3] = {
'\x08',
' ',
'\x08'};
437 state.output.
write(ctl, 3);
444 if (isControlCharacter(slaveFlags, buf[i], controlChars)) {
449 if (!state.output.
canWrite(
false)) {
453 "Console: dropping bytes to be able to "
454 "render visual control code (e.g. ^C)");
456 state.output.
read(tmp, 3);
460 char ctl_c =
'@' + buf[i];
461 char ctl[3] = {
'^', ctl_c,
'\n'};
462 state.output.
write(ctl, 3);
465 notifyControlCharacter(buf[i], controlChars);
470 if (slaveFlags & ConsoleManager::LEcho) {
471 state.output.
write(&buf[i], 1);
476 if (slaveFlags & ConsoleManager::LCookedMode)
477 state.line[state.lineSize++] = buf[i];
479 destBuff[destBuffOffset++] = buf[i];
485 if (state.lineSize >= LINEBUFFER_MAXIMUM) {
488 size_t numBytesToRemove = state.lineSize;
491 state.input.
write(state.line, numBytesToRemove, canBlock);
494 uint64_t nConsumedBytes = state.lineSize - numBytesToRemove;
497 MemoryCopy(state.line, &state.line[numBytesToRemove], nConsumedBytes);
500 state.lineSize -= numBytesToRemove;
507 if (destBuffOffset) {
508 state.input.
write(destBuff, destBuffOffset, canBlock);
514 while (consumed < len && !state.revoked()) {
515 if (isControlCharacter(slaveFlags, buf[consumed], controlChars)) {
516 notifyControlCharacter(buf[consumed++], controlChars);
520 size_t end = consumed + 1;
521 while (end < len && !isControlCharacter(slaveFlags, buf[end], controlChars)) {
528 if (!state.input.
canWrite(canBlock && !consumed)) {
531 size_t amount = state.input.
writeAvailable(buf + consumed, end - consumed);
533 if (consumed < end) {
546bool ConsoleFile::isControlCharacter(
size_t flags,
char check,
const char* controlChars) {
548 if (flags & ConsoleManager::LGenerateEvent) {
549 if (check && (check == controlChars[VINTR] || check == controlChars[VQUIT] ||
550 check == controlChars[VSUSP])) {
557void ConsoleFile::notifyControlCharacter(
char cause,
const char* controlChars) {
558 auto control = controlState();
563 if (cause == controlChars[VINTR])
564 control->controlCharacter(ConsoleControlState::Character::Interrupt);
565 else if (cause == controlChars[VQUIT])
566 control->controlCharacter(ConsoleControlState::Character::Quit);
567 else if (cause == controlChars[VSUSP])
568 control->controlCharacter(ConsoleControlState::Character::Suspend);
size_t write(const T *buffer, size_t count, bool block=true)
bool canWrite(bool block)
size_t writeAvailable(const T *buffer, size_t count, bool atomic=false)
size_t read(T *buffer, size_t count, bool block=true)
ConsoleFile * m_pOther
Other side of the console.
ReadyMask queryReady(bool reading, bool writing) override
ReadinessGenerations readinessGenerations() override
virtual int select(bool bWriting, int timeout)
select - check and optionally for a particular state.
static size_t outputLineDiscipline(char *buf, size_t len, size_t maxSz, size_t flags=0)
Output line discipline.
size_t inputLineDiscipline(ConsoleIoState &state, char *buf, size_t len, bool canBlock, size_t flags=~0U, const char *controlChars=nullptr)
Input line discipline.
size_t processInput(char *buf, size_t len)
Input processing.
virtual ReadyMask queryReady(bool reading, bool writing)
static SharedPointer< T > tryAllocate(Args...)