The Pedigree Project  0.1
VirtualTerminal.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 "modules/subsys/posix/VirtualTerminal.h"
21 #include "modules/subsys/posix/DevFs.h"
22 #include "modules/subsys/posix/PosixSubsystem.h"
23 #include "modules/system/console/Console.h"
24 #include "pedigree/kernel/process/Process.h"
25 
26 extern DevFs *g_pDevFs;
27 
28 VirtualTerminalManager::VirtualTerminalManager(DevFsDirectory *parentDir)
29  : m_pTty(nullptr), m_CurrentTty(0), m_WantedTty(~0U), m_NumTtys(0),
30  m_ParentDir(parentDir), m_bSwitchingLocked(false), m_SystemMode(Text)
31 {
32  for (size_t i = 0; i < MAX_VT; ++i)
33  {
34  m_Terminals[i].textio = nullptr;
35  m_Terminals[i].file = nullptr;
36 #ifdef THREADS
37  m_Terminals[i].owner = nullptr;
38 #endif
39 
40  ByteSet(&m_Terminals[i].mode, 0, sizeof(m_Terminals[i].mode));
41  m_Terminals[i].mode.mode = VT_AUTO;
42  }
43 }
44 
45 VirtualTerminalManager::~VirtualTerminalManager()
46 {
47  //
48 }
49 
50 bool VirtualTerminalManager::initialise()
51 {
52  // Create /dev/textui for the text-only UI device.
53  m_pTty = new TextIO(
54  String("textui"), g_pDevFs->getNextInode(), g_pDevFs, m_ParentDir);
55  m_pTty->markPrimary();
56  if (m_pTty->initialise(false))
57  {
58  m_ParentDir->addEntry(m_pTty->getName(), m_pTty);
59  }
60  else
61  {
62  WARNING("POSIX: no /dev/tty - VirtualTerminalManager failed to "
63  "initialise.");
64  g_pDevFs->revertInode();
65  delete m_pTty;
66  m_pTty = nullptr;
67 
68  return false;
69  }
70 
71  // set up tty1
72  ConsolePhysicalFile *pTty1 =
73  new ConsolePhysicalFile(0, m_pTty, String("tty1"), g_pDevFs);
74  m_ParentDir->addEntry(pTty1->getName(), pTty1);
75 
76  m_Terminals[0].textio = m_pTty;
77  m_Terminals[0].file = pTty1;
78 
79  // create tty2-6 as non-overloaded TextIO instances
80  for (size_t i = 1; i < 8; ++i)
81  {
82  String ttyname;
83  ttyname.Format("tty%u", i + 1);
84 
85  TextIO *tio = new TextIO(
86  ttyname, g_pDevFs->getNextInode(), g_pDevFs, m_ParentDir);
87  if (tio->initialise(true))
88  {
89  ConsolePhysicalFile *file =
90  new ConsolePhysicalFile(i + 1, tio, ttyname, g_pDevFs);
91  m_ParentDir->addEntry(tio->getName(), file);
92 
93  m_Terminals[i].textio = tio;
94  m_Terminals[i].file = file;
95 
96  // activate the terminal by performing an empty write, which will
97  // ensure users switching to the terminal see a blank screen if
98  // nothing has actually opened it - this is better than seeing the
99  // previous tty's output...
100  tio->writeStr("", 0);
101  }
102  else
103  {
104  WARNING("POSIX: failed to create " << ttyname);
105  g_pDevFs->revertInode();
106  delete tio;
107  }
108  }
109 
110  return true;
111 }
112 
114 {
115  if (n >= MAX_VT)
116  {
117  ERROR("VirtualTerminalManager: trying to activate invalid VT #" << n);
118  return;
119  }
120  else if (n == m_CurrentTty)
121  {
122  ERROR("VirtualTerminalManager: trying to activate current VT");
123  return;
124  }
125 
126  if (m_bSwitchingLocked)
127  {
128  ERROR("VirtualTerminalManager: switching is currently locked");
129  return;
130  }
131 
132  struct vt_mode currentMode = getTerminalMode(m_CurrentTty);
133 
134  if (currentMode.mode == VT_AUTO)
135  {
136  NOTICE("VirtualTerminalManager: switching from auto VT");
137 
138  // Easy transfer.
139  m_Terminals[m_CurrentTty].textio->unmarkPrimary();
140  m_CurrentTty = n;
141  m_Terminals[m_CurrentTty].textio->markPrimary();
142 
143  // acquiring the new terminal
144  sendSignal(m_CurrentTty, true);
145  }
146  else
147  {
148  NOTICE("VirtualTerminalManager: switching from owned VT");
149 
150  m_WantedTty = n;
151 
152  // we want to release the current terminal
153  sendSignal(m_CurrentTty, false);
154  }
155 }
156 
157 void VirtualTerminalManager::reportPermission(SwitchPermission perm)
158 {
159  if (m_WantedTty == static_cast<size_t>(~0))
160  {
161  // No switch in progress.
162  NOTICE("VirtualTerminalManager: can't acknowledge as no switch in "
163  "progress");
164  return;
165  }
166 
167  if (perm == Disallowed)
168  {
169  // abort the switch
170  NOTICE("VirtualTerminalManager: VT switch disallowed");
171  m_WantedTty = static_cast<size_t>(~0U);
172  return;
173  }
174 
175  NOTICE("VirtualTerminalManager: VT switch allowed");
176 
177  // OK to switch!
178  m_Terminals[m_CurrentTty].textio->unmarkPrimary();
179  m_CurrentTty = m_WantedTty;
180  m_Terminals[m_CurrentTty].textio->markPrimary();
181 
182  // acquiring the new terminal (acquire signal)
183  sendSignal(m_CurrentTty, true);
184 }
185 
187 {
188  for (size_t i = 0; i < MAX_VT; ++i)
189  {
190  if (m_Terminals[i].textio == nullptr)
191  {
192  NOTICE("VirtualTerminalManager: opening inactive VT #" << i);
193 
194  String ttyname;
195  ttyname.Format("tty%u", i + 1);
196 
197  TextIO *tio = new TextIO(
198  ttyname, g_pDevFs->getNextInode(), g_pDevFs, m_ParentDir);
199  if (tio->initialise(true))
200  {
201  ConsolePhysicalFile *file =
202  new ConsolePhysicalFile(i, tio, ttyname, g_pDevFs);
203  m_ParentDir->addEntry(tio->getName(), file);
204 
205  m_Terminals[i].textio = tio;
206  m_Terminals[i].file = file;
207 
208  // activate the terminal by performing an empty write, which
209  // will ensure users switching to the terminal see a blank
210  // screen if nothing has actually opened it - this is better
211  // than seeing the previous tty's output...
212  tio->writeStr("", 0);
213 
214  return i;
215  }
216  else
217  {
218  WARNING("POSIX: failed to create " << ttyname);
219  g_pDevFs->revertInode();
220  delete tio;
221  }
222  }
223  }
224 
225  return ~0;
226 }
227 
229 {
230  m_bSwitchingLocked = locked;
231 }
232 
233 size_t VirtualTerminalManager::getCurrentTerminalNumber() const
234 {
235  return m_CurrentTty;
236 }
237 
238 TextIO *VirtualTerminalManager::getCurrentTerminal() const
239 {
240  return m_Terminals[m_CurrentTty].textio;
241 }
242 
243 File *VirtualTerminalManager::getCurrentTerminalFile() const
244 {
245  return m_Terminals[m_CurrentTty].file;
246 }
247 
248 struct vt_mode VirtualTerminalManager::getTerminalMode(size_t n) const
249 {
251  NOTICE("getTerminalMode #" << n);
252  return m_Terminals[n].mode;
253 }
254 
256 {
258  NOTICE("setTerminalMode #" << n);
259  m_Terminals[n].mode = mode;
260 
261 #ifdef THREADS
262  if (mode.mode == VT_PROCESS)
263  {
264  m_Terminals[n].owner =
265  Processor::information().getCurrentThread()->getParent();
266  }
267  else
268  {
269  m_Terminals[n].owner = nullptr;
270  }
271 #endif
272 }
273 
274 struct vt_stat VirtualTerminalManager::getState() const
275 {
276  struct vt_stat state;
277 
278  state.v_active = m_CurrentTty + 1;
279  state.v_signal = 0;
280  state.v_state = 1; // VT 0 == current, always
281  for (size_t i = 0; i < MAX_VT; ++i)
282  {
283  if (m_Terminals[i].textio != nullptr)
284  {
285  state.v_state |= (1 << (i + 1));
286  }
287  }
288 
289  NOTICE("getState:");
290  NOTICE(" -> active = " << state.v_active);
291  NOTICE(" -> state = " << Hex << state.v_state);
292 
293  return state;
294 }
295 
296 void VirtualTerminalManager::setSystemMode(SystemMode mode)
297 {
298  m_SystemMode = mode;
299 }
300 
301 VirtualTerminalManager::SystemMode VirtualTerminalManager::getSystemMode() const
302 {
303  return m_SystemMode;
304 }
305 
306 void VirtualTerminalManager::setInputMode(size_t n, TextIO::InputMode newMode)
307 {
308  if (!m_Terminals[n].textio)
309  {
310  NOTICE(
311  "VirtualTerminalManager: can't set mode of VT #"
312  << n << " as it is inactive");
313  return;
314  }
315 
316  m_Terminals[n].textio->setMode(newMode);
317 }
318 
319 TextIO::InputMode VirtualTerminalManager::getInputMode(size_t n) const
320 {
321  if (!m_Terminals[n].textio)
322  {
323  NOTICE(
324  "VirtualTerminalManager: can't get mode of VT #"
325  << n << " as it is inactive");
326  return TextIO::Standard;
327  }
328 
329  return m_Terminals[n].textio->getMode();
330 }
331 
332 void VirtualTerminalManager::sendSignal(size_t n, bool acq)
333 {
334  if (!m_Terminals[n].textio)
335  {
336  NOTICE(
337  "VirtualTerminalManager: can't send signal to VT #"
338  << n << " as it is inactive");
339  return;
340  }
341 
342  auto mode = getTerminalMode(n);
343  if (mode.mode != VT_PROCESS)
344  {
345  NOTICE(
346  "VirtualTerminalManager: can't send signal to VT #"
347  << n << " as it is not owned");
348  return;
349  }
350 
351 #ifdef THREADS
352  Process *pProcess = m_Terminals[n].owner;
353  PosixSubsystem *pSubsystem =
354  reinterpret_cast<PosixSubsystem *>(pProcess->getSubsystem());
355  if (!pSubsystem)
356  {
357  ERROR("VirtualTerminal::sendSignal: no subsystem");
358  return;
359  }
360 
361  NOTICE("VirtualTerminalManager: signaling VT #" << n);
362  pSubsystem->sendSignal(
363  pProcess->getThread(0), acq ? mode.acqsig : mode.relsig);
364 #endif
365 }
void activate(size_t n)
Starts the process of activating the given tty.
void reportPermission(SwitchPermission perm)
void lockSwitching(bool locked)
void writeStr(const char *s, size_t len)
Definition: TextIO.cc:177
Definition: String.h:49
static ProcessorInformation & information()
Definition: Processor.cc:45
Definition: TextIO.h:48
#define WARNING(text)
Definition: Log.h:78
bool initialise(bool bClear=true)
Definition: TextIO.cc:104
#define NOTICE(text)
Definition: Log.h:74
Definition: Log.h:136
void setTerminalMode(size_t n, struct vt_mode mode)
Thread * getThread(size_t n)
Definition: Process.cc:225
String getName() const
Definition: File.cc:411
Definition: DevFs.h:287
#define ERROR(text)
Definition: Log.h:82
virtual void sendSignal(Thread *pThread, int signal, bool yield=true)
void markPrimary()
Definition: TextIO.cc:2102
Definition: File.h:66