1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
4 */
5
6#include <stdio.h>
7#include <stdlib.h>
8#include <unistd.h>
9#include <errno.h>
10#include <fcntl.h>
11#include <string.h>
12#include <termios.h>
13#include <sys/stat.h>
14#include "chan_user.h"
15#include <os.h>
16#include <um_malloc.h>
17
18struct pty_chan {
19	void (*announce)(char *dev_name, int dev);
20	int dev;
21	int raw;
22	struct termios tt;
23	char dev_name[sizeof("/dev/pts/0123456\0")];
24};
25
26static void *pty_chan_init(char *str, int device, const struct chan_opts *opts)
27{
28	struct pty_chan *data;
29
30	data = uml_kmalloc(sizeof(*data), UM_GFP_KERNEL);
31	if (data == NULL)
32		return NULL;
33
34	*data = ((struct pty_chan) { .announce  	= opts->announce,
35				     .dev  		= device,
36				     .raw  		= opts->raw });
37	return data;
38}
39
40static int pts_open(int input, int output, int primary, void *d,
41		    char **dev_out)
42{
43	struct pty_chan *data = d;
44	char *dev;
45	int fd, err;
46
47	fd = get_pty();
48	if (fd < 0) {
49		err = -errno;
50		printk(UM_KERN_ERR "open_pts : Failed to open pts\n");
51		return err;
52	}
53
54	if (data->raw) {
55		CATCH_EINTR(err = tcgetattr(fd, &data->tt));
56		if (err)
57			goto out_close;
58
59		err = raw(fd);
60		if (err)
61			goto out_close;
62	}
63
64	dev = ptsname(fd);
65	sprintf(data->dev_name, "%s", dev);
66	*dev_out = data->dev_name;
67
68	if (data->announce)
69		(*data->announce)(dev, data->dev);
70
71	return fd;
72
73out_close:
74	close(fd);
75	return err;
76}
77
78static int getmaster(char *line)
79{
80	struct stat buf;
81	char *pty, *bank, *cp;
82	int master, err;
83
84	pty = &line[strlen("/dev/ptyp")];
85	for (bank = "pqrs"; *bank; bank++) {
86		line[strlen("/dev/pty")] = *bank;
87		*pty = '0';
88		/* Did we hit the end ? */
89		if ((stat(line, &buf) < 0) && (errno == ENOENT))
90			break;
91
92		for (cp = "0123456789abcdef"; *cp; cp++) {
93			*pty = *cp;
94			master = open(line, O_RDWR);
95			if (master >= 0) {
96				char *tp = &line[strlen("/dev/")];
97
98				/* verify slave side is usable */
99				*tp = 't';
100				err = access(line, R_OK | W_OK);
101				*tp = 'p';
102				if (!err)
103					return master;
104				close(master);
105			}
106		}
107	}
108
109	printk(UM_KERN_ERR "getmaster - no usable host pty devices\n");
110	return -ENOENT;
111}
112
113static int pty_open(int input, int output, int primary, void *d,
114		    char **dev_out)
115{
116	struct pty_chan *data = d;
117	int fd, err;
118	char dev[sizeof("/dev/ptyxx\0")] = "/dev/ptyxx";
119
120	fd = getmaster(dev);
121	if (fd < 0)
122		return fd;
123
124	if (data->raw) {
125		err = raw(fd);
126		if (err) {
127			close(fd);
128			return err;
129		}
130	}
131
132	if (data->announce)
133		(*data->announce)(dev, data->dev);
134
135	sprintf(data->dev_name, "%s", dev);
136	*dev_out = data->dev_name;
137
138	return fd;
139}
140
141const struct chan_ops pty_ops = {
142	.type		= "pty",
143	.init		= pty_chan_init,
144	.open		= pty_open,
145	.close		= generic_close,
146	.read		= generic_read,
147	.write		= generic_write,
148	.console_write	= generic_console_write,
149	.window_size	= generic_window_size,
150	.free		= generic_free,
151	.winch		= 0,
152};
153
154const struct chan_ops pts_ops = {
155	.type		= "pts",
156	.init		= pty_chan_init,
157	.open		= pts_open,
158	.close		= generic_close,
159	.read		= generic_read,
160	.write		= generic_write,
161	.console_write	= generic_console_write,
162	.window_size	= generic_window_size,
163	.free		= generic_free,
164	.winch		= 0,
165};
166