summaryrefslogtreecommitdiffstatshomepage
path: root/src/osd/modules/file/posixptty.cpp
blob: b84838c0578da50638aa8ebfd1bc347ce8a1c7d7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert, R. Belmont, Vas Crabb
//============================================================
//
//  sdlptty_unix.c - SDL pseudo tty access functions
//
//  SDLMAME by Olivier Galibert and R. Belmont
//
//============================================================

#include "posixfile.h"

#include <cassert>
#include <cerrno>
#include <cstring>

#include <fcntl.h>
#include <limits.h>
#include <unistd.h>
#include <stdlib.h>

#if defined(__FreeBSD__) || defined(__DragonFly__)
#include <termios.h>
#include <libutil.h>
#elif defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
#include <termios.h>
#include <util.h>
#elif defined(__linux__) || defined(EMSCRIPTEN)
#include <pty.h>
#elif defined(__HAIKU__)
#include <bsd/pty.h>
#endif


namespace {
#if defined(__APPLE__)
char const *const posix_ptty_identifier  = "/dev/pty";
#else
char const *const posix_ptty_identifier  = "/dev/pts";
#endif


class posix_osd_ptty : public osd_file
{
public:
	posix_osd_ptty(posix_osd_ptty const &) = delete;
	posix_osd_ptty(posix_osd_ptty &&) = delete;
	posix_osd_ptty& operator=(posix_osd_ptty const &) = delete;
	posix_osd_ptty& operator=(posix_osd_ptty &&) = delete;

	posix_osd_ptty(int fd) : m_fd(fd)
	{
		assert(m_fd >= 0);
	}

	virtual ~posix_osd_ptty()
	{
		::close(m_fd);
	}

	virtual error read(void *buffer, std::uint64_t offset, std::uint32_t count, std::uint32_t &actual) override
	{
		ssize_t const result = ::read(m_fd, buffer, count);
		if (result < 0)
			return errno_to_file_error(errno);

		actual = std::uint32_t(size_t(result));
		return error::NONE;
	}

	virtual error write(void const *buffer, std::uint64_t offset, std::uint32_t count, std::uint32_t &actual) override
	{
		ssize_t const result = ::write(m_fd, buffer, count);
		if (result < 0)
			return errno_to_file_error(errno);

		actual = std::uint32_t(size_t(result));
		return error::NONE;
	}

	virtual error truncate(std::uint64_t offset) override
	{
		// doesn't make sense on ptty
		return error::INVALID_ACCESS;
	}

	virtual error flush() override
	{
		// no userspace buffers on read/write
		return error::NONE;
	}

private:
	int m_fd;
};

} // anonymous namespace


bool posix_check_ptty_path(std::string const &path)
{
	return strncmp(path.c_str(), posix_ptty_identifier, strlen(posix_ptty_identifier)) == 0;
}


osd_file::error posix_open_ptty(std::uint32_t openflags, osd_file::ptr &file, std::uint64_t &filesize, std::string &name)
{
#if (defined(sun) || defined(__sun)) && (defined(__SVR4) || defined(__svr4__))
	int access = O_NOCTTY;
	if (openflags & OPEN_FLAG_WRITE)
		access |= (openflags & OPEN_FLAG_READ) ? O_RDWR : O_WRONLY;
	else if (openflags & OPEN_FLAG_READ)
		access |= O_RDONLY;
	else
		return error::INVALID_ACCESS;

	int const masterfd = ::posix_openpt(access);
	if (masterfd < 0)
		return errno_to_file_error(errno);

	// grant access to slave device and check that it can be opened
	char const *slavepath;
	int slavefd;
	if ((::grantpt(masterfd) < 0) ||
		(::unlockpt(masterfd) < 0) ||
		((slavepath = ::ptsname(masterfd)) == nullptr) ||
		((slavefd = ::open(slavepath, O_RDWR | O_NOCTTY)) < 0))
	{
		int const err = errno;
		::close(masterfd);
		return errno_to_file_error(err);
	}

	// check that it's possible to stack BSD-compatibility STREAMS modules
	if ((::ioctl(slavefd, I_PUSH, "ptem") < 0) ||
		(::ioctl(slavefd, I_PUSH, "ldterm") < 0) ||
		(::ioctl(slavefd, I_PUSH, "ttcompat") < 0))
	{
		int const err = errno;
		::close(slavefd);
		::close(masterfd);
		return errno_to_file_error(err);
	}
#else
	struct termios tios;
	std::memset(&tios, 0, sizeof(tios));
	::cfmakeraw(&tios);

	int masterfd = -1, slavefd = -1;
	char slavepath[PATH_MAX];
	if (::openpty(&masterfd, &slavefd, slavepath, &tios, nullptr) < 0)
		return errno_to_file_error(errno);
#endif

	::close(slavefd);

	int const oldflags = ::fcntl(masterfd, F_GETFL, 0);
	if (oldflags < 0)
	{
		int const err = errno;
		::close(masterfd);
		return errno_to_file_error(err);
	}

	if (::fcntl(masterfd, F_SETFL, oldflags | O_NONBLOCK) < 0)
	{
		int const err = errno;
		::close(masterfd);
		return errno_to_file_error(err);
	}

	try
	{
		name = slavepath;
		file = std::make_unique<posix_osd_ptty>(masterfd);
		filesize = 0;
		return osd_file::error::NONE;
	}
	catch (...)
	{
		::close(masterfd);
		return osd_file::error::OUT_OF_MEMORY;
	}
}