The Pedigree Project  0.1
ConsoleCommon.cc
1 /*
2  * Copyright (c) 2008-2014, Pedigree Developers
3  *
4  * Please see the CONTRIB file in the root of the source tree for a full
5  * list of contributors.
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include "Console.h"
21 #include "ConsoleDefines.h"
22 #include "modules/system/vfs/File.h"
23 #include "pedigree/kernel/Log.h"
24 #include "pedigree/kernel/process/Mutex.h"
25 #include "pedigree/kernel/process/Scheduler.h"
26 #include "pedigree/kernel/process/Thread.h"
27 #include "pedigree/kernel/processor/Processor.h"
28 #include "pedigree/kernel/processor/ProcessorInformation.h"
29 #include "pedigree/kernel/processor/types.h"
30 #include "pedigree/kernel/utilities/Buffer.h"
31 #include "pedigree/kernel/utilities/String.h"
32 #include "pedigree/kernel/utilities/utility.h"
33 
34 class Filesystem;
35 
36 extern const char defaultControl[MAX_CONTROL_CHAR];
37 
38 ConsoleFile::ConsoleFile(
39  size_t consoleNumber, String consoleName, Filesystem *pFs)
40  : File(consoleName, 0, 0, 0, 0xdeadbeef, pFs, 0, 0), m_pOther(0),
41  m_Flags(DEFAULT_FLAGS), m_Rows(25), m_Cols(80), m_LineBuffer(),
42  m_LineBufferSize(0), m_LineBufferFirstNewline(~0), m_Last(0),
43  m_Buffer(PTY_BUFFER_SIZE), m_ConsoleNumber(consoleNumber),
44  m_Name(consoleName), m_pEvent(0), m_EventTrigger(true)
45 {
46  MemoryCopy(m_ControlChars, defaultControl, MAX_CONTROL_CHAR);
47 
48  // r/w for all (todo: when a console is locked, it should become owned
49  // by the locking user)
50  setPermissionsOnly(
51  FILE_UR | FILE_UW | FILE_GR | FILE_GW | FILE_OR | FILE_OW);
52  setUidOnly(0);
53  setGidOnly(0);
54 }
55 
56 int ConsoleFile::select(bool bWriting, int timeout)
57 {
58  if (bWriting)
59  {
60  return m_Buffer.canWrite(timeout > 0) ? 1 : 0;
61  }
62  else
63  {
64  return m_Buffer.canRead(timeout > 0) ? 1 : 0;
65  }
66 }
67 
68 void ConsoleFile::inject(char *buf, size_t len, bool canBlock)
69 {
70  m_Buffer.write(buf, len, canBlock);
71  dataChanged();
72 }
73 
75  char *buf, size_t len, size_t maxSz, size_t flags)
76 {
77  // Make sure we always have the latest flags from the slave.
78  size_t slaveFlags = flags;
79 
80  // Post-process output if enabled.
81  if (slaveFlags & (ConsoleManager::OPostProcess))
82  {
83  char *tmpBuff = new char[len];
84  size_t realSize = len;
85 
86  char *pC = buf;
87  for (size_t i = 0, j = 0; j < len; j++)
88  {
89  bool bInsert = true;
90 
91  // OCRNL: Map CR to NL on output
92  if (pC[j] == '\r' && (slaveFlags & ConsoleManager::OMapCRToNL))
93  {
94  tmpBuff[i++] = '\n';
95  continue;
96  }
97 
98  // ONLCR: Map NL to CR-NL on output
99  else if (
100  pC[j] == '\n' && (slaveFlags & ConsoleManager::OMapNLToCRNL))
101  {
102  if (realSize >= maxSz)
103  {
104  // We do not have any room to add in the mapped character.
105  // Drop it.
106  WARNING("Console ignored an NL -> CRNL conversion due to a "
107  "full buffer.");
108  tmpBuff[i++] = '\n';
109  continue;
110  }
111 
112  realSize++;
113 
114  char *newBuff = new char[realSize];
115  MemoryCopy(newBuff, tmpBuff, i);
116  delete[] tmpBuff;
117  tmpBuff = newBuff;
118 
119  // Add the newline and the caused carriage return
120  tmpBuff[i++] = '\r';
121  tmpBuff[i++] = '\n';
122 
123  continue;
124  }
125 
126  // ONLRET: NL performs CR function
127  if (pC[j] == '\n' && (slaveFlags & ConsoleManager::ONLCausesCR))
128  {
129  tmpBuff[i++] = '\r';
130  continue;
131  }
132 
133  if (bInsert)
134  {
135  tmpBuff[i++] = pC[j];
136  }
137  }
138 
139  MemoryCopy(buf, tmpBuff, realSize);
140  delete[] tmpBuff;
141  len = realSize;
142  }
143 
144  return len;
145 }
146 
147 size_t ConsoleFile::processInput(char *buf, size_t len)
148 {
149  // Perform input processing.
150  char *pC = buf;
151  size_t realLen = len;
152  for (size_t i = 0; i < len; i++)
153  {
154  if (m_Flags & ConsoleManager::IStripToSevenBits)
155  pC[i] = static_cast<uint8_t>(pC[i]) & 0x7F;
156  if (m_Flags & ConsoleManager::LCookedMode)
157  {
158  if (pC[i] == m_ControlChars[VEOF])
159  {
160  // Zero-length read: EOF.
161  realLen = 0;
162  break;
163  }
164  }
165 
166  if (pC[i] == '\n' && (m_Flags & ConsoleManager::IMapNLToCR))
167  pC[i] = '\r';
168  else if (pC[i] == '\r' && (m_Flags & ConsoleManager::IMapCRToNL))
169  pC[i] = '\n';
170  else if (pC[i] == '\r' && (m_Flags & ConsoleManager::IIgnoreCR))
171  {
172  MemoryCopy(buf + i, buf + i + 1, len - i - 1);
173  i--; // Need to process this byte again, its contents have changed.
174  realLen--;
175  }
176  }
177 
178  return realLen;
179 }
180 
182  char *buf, size_t len, size_t flags, const char *controlChars)
183 {
184  // Make sure we always have the latest flags from the slave.
185  if (flags == ~0U)
186  {
187  flags = m_pOther->m_Flags;
188  }
189  size_t slaveFlags = flags;
190  if (controlChars == 0)
191  {
192  controlChars = m_pOther->m_ControlChars;
193  }
194  const char *slaveControlChars = controlChars;
195 
196  size_t localWritten = 0;
197 
198  // Handle temios local modes
199  if (slaveFlags & (ConsoleManager::LCookedMode | ConsoleManager::LEcho))
200  {
201  // Whether or not the application buffer has already been filled
202  bool bAppBufferComplete = false;
203 
204  // Used for raw mode - just a buffer for erase echo etc
205  char *destBuff = new char[len];
206  size_t destBuffOffset = 0;
207 
208  // Iterate over the buffer
209  while (!bAppBufferComplete)
210  {
211  for (size_t i = 0; i < len; i++)
212  {
213  // Handle incoming newline
214  bool isCanonical = (slaveFlags & ConsoleManager::LCookedMode);
215  if (isCanonical && (buf[i] == slaveControlChars[VEOF]))
216  {
217  // EOF. Write it and it alone to the slave.
218  performInject(&buf[i], 1, true);
219  return;
220  }
221 
222  if ((buf[i] == '\r') ||
223  (isCanonical && (buf[i] == slaveControlChars[VEOL])))
224  {
225  // LEcho - output the newline. LCookedMode - handle line
226  // buffer.
227  if ((slaveFlags & ConsoleManager::LEcho) ||
228  (slaveFlags & ConsoleManager::LCookedMode))
229  {
230  // Only echo the newline if we are supposed to
231  m_LineBuffer[m_LineBufferSize++] = '\n';
232  if ((slaveFlags & ConsoleManager::LEchoNewline) ||
233  (slaveFlags & ConsoleManager::LEcho))
234  {
235  char tmp[] = {'\n', 0};
236  m_Buffer.write(tmp, 1);
237  ++localWritten;
238  }
239 
240  if ((slaveFlags & ConsoleManager::LCookedMode) &&
241  !bAppBufferComplete)
242  {
243  // Transmit full buffer to slave.
244  size_t realSize = m_LineBufferSize;
245  if (m_LineBufferFirstNewline < realSize)
246  {
247  realSize = m_LineBufferFirstNewline;
248  m_LineBufferFirstNewline = ~0UL;
249  }
250 
251  performInject(m_LineBuffer, realSize, true);
252 
253  // And now move the buffer over the space we just
254  // consumed
255  uint64_t nConsumedBytes =
256  m_LineBufferSize - realSize;
257  if (nConsumedBytes) // If zero, the buffer was
258  // consumed completely
259  MemoryCopy(
260  m_LineBuffer, &m_LineBuffer[realSize],
261  nConsumedBytes);
262 
263  // Reduce the buffer size now
264  m_LineBufferSize -= realSize;
265 
266  // The buffer has been filled!
267  bAppBufferComplete = true;
268  }
269  else if (
270  (slaveFlags & ConsoleManager::LCookedMode) &&
271  (m_LineBufferFirstNewline == ~0UL))
272  {
273  // Application buffer has already been filled, let
274  // future runs know where the limit is
275  m_LineBufferFirstNewline = m_LineBufferSize - 1;
276  }
277  else if (!(slaveFlags & ConsoleManager::LCookedMode))
278  {
279  // Inject this byte into the slave...
280  destBuff[destBuffOffset++] = buf[i];
281  }
282 
283  // Ignore the \n if one is present
284  if (buf[i + 1] == '\n')
285  i++;
286  }
287  }
288  else if (buf[i] == m_ControlChars[VERASE])
289  {
290  if (slaveFlags & (ConsoleManager::LCookedMode |
291  ConsoleManager::LEchoErase))
292  {
293  if ((slaveFlags & ConsoleManager::LCookedMode) &&
294  m_LineBufferSize)
295  {
296  char ctl[3] = {'\x08', ' ', '\x08'};
297  m_Buffer.write(ctl, 3);
298  m_LineBufferSize--;
299  ++localWritten;
300  }
301  else if (
302  (!(slaveFlags & ConsoleManager::LCookedMode)) &&
303  destBuffOffset)
304  {
305  char ctl[3] = {'\x08', ' ', '\x08'};
306  m_Buffer.write(ctl, 3);
307  destBuffOffset--;
308  ++localWritten;
309  }
310  }
311  }
312  else
313  {
314  // Do we need to handle this character differently?
315  if (checkForEvent(slaveFlags, buf[i], controlChars))
316  {
317  // So, normally we'll be fine to print nicely, but if
318  // we can't write to the ring buffer, we must not try
319  // to do so. This event may be necessary to unblock the
320  // buffer!
321  if (!m_Buffer.canWrite(false))
322  {
323  // Forcefully clear out bytes so we can write what
324  // we need to to the ring buffer.
325  WARNING("Console: dropping bytes to be able to "
326  "render visual control code (e.g. ^C)");
327  char tmp[3];
328  m_Buffer.read(tmp, 3);
329  }
330 
331  // Write it to the master nicely (eg, ^C, ^D)
332  char ctl_c = '@' + buf[i];
333  char ctl[3] = {'^', ctl_c, '\n'};
334  m_Buffer.write(ctl, 3);
335  ++localWritten;
336 
337  // Trigger the actual event.
338  triggerEvent(buf[i]);
339  continue;
340  }
341 
342  // Write the character to the slave
343  if (slaveFlags & ConsoleManager::LEcho)
344  {
345  m_Buffer.write(&buf[i], 1);
346  ++localWritten;
347  }
348 
349  // Add to the buffer
350  if (slaveFlags & ConsoleManager::LCookedMode)
351  m_LineBuffer[m_LineBufferSize++] = buf[i];
352  else
353  {
354  destBuff[destBuffOffset++] = buf[i];
355  }
356  }
357  }
358 
359  // We appear to have hit the top of the line buffer!
360  if (m_LineBufferSize >= LINEBUFFER_MAXIMUM)
361  {
362  // Our best bet is to return early, giving the application what
363  // we can of the line buffer
364  size_t numBytesToRemove = m_LineBufferSize;
365 
366  // Copy the buffer across
367  performInject(m_LineBuffer, numBytesToRemove, true);
368 
369  // And now move the buffer over the space we just consumed
370  uint64_t nConsumedBytes = m_LineBufferSize - numBytesToRemove;
371  if (nConsumedBytes) // If zero, the buffer was consumed
372  // completely
373  MemoryCopy(
374  m_LineBuffer, &m_LineBuffer[numBytesToRemove],
375  nConsumedBytes);
376 
377  // Reduce the buffer size now
378  m_LineBufferSize -= numBytesToRemove;
379  }
380 
382  break;
383  }
384 
385  if (destBuffOffset)
386  {
387  performInject(destBuff, len, true);
388  }
389 
390  delete[] destBuff;
391  }
392  else
393  {
394  for (size_t i = 0; i < len; ++i)
395  {
396  // Do we need to send an event?
397  if (checkForEvent(slaveFlags, buf[i], controlChars))
398  {
399  triggerEvent(buf[i]);
400  continue;
401  }
402 
403  // No event. Simply write the character out.
404  performInject(&buf[i], 1, true);
405  }
406  }
407 
408  // Wake up anything waiting on data to read from us.
409  if (localWritten)
410  dataChanged();
411 }
412 
414  size_t flags, char check, const char *controlChars)
415 {
416  // ISIG?
417  if (flags & ConsoleManager::LGenerateEvent)
418  {
419  if (check &&
420  (check == controlChars[VINTR] || check == controlChars[VQUIT] ||
421  check == controlChars[VSUSP]))
422  {
423  return true;
424  }
425  }
426  return false;
427 }
428 
430 {
431  if (m_pOther->m_pEvent)
432  {
433  Thread *pThread = Processor::information().getCurrentThread();
434  m_Last = cause;
435  pThread->sendEvent(m_pOther->m_pEvent);
437 
438  // Note that we do not release the mutex here.
439  while (!m_EventTrigger.acquire())
440  ;
441  }
442 }
443 
444 void ConsoleFile::performInject(char *buf, size_t len, bool canBlock)
445 {
446  m_pOther->inject(buf, len, canBlock);
447 }
448 
450 {
451  triggerEvent(cause);
452 }
static size_t outputLineDiscipline(char *buf, size_t len, size_t maxSz, size_t flags=0)
Output line discipline.
Definition: String.h:49
static ProcessorInformation & information()
Definition: Processor.cc:45
size_t processInput(char *buf, size_t len)
Input processing.
bool checkForEvent(size_t flags, char check, const char *controlChars)
Check if the given character requires an event.
#define WARNING(text)
Definition: Log.h:78
bool sendEvent(Event *pEvent)
Definition: Thread.cc:529
virtual void performEventTrigger(char cause)
virtual int select(bool bWriting, int timeout)
select - check and optionally for a particular state.
static Scheduler & instance()
Definition: Scheduler.h:48
void inputLineDiscipline(char *buf, size_t len, size_t flags=~0U, const char *controlChars=0)
Input line discipline.
void inject(char *buf, size_t len, bool canBlock)
inject - inject bytes into the ring buffer
void triggerEvent(char cause)
Triggers our event.
Definition: Thread.h:54
void yield()
Definition: Scheduler.cc:135
virtual void performInject(char *buf, size_t len, bool canBlock)
Definition: File.h:66