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