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 | |
18 | struct 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 | |
26 | static 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 | |
40 | static 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 | |
73 | out_close: |
74 | close(fd); |
75 | return err; |
76 | } |
77 | |
78 | static 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 | |
113 | static 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(line: 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 | |
141 | const 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 | |
154 | const 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 | |