Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <Kernel/API/Ioctl.h>
8#include <Kernel/API/POSIX/errno.h>
9#include <Kernel/API/POSIX/signal_numbers.h>
10#include <Kernel/Debug.h>
11#include <Kernel/InterruptDisabler.h>
12#include <Kernel/Process.h>
13#include <Kernel/TTY/MasterPTY.h>
14#include <Kernel/TTY/PTYMultiplexer.h>
15#include <Kernel/TTY/SlavePTY.h>
16
17namespace Kernel {
18
19ErrorOr<NonnullLockRefPtr<MasterPTY>> MasterPTY::try_create(unsigned int index)
20{
21 auto buffer = TRY(DoubleBuffer::try_create("MasterPTY: Buffer"sv));
22 auto master_pty = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) MasterPTY(index, move(buffer))));
23 auto slave_pty = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) SlavePTY(*master_pty, index)));
24 master_pty->m_slave = slave_pty;
25 TRY(master_pty->after_inserting());
26 TRY(slave_pty->after_inserting());
27 return master_pty;
28}
29
30MasterPTY::MasterPTY(unsigned index, NonnullOwnPtr<DoubleBuffer> buffer)
31 : CharacterDevice(200, index)
32 , m_index(index)
33 , m_buffer(move(buffer))
34{
35 auto& process = Process::current();
36 auto credentials = process.credentials();
37 set_uid(credentials->uid());
38 set_gid(credentials->gid());
39
40 m_buffer->set_unblock_callback([this]() {
41 if (m_slave)
42 evaluate_block_conditions();
43 });
44}
45
46MasterPTY::~MasterPTY()
47{
48 dbgln_if(MASTERPTY_DEBUG, "~MasterPTY({})", m_index);
49 PTYMultiplexer::the().notify_master_destroyed({}, m_index);
50}
51
52ErrorOr<size_t> MasterPTY::read(OpenFileDescription&, u64, UserOrKernelBuffer& buffer, size_t size)
53{
54 if (!m_slave && m_buffer->is_empty())
55 return 0;
56 return m_buffer->read(buffer, size);
57}
58
59ErrorOr<size_t> MasterPTY::write(OpenFileDescription&, u64, UserOrKernelBuffer const& buffer, size_t size)
60{
61 if (!m_slave)
62 return EIO;
63 m_slave->on_master_write(buffer, size);
64 return size;
65}
66
67bool MasterPTY::can_read(OpenFileDescription const&, u64) const
68{
69 if (!m_slave)
70 return true;
71 return !m_buffer->is_empty();
72}
73
74bool MasterPTY::can_write(OpenFileDescription const&, u64) const
75{
76 return true;
77}
78
79void MasterPTY::notify_slave_closed(Badge<SlavePTY>)
80{
81 dbgln_if(MASTERPTY_DEBUG, "MasterPTY({}): slave closed, my retains: {}, slave retains: {}", m_index, ref_count(), m_slave->ref_count());
82 // +1 ref for my MasterPTY::m_slave
83 // +1 ref for OpenFileDescription::m_device
84 if (m_slave->ref_count() == 2)
85 m_slave = nullptr;
86}
87
88ErrorOr<size_t> MasterPTY::on_slave_write(UserOrKernelBuffer const& data, size_t size)
89{
90 if (m_closed)
91 return EIO;
92 return m_buffer->write(data, size);
93}
94
95bool MasterPTY::can_write_from_slave() const
96{
97 if (m_closed)
98 return true;
99 return m_buffer->space_for_writing();
100}
101
102ErrorOr<void> MasterPTY::close()
103{
104 InterruptDisabler disabler;
105 // After the closing OpenFileDescription dies, slave is the only thing keeping me alive.
106 // From this point, let's consider ourselves closed.
107 m_closed = true;
108
109 if (m_slave)
110 m_slave->hang_up();
111
112 return {};
113}
114
115ErrorOr<void> MasterPTY::ioctl(OpenFileDescription& description, unsigned request, Userspace<void*> arg)
116{
117 TRY(Process::current().require_promise(Pledge::tty));
118 if (!m_slave)
119 return EIO;
120 switch (request) {
121 case TIOCGPTN: {
122 int master_pty_index = index();
123 return copy_to_user(static_ptr_cast<int*>(arg), &master_pty_index);
124 }
125 case TIOCSWINSZ:
126 case TIOCGPGRP:
127 return m_slave->ioctl(description, request, arg);
128 default:
129 return EINVAL;
130 }
131}
132
133ErrorOr<NonnullOwnPtr<KString>> MasterPTY::pseudo_path(OpenFileDescription const&) const
134{
135 return KString::formatted("ptm:{}", m_index);
136}
137
138}