]>
Commit | Line | Data |
---|---|---|
35eb5cdc CB |
1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
2 | ||
3 | #define _GNU_SOURCE | |
e827ff7e SG |
4 | #include <errno.h> |
5 | #include <fcntl.h> | |
6 | #include <limits.h> | |
7 | #include <stdlib.h> | |
8 | #include <string.h> | |
35eb5cdc CB |
9 | #include <sys/ioctl.h> |
10 | #include <sys/types.h> | |
e827ff7e SG |
11 | #include <termios.h> |
12 | #include <unistd.h> | |
e827ff7e | 13 | |
6ae86a9c | 14 | #if HAVE_PTY_H |
35eb5cdc CB |
15 | #include <pty.h> |
16 | #endif | |
e827ff7e | 17 | |
35eb5cdc | 18 | static int pts_name(int fd, char **pts, size_t buf_len) |
e827ff7e | 19 | { |
35eb5cdc CB |
20 | int rv; |
21 | char *buf = *pts; | |
22 | ||
23 | for (;;) { | |
24 | char *new_buf; | |
25 | ||
26 | if (buf_len) { | |
27 | rv = ptsname_r(fd, buf, buf_len); | |
28 | ||
29 | if (rv != 0 || memchr(buf, '\0', buf_len)) | |
30 | /* We either got an error, or we succeeded and the | |
31 | returned name fit in the buffer. */ | |
32 | break; | |
33 | ||
34 | /* Try again with a longer buffer. */ | |
35 | buf_len += buf_len; /* Double it */ | |
36 | } else | |
37 | /* No initial buffer; start out by mallocing one. */ | |
38 | buf_len = 128; /* First time guess. */ | |
39 | ||
40 | if (buf != *pts) | |
41 | /* We've already malloced another buffer at least once. */ | |
42 | new_buf = realloc(buf, buf_len); | |
43 | else | |
44 | new_buf = malloc(buf_len); | |
45 | if (!new_buf) { | |
46 | rv = -1; | |
47 | break; | |
48 | } | |
49 | buf = new_buf; | |
50 | } | |
51 | ||
52 | if (rv == 0) | |
53 | *pts = buf; /* Return buffer to the user. */ | |
54 | else if (buf != *pts) | |
55 | free(buf); /* Free what we malloced when returning an error. */ | |
56 | ||
57 | return rv; | |
58 | } | |
59 | ||
60 | int __unlockpt(int fd) | |
61 | { | |
62 | #ifdef TIOCSPTLCK | |
63 | int unlock = 0; | |
64 | ||
65 | if (ioctl(fd, TIOCSPTLCK, &unlock)) { | |
66 | if (errno != EINVAL) | |
67 | return -1; | |
68 | } | |
69 | #endif | |
70 | return 0; | |
71 | } | |
72 | ||
73 | int openpty(int *ptx, int *pty, char *name, const struct termios *termp, | |
74 | const struct winsize *winp) | |
75 | { | |
76 | char _buf[PATH_MAX]; | |
77 | char *buf = _buf; | |
78 | int ptx_fd, ret = -1, pty_fd = -1; | |
79 | ||
80 | *buf = '\0'; | |
81 | ||
82 | ptx_fd = open("/dev/ptmx", O_RDWR | O_NOCTTY); | |
83 | if (ptx_fd == -1) | |
84 | return -1; | |
85 | ||
86 | if (__unlockpt(ptx_fd)) | |
87 | goto on_error; | |
88 | ||
89 | #ifdef TIOCGPTPEER | |
90 | /* Try to allocate pty_fd solely based on ptx_fd first. */ | |
91 | pty_fd = ioctl(ptx_fd, TIOCGPTPEER, O_RDWR | O_NOCTTY); | |
92 | #endif | |
93 | if (pty_fd == -1) { | |
94 | /* Fallback to path-based pty_fd allocation in case kernel doesn't | |
95 | * support TIOCGPTPEER. | |
96 | */ | |
97 | if (pts_name(ptx_fd, &buf, sizeof(_buf))) | |
98 | goto on_error; | |
e827ff7e | 99 | |
35eb5cdc CB |
100 | pty_fd = open(buf, O_RDWR | O_NOCTTY); |
101 | if (pty_fd == -1) | |
102 | goto on_error; | |
103 | } | |
e827ff7e | 104 | |
35eb5cdc CB |
105 | if (termp) |
106 | tcsetattr(pty_fd, TCSAFLUSH, termp); | |
107 | #ifdef TIOCSWINSZ | |
108 | if (winp) | |
109 | ioctl(pty_fd, TIOCSWINSZ, winp); | |
110 | #endif | |
e827ff7e | 111 | |
35eb5cdc CB |
112 | *ptx = ptx_fd; |
113 | *pty = pty_fd; | |
114 | if (name != NULL) { | |
115 | if (*buf == '\0') | |
116 | if (pts_name(ptx_fd, &buf, sizeof(_buf))) | |
117 | goto on_error; | |
e827ff7e | 118 | |
35eb5cdc CB |
119 | strcpy(name, buf); |
120 | } | |
e827ff7e | 121 | |
35eb5cdc | 122 | ret = 0; |
e827ff7e | 123 | |
35eb5cdc CB |
124 | on_error: |
125 | if (ret == -1) { | |
126 | close(ptx_fd); | |
e827ff7e | 127 | |
35eb5cdc CB |
128 | if (pty_fd != -1) |
129 | close(pty_fd); | |
130 | } | |
e827ff7e | 131 | |
35eb5cdc CB |
132 | if (buf != _buf) |
133 | free(buf); | |
e827ff7e | 134 | |
35eb5cdc | 135 | return ret; |
e827ff7e | 136 | } |