162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <stdio.h>
762306a36Sopenharmony_ci#include <stdlib.h>
862306a36Sopenharmony_ci#include <unistd.h>
962306a36Sopenharmony_ci#include <errno.h>
1062306a36Sopenharmony_ci#include <fcntl.h>
1162306a36Sopenharmony_ci#include <string.h>
1262306a36Sopenharmony_ci#include <termios.h>
1362306a36Sopenharmony_ci#include <sys/stat.h>
1462306a36Sopenharmony_ci#include "chan_user.h"
1562306a36Sopenharmony_ci#include <os.h>
1662306a36Sopenharmony_ci#include <um_malloc.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistruct pty_chan {
1962306a36Sopenharmony_ci	void (*announce)(char *dev_name, int dev);
2062306a36Sopenharmony_ci	int dev;
2162306a36Sopenharmony_ci	int raw;
2262306a36Sopenharmony_ci	struct termios tt;
2362306a36Sopenharmony_ci	char dev_name[sizeof("/dev/pts/0123456\0")];
2462306a36Sopenharmony_ci};
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic void *pty_chan_init(char *str, int device, const struct chan_opts *opts)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	struct pty_chan *data;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	data = uml_kmalloc(sizeof(*data), UM_GFP_KERNEL);
3162306a36Sopenharmony_ci	if (data == NULL)
3262306a36Sopenharmony_ci		return NULL;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	*data = ((struct pty_chan) { .announce  	= opts->announce,
3562306a36Sopenharmony_ci				     .dev  		= device,
3662306a36Sopenharmony_ci				     .raw  		= opts->raw });
3762306a36Sopenharmony_ci	return data;
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic int pts_open(int input, int output, int primary, void *d,
4162306a36Sopenharmony_ci		    char **dev_out)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	struct pty_chan *data = d;
4462306a36Sopenharmony_ci	char *dev;
4562306a36Sopenharmony_ci	int fd, err;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	fd = get_pty();
4862306a36Sopenharmony_ci	if (fd < 0) {
4962306a36Sopenharmony_ci		err = -errno;
5062306a36Sopenharmony_ci		printk(UM_KERN_ERR "open_pts : Failed to open pts\n");
5162306a36Sopenharmony_ci		return err;
5262306a36Sopenharmony_ci	}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	if (data->raw) {
5562306a36Sopenharmony_ci		CATCH_EINTR(err = tcgetattr(fd, &data->tt));
5662306a36Sopenharmony_ci		if (err)
5762306a36Sopenharmony_ci			goto out_close;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci		err = raw(fd);
6062306a36Sopenharmony_ci		if (err)
6162306a36Sopenharmony_ci			goto out_close;
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	dev = ptsname(fd);
6562306a36Sopenharmony_ci	sprintf(data->dev_name, "%s", dev);
6662306a36Sopenharmony_ci	*dev_out = data->dev_name;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	if (data->announce)
6962306a36Sopenharmony_ci		(*data->announce)(dev, data->dev);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	return fd;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ciout_close:
7462306a36Sopenharmony_ci	close(fd);
7562306a36Sopenharmony_ci	return err;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic int getmaster(char *line)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	struct stat buf;
8162306a36Sopenharmony_ci	char *pty, *bank, *cp;
8262306a36Sopenharmony_ci	int master, err;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	pty = &line[strlen("/dev/ptyp")];
8562306a36Sopenharmony_ci	for (bank = "pqrs"; *bank; bank++) {
8662306a36Sopenharmony_ci		line[strlen("/dev/pty")] = *bank;
8762306a36Sopenharmony_ci		*pty = '0';
8862306a36Sopenharmony_ci		/* Did we hit the end ? */
8962306a36Sopenharmony_ci		if ((stat(line, &buf) < 0) && (errno == ENOENT))
9062306a36Sopenharmony_ci			break;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci		for (cp = "0123456789abcdef"; *cp; cp++) {
9362306a36Sopenharmony_ci			*pty = *cp;
9462306a36Sopenharmony_ci			master = open(line, O_RDWR);
9562306a36Sopenharmony_ci			if (master >= 0) {
9662306a36Sopenharmony_ci				char *tp = &line[strlen("/dev/")];
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci				/* verify slave side is usable */
9962306a36Sopenharmony_ci				*tp = 't';
10062306a36Sopenharmony_ci				err = access(line, R_OK | W_OK);
10162306a36Sopenharmony_ci				*tp = 'p';
10262306a36Sopenharmony_ci				if (!err)
10362306a36Sopenharmony_ci					return master;
10462306a36Sopenharmony_ci				close(master);
10562306a36Sopenharmony_ci			}
10662306a36Sopenharmony_ci		}
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	printk(UM_KERN_ERR "getmaster - no usable host pty devices\n");
11062306a36Sopenharmony_ci	return -ENOENT;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic int pty_open(int input, int output, int primary, void *d,
11462306a36Sopenharmony_ci		    char **dev_out)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	struct pty_chan *data = d;
11762306a36Sopenharmony_ci	int fd, err;
11862306a36Sopenharmony_ci	char dev[sizeof("/dev/ptyxx\0")] = "/dev/ptyxx";
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	fd = getmaster(dev);
12162306a36Sopenharmony_ci	if (fd < 0)
12262306a36Sopenharmony_ci		return fd;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	if (data->raw) {
12562306a36Sopenharmony_ci		err = raw(fd);
12662306a36Sopenharmony_ci		if (err) {
12762306a36Sopenharmony_ci			close(fd);
12862306a36Sopenharmony_ci			return err;
12962306a36Sopenharmony_ci		}
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	if (data->announce)
13362306a36Sopenharmony_ci		(*data->announce)(dev, data->dev);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	sprintf(data->dev_name, "%s", dev);
13662306a36Sopenharmony_ci	*dev_out = data->dev_name;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	return fd;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ciconst struct chan_ops pty_ops = {
14262306a36Sopenharmony_ci	.type		= "pty",
14362306a36Sopenharmony_ci	.init		= pty_chan_init,
14462306a36Sopenharmony_ci	.open		= pty_open,
14562306a36Sopenharmony_ci	.close		= generic_close,
14662306a36Sopenharmony_ci	.read		= generic_read,
14762306a36Sopenharmony_ci	.write		= generic_write,
14862306a36Sopenharmony_ci	.console_write	= generic_console_write,
14962306a36Sopenharmony_ci	.window_size	= generic_window_size,
15062306a36Sopenharmony_ci	.free		= generic_free,
15162306a36Sopenharmony_ci	.winch		= 0,
15262306a36Sopenharmony_ci};
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ciconst struct chan_ops pts_ops = {
15562306a36Sopenharmony_ci	.type		= "pts",
15662306a36Sopenharmony_ci	.init		= pty_chan_init,
15762306a36Sopenharmony_ci	.open		= pts_open,
15862306a36Sopenharmony_ci	.close		= generic_close,
15962306a36Sopenharmony_ci	.read		= generic_read,
16062306a36Sopenharmony_ci	.write		= generic_write,
16162306a36Sopenharmony_ci	.console_write	= generic_console_write,
16262306a36Sopenharmony_ci	.window_size	= generic_window_size,
16362306a36Sopenharmony_ci	.free		= generic_free,
16462306a36Sopenharmony_ci	.winch		= 0,
16562306a36Sopenharmony_ci};
166