aboutsummaryrefslogtreecommitdiff
path: root/arch/um/drivers/pty.c
blob: 39c60068cfdf67ad4cc4509fd806be08e680591d (plain)
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
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <termios.h>
#include <sys/stat.h>
#include "chan_user.h"
#include <os.h>
#include <um_malloc.h>

struct pty_chan {
	void (*announce)(char *dev_name, int dev);
	int dev;
	int raw;
	struct termios tt;
	char dev_name[sizeof("/dev/pts/0123456\0")];
};

static void *pty_chan_init(char *str, int device, const struct chan_opts *opts)
{
	struct pty_chan *data;

	data = uml_kmalloc(sizeof(*data), UM_GFP_KERNEL);
	if (data == NULL)
		return NULL;

	*data = ((struct pty_chan) { .announce  	= opts->announce,
				     .dev  		= device,
				     .raw  		= opts->raw });
	return data;
}

static int pts_open(int input, int output, int primary, void *d,
		    char **dev_out)
{
	struct pty_chan *data = d;
	char *dev;
	int fd, err;

	fd = get_pty();
	if (fd < 0) {
		err = -errno;
		printk(UM_KERN_ERR "open_pts : Failed to open pts\n");
		return err;
	}

	if (data->raw) {
		CATCH_EINTR(err = tcgetattr(fd, &data->tt));
		if (err)
			goto out_close;

		err = raw(fd);
		if (err)
			goto out_close;
	}

	dev = ptsname(fd);
	sprintf(data->dev_name, "%s", dev);
	*dev_out = data->dev_name;

	if (data->announce)
		(*data->announce)(dev, data->dev);

	return fd;

out_close:
	close(fd);
	return err;
}

static int getmaster(char *line)
{
	struct stat buf;
	char *pty, *bank, *cp;
	int master, err;

	pty = &line[strlen("/dev/ptyp")];
	for (bank = "pqrs"; *bank; bank++) {
		line[strlen("/dev/pty")] = *bank;
		*pty = '0';
		/* Did we hit the end ? */
		if ((stat(line, &buf) < 0) && (errno == ENOENT))
			break;

		for (cp = "0123456789abcdef"; *cp; cp++) {
			*pty = *cp;
			master = open(line, O_RDWR);
			if (master >= 0) {
				char *tp = &line[strlen("/dev/")];

				/* verify slave side is usable */
				*tp = 't';
				err = access(line, R_OK | W_OK);
				*tp = 'p';
				if (!err)
					return master;
				close(master);
			}
		}
	}

	printk(UM_KERN_ERR "getmaster - no usable host pty devices\n");
	return -ENOENT;
}

static int pty_open(int input, int output, int primary, void *d,
		    char **dev_out)
{
	struct pty_chan *data = d;
	int fd, err;
	char dev[sizeof("/dev/ptyxx\0")] = "/dev/ptyxx";

	fd = getmaster(dev);
	if (fd < 0)
		return fd;

	if (data->raw) {
		err = raw(fd);
		if (err) {
			close(fd);
			return err;
		}
	}

	if (data->announce)
		(*data->announce)(dev, data->dev);

	sprintf(data->dev_name, "%s", dev);
	*dev_out = data->dev_name;

	return fd;
}

const struct chan_ops pty_ops = {
	.type		= "pty",
	.init		= pty_chan_init,
	.open		= pty_open,
	.close		= generic_close,
	.read		= generic_read,
	.write		= generic_write,
	.console_write	= generic_console_write,
	.window_size	= generic_window_size,
	.free		= generic_free,
	.winch		= 0,
};

const struct chan_ops pts_ops = {
	.type		= "pts",
	.init		= pty_chan_init,
	.open		= pts_open,
	.close		= generic_close,
	.read		= generic_read,
	.write		= generic_write,
	.console_write	= generic_console_write,
	.window_size	= generic_window_size,
	.free		= generic_free,
	.winch		= 0,
};