The Pedigree Project 0.1
console-syscalls.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 "pedigree/kernel/Subsystem.h"
21#include "pedigree/kernel/process/Process.h"
22#include "pedigree/kernel/processor/Processor.h"
23#include "pedigree/kernel/processor/types.h"
24#include "pedigree/kernel/syscallError.h"
25#include "pedigree/kernel/utilities/Tree.h"
26
27#include <FileDescriptor.h>
28#include <PosixProcess.h>
29#include <PosixSubsystem.h>
30#include <limits.h>
31#include <stddef.h>
32#include <termios.h>
33
34#include "TerminalControl.h"
35#include "console-syscalls.h"
36#include "file-syscalls.h"
37#include "logging.h"
38#include "modules/system/console/Console.h"
39#include "modules/system/vfs/File.h"
40#include "modules/system/vfs/VFS.h"
41#include <sys/ioctl.h>
42
43#define NCCS_COMPATIBLE 20
44
46 tcflag_t c_iflag;
47 tcflag_t c_oflag;
48 tcflag_t c_cflag;
49 tcflag_t c_lflag;
50 cc_t c_cc[NCCS_COMPATIBLE];
51 speed_t __c_ispeed;
52 speed_t __c_ospeed;
53};
54
55// TCGETS/TCSETS exchange the Linux kernel prefix, not musl's larger termios.
57 uint32_t c_iflag;
58 uint32_t c_oflag;
59 uint32_t c_cflag;
60 uint32_t c_lflag;
61 uint8_t c_line;
62 uint8_t c_cc[19];
63};
64
65static_assert(sizeof(LinuxAmd64Termios) == 36, "Linux amd64 termios extent");
66static_assert(offsetof(LinuxAmd64Termios, c_cc) == 17, "Linux amd64 control characters");
67
68int posix_tcgetattr(int fd, struct termios* p) {
69 auto* process = Processor::information().getCurrentThread()->getParent();
70 auto* subsystem = static_cast<PosixSubsystem*>(process->getSubsystem());
71 DescriptorLease descriptor;
72 if (!subsystem || !subsystem->acquireFileDescriptor(fd, descriptor)) {
73 SYSCALL_ERROR(BadFileDescriptor);
74 return -1;
75 }
76 return console_tcgetattr(descriptor, p);
77}
78
79int console_tcgetattr(const DescriptorLease& pFd, struct termios* p) {
80 F_NOTICE("posix_tcgetattr(" << pFd->fd << ")");
81
82 // Lookup this process.
83 Process* pProcess = Processor::information().getCurrentThread()->getParent();
84 PosixSubsystem* pSubsystem = static_cast<PosixSubsystem*>(pProcess->getSubsystem());
85 if (!pSubsystem) {
86 ERROR("No subsystem for one or both of the processes!");
87 return -1;
88 }
89
91 if (!pFd->acquireTerminalOperation(operation)) {
92 SYSCALL_ERROR(IoError);
93 return -1;
94 }
95
96 if (!ConsoleManager::instance().isConsole(pFd->getFile())) {
97 // Error - not a TTY.
98 SYSCALL_ERROR(NotAConsole);
99 F_NOTICE(" -> ENOTTY");
100 return -1;
101 }
102
103 termios_compatible attributes = {};
104 termios_compatible* pc = &attributes;
105
106 size_t flags;
107 ConsoleManager::instance().getAttributes(pFd->getFile(), &flags);
108
109 pc->c_iflag = ((flags & ConsoleManager::IMapNLToCR) ? INLCR : 0) |
110 ((flags & ConsoleManager::IMapCRToNL) ? ICRNL : 0) |
111 ((flags & ConsoleManager::IIgnoreCR) ? IGNCR : 0) |
112 ((flags & ConsoleManager::IStripToSevenBits) ? ISTRIP : 0);
113 pc->c_oflag = ((flags & ConsoleManager::OPostProcess) ? OPOST : 0) |
114 ((flags & ConsoleManager::OMapCRToNL) ? OCRNL : 0) |
115 ((flags & ConsoleManager::OMapNLToCRNL) ? ONLCR : 0) |
116 ((flags & ConsoleManager::ONLCausesCR) ? ONLRET : 0);
117 pc->c_cflag = CREAD | CS8 | HUPCL | B38400;
118 pc->c_lflag = ((flags & ConsoleManager::LEcho) ? ECHO : 0) |
119 ((flags & ConsoleManager::LEchoErase) ? ECHOE : 0) |
120 ((flags & ConsoleManager::LEchoKill) ? ECHOK : 0) |
121 ((flags & ConsoleManager::LEchoNewline) ? ECHONL : 0) |
122 ((flags & ConsoleManager::LCookedMode) ? ICANON : 0) |
123 ((flags & ConsoleManager::LGenerateEvent) ? ISIG : 0);
124
125 char controlChars[MAX_CONTROL_CHAR] = {0};
126 ConsoleManager::instance().getControlChars(pFd->getFile(), controlChars);
127
128 // c_cc is of type cc_t, but we don't want to expose that type to
129 // ConsoleManager. By doing this conversion, we can use whatever type we
130 // like in the kernel.
131 for (size_t i = 0; i < NCCS_COMPATIBLE; ++i)
132 pc->c_cc[i] = controlChars[i];
133
134 // ispeed/ospeed
135 pc->__c_ispeed = 115200;
136 pc->__c_ospeed = 115200;
137
138 bool copied;
139 if (pSubsystem->getAbi() == PosixSubsystem::LinuxAbi) {
140 LinuxAmd64Termios result = {};
141 result.c_iflag = pc->c_iflag;
142 result.c_oflag = pc->c_oflag;
143 result.c_cflag = pc->c_cflag;
144 result.c_lflag = pc->c_lflag;
145 for (size_t i = 0; i < sizeof(result.c_cc); ++i) {
146 result.c_cc[i] = pc->c_cc[i];
147 }
148 copied = PosixSubsystem::copyToUser(p, &result, sizeof(result));
149 } else {
150 // Retain the historical direct-POSIX payload for non-Linux callers.
151 copied = PosixSubsystem::copyToUser(p, &attributes, sizeof(attributes));
152 }
153 if (!copied) {
154 SYSCALL_ERROR(BadAddress);
155 return -1;
156 }
157
158 F_NOTICE("posix_tcgetattr returns");
159 F_NOTICE(" -> {c_iflag=" << pc->c_iflag << ", c_oflag=" << pc->c_oflag
160 << ", c_lflag=" << pc->c_lflag << "}");
161 F_NOTICE(" -> {c_cflag=" << pc->c_cflag << "}");
162 F_NOTICE(" -> {c_ispeed=" << pc->__c_ispeed << ", c_ospeed=" << pc->__c_ospeed << "}");
163 return 0;
164}
165
166int posix_tcsetattr(int fd, int optional_actions, struct termios* p) {
167 auto* process = Processor::information().getCurrentThread()->getParent();
168 auto* subsystem = static_cast<PosixSubsystem*>(process->getSubsystem());
169 DescriptorLease descriptor;
170 if (!subsystem || !subsystem->acquireFileDescriptor(fd, descriptor)) {
171 SYSCALL_ERROR(BadFileDescriptor);
172 return -1;
173 }
174 return console_tcsetattr(descriptor, optional_actions, p);
175}
176
177int console_tcsetattr(const DescriptorLease& pFd, int optional_actions, struct termios* p) {
178 // Lookup this process.
179 Process* pProcess = Processor::information().getCurrentThread()->getParent();
180 PosixSubsystem* pSubsystem = static_cast<PosixSubsystem*>(pProcess->getSubsystem());
181 if (!pSubsystem) {
182 ERROR("No subsystem for one or both of the processes!");
183 return -1;
184 }
185
186 termios_compatible attributes = {};
187 termios_compatible* pc = &attributes;
188 size_t controlCount = NCCS_COMPATIBLE;
189 if (pSubsystem->getAbi() == PosixSubsystem::LinuxAbi) {
190 LinuxAmd64Termios input = {};
191 if (!PosixSubsystem::copyFromUser(&input, p, sizeof(input))) {
192 SYSCALL_ERROR(BadAddress);
193 return -1;
194 }
195 pc->c_iflag = input.c_iflag;
196 pc->c_oflag = input.c_oflag;
197 pc->c_cflag = input.c_cflag;
198 pc->c_lflag = input.c_lflag;
199 controlCount = sizeof(input.c_cc);
200 for (size_t i = 0; i < controlCount; ++i) {
201 pc->c_cc[i] = input.c_cc[i];
202 }
203 } else if (!PosixSubsystem::copyFromUser(&attributes, p, sizeof(attributes))) {
204 SYSCALL_ERROR(BadAddress);
205 return -1;
206 }
207
208 F_NOTICE("posix_tcsetattr(" << pFd->fd << ", " << optional_actions << ")");
209 F_NOTICE(" -> {c_iflag=" << pc->c_iflag << ", c_oflag=" << pc->c_oflag
210 << ", c_lflag=" << pc->c_lflag << "}");
211 F_NOTICE(" -> {c_cflag=" << pc->c_cflag << "}");
212
214 if (!pFd->acquireTerminalOperation(operation)) {
215 SYSCALL_ERROR(IoError);
216 return -1;
217 }
218
219 if (!ConsoleManager::instance().isConsole(pFd->getFile())) {
220 // Error - not a TTY.
221 SYSCALL_ERROR(NotAConsole);
222 F_NOTICE(" -> ENOTTY");
223 return -1;
224 }
225
226 size_t flags = 0;
227 if (pc->c_iflag & INLCR)
228 flags |= ConsoleManager::IMapNLToCR;
229 if (pc->c_iflag & ICRNL)
230 flags |= ConsoleManager::IMapCRToNL;
231 if (pc->c_iflag & IGNCR)
232 flags |= ConsoleManager::IIgnoreCR;
233 if (pc->c_iflag & ISTRIP)
234 flags |= ConsoleManager::IStripToSevenBits;
235 if (pc->c_oflag & OPOST)
236 flags |= ConsoleManager::OPostProcess;
237 if (pc->c_oflag & OCRNL)
238 flags |= ConsoleManager::OMapCRToNL;
239 if (pc->c_oflag & ONLCR)
240 flags |= ConsoleManager::OMapNLToCRNL;
241 if (pc->c_oflag & ONLRET)
242 flags |= ConsoleManager::ONLCausesCR;
243 if (pc->c_lflag & ECHO)
244 flags |= ConsoleManager::LEcho;
245 if (pc->c_lflag & ECHOE)
246 flags |= ConsoleManager::LEchoErase;
247 if (pc->c_lflag & ECHOK)
248 flags |= ConsoleManager::LEchoKill;
249 if (pc->c_lflag & ECHONL)
250 flags |= ConsoleManager::LEchoNewline;
251 if (pc->c_lflag & ICANON)
252 flags |= ConsoleManager::LCookedMode;
253 if (pc->c_lflag & ISIG)
254 flags |= ConsoleManager::LGenerateEvent;
255 F_NOTICE("TCSETATTR: " << Hex << flags);
257 ConsoleManager::instance().setAttributes(pFd->getFile(), flags);
258
259 char controlChars[MAX_CONTROL_CHAR] = {0};
260 for (size_t i = 0; i < controlCount; ++i)
261 controlChars[i] = pc->c_cc[i];
262 ConsoleManager::instance().setControlChars(pFd->getFile(), controlChars);
263
264 return 0;
265}
266
267int console_getwinsize(File* file, struct winsize* buf) {
268 if (!ConsoleManager::instance().isConsole(file)) {
269 // Error - not a TTY.
270 return -1;
271 }
272
273 return ConsoleManager::instance().getWindowSize(file, &buf->ws_row, &buf->ws_col);
274}
275
276int console_setwinsize(File* file, const struct winsize* buf) {
277 if (!ConsoleManager::instance().isConsole(file)) {
278 // Error - not a TTY.
279 return -1;
280 }
281
283 return ConsoleManager::instance().setWindowSize(file, buf->ws_row, buf->ws_col);
284}
285
286int console_flush(File* file, int selector) {
287 if (!ConsoleManager::instance().isConsole(file)) {
288 // Error - not a TTY.
289 return -1;
290 }
291
292 if (selector != TCIFLUSH && selector != TCOFLUSH && selector != TCIOFLUSH) {
293 SYSCALL_ERROR(InvalidArgument);
294 return -1;
295 }
296 ConsoleManager::instance().flush(file);
297 return 0;
298}
299
300int console_ptsname(int fd, char* buf) {
301 // Lookup this process.
302 Process* pProcess = Processor::information().getCurrentThread()->getParent();
303 PosixSubsystem* pSubsystem = static_cast<PosixSubsystem*>(pProcess->getSubsystem());
304 if (!pSubsystem) {
305 ERROR("No subsystem for one or both of the processes!");
306 return -1;
307 }
308
309 DescriptorLease pFd;
310 if (!pSubsystem->acquireFileDescriptor(fd, pFd)) {
311 // Error - no such file descriptor.
312 SYSCALL_ERROR(BadFileDescriptor);
313 return -1;
314 }
315
316 if (!ConsoleManager::instance().isConsole(pFd->getFile())) {
317 // Error - not a TTY.
318 SYSCALL_ERROR(NotAConsole);
319 return -1;
320 }
321
322 File* slave = pFd->getFile();
323 if (ConsoleManager::instance().isMasterConsole(slave)) {
324 slave = ConsoleManager::instance().getOther(pFd->getFile());
325 } else {
326 return -1;
327 }
328
329 String path("/dev/");
330 path += slave->getName();
331 if (!PosixSubsystem::copyToUser(buf, path.cstr(), path.length() + 1)) {
332 SYSCALL_ERROR(BadAddress);
333 return -1;
334 }
335
336 F_NOTICE("ptsname(" << fd << ") -> " << path);
337 return 0;
338}
339
340int console_ttyname(int fd, char* buf) {
341 // Lookup this process.
342 Process* pProcess = Processor::information().getCurrentThread()->getParent();
343 PosixSubsystem* pSubsystem = static_cast<PosixSubsystem*>(pProcess->getSubsystem());
344 if (!pSubsystem) {
345 ERROR("No subsystem for one or both of the processes!");
346 return -1;
347 }
348
349 DescriptorLease pFd;
350 if (!pSubsystem->acquireFileDescriptor(fd, pFd)) {
351 // Error - no such file descriptor.
352 SYSCALL_ERROR(BadFileDescriptor);
353 return -1;
354 }
355
356 if (!ConsoleManager::instance().isConsole(pFd->getFile())) {
357 // Error - not a TTY.
358 SYSCALL_ERROR(NotAConsole);
359 return -1;
360 }
361
362 auto* tty = static_cast<ConsoleFile*>(pFd->getFile());
363 String path(tty->isPtySlave() ? "/dev/pts/" : "/dev/");
364 path += tty->getName();
365 if (!PosixSubsystem::copyToUser(buf, path.cstr(), path.length() + 1)) {
366 SYSCALL_ERROR(BadAddress);
367 return -1;
368 }
369
370 F_NOTICE("ttyname(" << fd << ") -> " << path);
371 return 0;
372}
373
374int console_setctty(File* file, bool steal) {
375 if (!ConsoleManager::instance().isConsole(file)) {
376 SYSCALL_ERROR(NotAConsole);
377 return -1;
378 }
379 return TerminalControl::attach(*static_cast<ConsoleFile*>(file), steal);
380}
381
382int console_setctty(int fd, bool steal) {
383 auto* process = Processor::information().getCurrentThread()->getParent();
384 auto* subsystem = static_cast<PosixSubsystem*>(process->getSubsystem());
385 DescriptorLease descriptor;
386 if (!subsystem || !subsystem->acquireFileDescriptor(fd, descriptor)) {
387 SYSCALL_ERROR(BadFileDescriptor);
388 return -1;
389 }
390 if (!ConsoleManager::instance().isConsole(descriptor->getFile())) {
391 SYSCALL_ERROR(NotAConsole);
392 return -1;
393 }
394 return TerminalControl::attach(*static_cast<ConsoleFile*>(descriptor->getFile()), steal, false,
395 descriptor->terminalEpoch());
396}
397
398int posix_tcsetpgrp(int fd, pid_t group) {
399 auto* process = Processor::information().getCurrentThread()->getParent();
400 auto* subsystem = static_cast<PosixSubsystem*>(process->getSubsystem());
401 DescriptorLease descriptor;
402 if (!subsystem || !subsystem->acquireFileDescriptor(fd, descriptor)) {
403 SYSCALL_ERROR(BadFileDescriptor);
404 return -1;
405 }
406 if (!ConsoleManager::instance().isConsole(descriptor->getFile())) {
407 SYSCALL_ERROR(NotAConsole);
408 return -1;
409 }
410 return TerminalControl::setForeground(*static_cast<ConsoleFile*>(descriptor->getFile()), group,
411 descriptor->terminalEpoch());
412}
413
414pid_t posix_tcgetpgrp(int fd) {
415 auto* process = Processor::information().getCurrentThread()->getParent();
416 auto* subsystem = static_cast<PosixSubsystem*>(process->getSubsystem());
417 DescriptorLease descriptor;
418 if (!subsystem || !subsystem->acquireFileDescriptor(fd, descriptor)) {
419 SYSCALL_ERROR(BadFileDescriptor);
420 return -1;
421 }
422 if (!ConsoleManager::instance().isConsole(descriptor->getFile())) {
423 SYSCALL_ERROR(NotAConsole);
424 return -1;
425 }
426 return TerminalControl::foreground(*static_cast<ConsoleFile*>(descriptor->getFile()),
427 descriptor->terminalEpoch());
428}
429
430unsigned int console_getptn(int fd) {
431 F_NOTICE("console_getptn(" << fd << ")");
432
433 Process* pProcess = Processor::information().getCurrentThread()->getParent();
434 PosixSubsystem* pSubsystem = static_cast<PosixSubsystem*>(pProcess->getSubsystem());
435 if (!pSubsystem) {
436 ERROR("No subsystem for one or both of the processes!");
437 return ~0U;
438 }
439
440 DescriptorLease pFd;
441 if (!pSubsystem->acquireFileDescriptor(fd, pFd)) {
442 // Error - no such file descriptor.
443 SYSCALL_ERROR(BadFileDescriptor);
444 F_NOTICE(" -> EBADF");
445 return ~0U;
446 }
447
448 if (!ConsoleManager::instance().isConsole(pFd->getFile())) {
449 SYSCALL_ERROR(NotAConsole);
450 F_NOTICE(" -> not a console!");
451 return ~0U;
452 }
453
454 ConsoleFile* pConsole = static_cast<ConsoleFile*>(pFd->getFile());
455 size_t result = pConsole->getConsoleNumber();
456 if (result == ~0U) {
457 // special case, it's a Console attached to a physical terminal instead
458 // of a pseudoterminal
459 SYSCALL_ERROR(NotAConsole);
460 F_NOTICE(" -> unknown console number!");
461 return ~0U;
462 }
463 F_NOTICE(" -> " << result);
464 return result;
465}
size_t fd
Descriptor number.
Definition File.h:75
String getName() const
Definition File.cc:782
bool acquireFileDescriptor(size_t fd, DescriptorLease &descriptor)
static bool copyFromUser(void *destination, const void *source, size_t count, size_t elementSize=1)
Abi getAbi() const
static bool copyToUser(void *destination, const void *source, size_t count, size_t elementSize=1)
Process * getParent()
Definition Process.h:620
static ProcessorInformation & information()
@ Hex
Definition Log.h:124