xref: /kernel/linux/linux-5.10/arch/um/drivers/pty.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <stdio.h>
78c2ecf20Sopenharmony_ci#include <stdlib.h>
88c2ecf20Sopenharmony_ci#include <unistd.h>
98c2ecf20Sopenharmony_ci#include <errno.h>
108c2ecf20Sopenharmony_ci#include <fcntl.h>
118c2ecf20Sopenharmony_ci#include <string.h>
128c2ecf20Sopenharmony_ci#include <termios.h>
138c2ecf20Sopenharmony_ci#include <sys/stat.h>
148c2ecf20Sopenharmony_ci#include "chan_user.h"
158c2ecf20Sopenharmony_ci#include <os.h>
168c2ecf20Sopenharmony_ci#include <um_malloc.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistruct pty_chan {
198c2ecf20Sopenharmony_ci	void (*announce)(char *dev_name, int dev);
208c2ecf20Sopenharmony_ci	int dev;
218c2ecf20Sopenharmony_ci	int raw;
228c2ecf20Sopenharmony_ci	struct termios tt;
238c2ecf20Sopenharmony_ci	char dev_name[sizeof("/dev/pts/0123456\0")];
248c2ecf20Sopenharmony_ci};
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic void *pty_chan_init(char *str, int device, const struct chan_opts *opts)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	struct pty_chan *data;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	data = uml_kmalloc(sizeof(*data), UM_GFP_KERNEL);
318c2ecf20Sopenharmony_ci	if (data == NULL)
328c2ecf20Sopenharmony_ci		return NULL;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	*data = ((struct pty_chan) { .announce  	= opts->announce,
358c2ecf20Sopenharmony_ci				     .dev  		= device,
368c2ecf20Sopenharmony_ci				     .raw  		= opts->raw });
378c2ecf20Sopenharmony_ci	return data;
388c2ecf20Sopenharmony_ci}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic int pts_open(int input, int output, int primary, void *d,
418c2ecf20Sopenharmony_ci		    char **dev_out)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	struct pty_chan *data = d;
448c2ecf20Sopenharmony_ci	char *dev;
458c2ecf20Sopenharmony_ci	int fd, err;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	fd = get_pty();
488c2ecf20Sopenharmony_ci	if (fd < 0) {
498c2ecf20Sopenharmony_ci		err = -errno;
508c2ecf20Sopenharmony_ci		printk(UM_KERN_ERR "open_pts : Failed to open pts\n");
518c2ecf20Sopenharmony_ci		return err;
528c2ecf20Sopenharmony_ci	}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	if (data->raw) {
558c2ecf20Sopenharmony_ci		CATCH_EINTR(err = tcgetattr(fd, &data->tt));
568c2ecf20Sopenharmony_ci		if (err)
578c2ecf20Sopenharmony_ci			goto out_close;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci		err = raw(fd);
608c2ecf20Sopenharmony_ci		if (err)
618c2ecf20Sopenharmony_ci			goto out_close;
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	dev = ptsname(fd);
658c2ecf20Sopenharmony_ci	sprintf(data->dev_name, "%s", dev);
668c2ecf20Sopenharmony_ci	*dev_out = data->dev_name;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	if (data->announce)
698c2ecf20Sopenharmony_ci		(*data->announce)(dev, data->dev);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	return fd;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ciout_close:
748c2ecf20Sopenharmony_ci	close(fd);
758c2ecf20Sopenharmony_ci	return err;
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic int getmaster(char *line)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	struct stat buf;
818c2ecf20Sopenharmony_ci	char *pty, *bank, *cp;
828c2ecf20Sopenharmony_ci	int master, err;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	pty = &line[strlen("/dev/ptyp")];
858c2ecf20Sopenharmony_ci	for (bank = "pqrs"; *bank; bank++) {
868c2ecf20Sopenharmony_ci		line[strlen("/dev/pty")] = *bank;
878c2ecf20Sopenharmony_ci		*pty = '0';
888c2ecf20Sopenharmony_ci		/* Did we hit the end ? */
898c2ecf20Sopenharmony_ci		if ((stat(line, &buf) < 0) && (errno == ENOENT))
908c2ecf20Sopenharmony_ci			break;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci		for (cp = "0123456789abcdef"; *cp; cp++) {
938c2ecf20Sopenharmony_ci			*pty = *cp;
948c2ecf20Sopenharmony_ci			master = open(line, O_RDWR);
958c2ecf20Sopenharmony_ci			if (master >= 0) {
968c2ecf20Sopenharmony_ci				char *tp = &line[strlen("/dev/")];
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci				/* verify slave side is usable */
998c2ecf20Sopenharmony_ci				*tp = 't';
1008c2ecf20Sopenharmony_ci				err = access(line, R_OK | W_OK);
1018c2ecf20Sopenharmony_ci				*tp = 'p';
1028c2ecf20Sopenharmony_ci				if (!err)
1038c2ecf20Sopenharmony_ci					return master;
1048c2ecf20Sopenharmony_ci				close(master);
1058c2ecf20Sopenharmony_ci			}
1068c2ecf20Sopenharmony_ci		}
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	printk(UM_KERN_ERR "getmaster - no usable host pty devices\n");
1108c2ecf20Sopenharmony_ci	return -ENOENT;
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic int pty_open(int input, int output, int primary, void *d,
1148c2ecf20Sopenharmony_ci		    char **dev_out)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	struct pty_chan *data = d;
1178c2ecf20Sopenharmony_ci	int fd, err;
1188c2ecf20Sopenharmony_ci	char dev[sizeof("/dev/ptyxx\0")] = "/dev/ptyxx";
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	fd = getmaster(dev);
1218c2ecf20Sopenharmony_ci	if (fd < 0)
1228c2ecf20Sopenharmony_ci		return fd;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	if (data->raw) {
1258c2ecf20Sopenharmony_ci		err = raw(fd);
1268c2ecf20Sopenharmony_ci		if (err) {
1278c2ecf20Sopenharmony_ci			close(fd);
1288c2ecf20Sopenharmony_ci			return err;
1298c2ecf20Sopenharmony_ci		}
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	if (data->announce)
1338c2ecf20Sopenharmony_ci		(*data->announce)(dev, data->dev);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	sprintf(data->dev_name, "%s", dev);
1368c2ecf20Sopenharmony_ci	*dev_out = data->dev_name;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	return fd;
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ciconst struct chan_ops pty_ops = {
1428c2ecf20Sopenharmony_ci	.type		= "pty",
1438c2ecf20Sopenharmony_ci	.init		= pty_chan_init,
1448c2ecf20Sopenharmony_ci	.open		= pty_open,
1458c2ecf20Sopenharmony_ci	.close		= generic_close,
1468c2ecf20Sopenharmony_ci	.read		= generic_read,
1478c2ecf20Sopenharmony_ci	.write		= generic_write,
1488c2ecf20Sopenharmony_ci	.console_write	= generic_console_write,
1498c2ecf20Sopenharmony_ci	.window_size	= generic_window_size,
1508c2ecf20Sopenharmony_ci	.free		= generic_free,
1518c2ecf20Sopenharmony_ci	.winch		= 0,
1528c2ecf20Sopenharmony_ci};
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ciconst struct chan_ops pts_ops = {
1558c2ecf20Sopenharmony_ci	.type		= "pts",
1568c2ecf20Sopenharmony_ci	.init		= pty_chan_init,
1578c2ecf20Sopenharmony_ci	.open		= pts_open,
1588c2ecf20Sopenharmony_ci	.close		= generic_close,
1598c2ecf20Sopenharmony_ci	.read		= generic_read,
1608c2ecf20Sopenharmony_ci	.write		= generic_write,
1618c2ecf20Sopenharmony_ci	.console_write	= generic_console_write,
1628c2ecf20Sopenharmony_ci	.window_size	= generic_window_size,
1638c2ecf20Sopenharmony_ci	.free		= generic_free,
1648c2ecf20Sopenharmony_ci	.winch		= 0,
1658c2ecf20Sopenharmony_ci};
166