WIP FPC-III support
[linux/fpc-iii.git] / tools / testing / selftests / filesystems / devpts_pts.c
blobb1fc9b916acee2c14ff2d73a905a121abae64c8b
1 // SPDX-License-Identifier: GPL-2.0
2 #define _GNU_SOURCE
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <sched.h>
6 #include <stdbool.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include <asm/ioctls.h>
12 #include <sys/mount.h>
13 #include <sys/wait.h>
14 #include "../kselftest.h"
16 static bool terminal_dup2(int duplicate, int original)
18 int ret;
20 ret = dup2(duplicate, original);
21 if (ret < 0)
22 return false;
24 return true;
27 static int terminal_set_stdfds(int fd)
29 int i;
31 if (fd < 0)
32 return 0;
34 for (i = 0; i < 3; i++)
35 if (!terminal_dup2(fd, (int[]){STDIN_FILENO, STDOUT_FILENO,
36 STDERR_FILENO}[i]))
37 return -1;
39 return 0;
42 static int login_pty(int fd)
44 int ret;
46 setsid();
48 ret = ioctl(fd, TIOCSCTTY, NULL);
49 if (ret < 0)
50 return -1;
52 ret = terminal_set_stdfds(fd);
53 if (ret < 0)
54 return -1;
56 if (fd > STDERR_FILENO)
57 close(fd);
59 return 0;
62 static int wait_for_pid(pid_t pid)
64 int status, ret;
66 again:
67 ret = waitpid(pid, &status, 0);
68 if (ret == -1) {
69 if (errno == EINTR)
70 goto again;
71 return -1;
73 if (ret != pid)
74 goto again;
76 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
77 return -1;
79 return 0;
82 static int resolve_procfd_symlink(int fd, char *buf, size_t buflen)
84 int ret;
85 char procfd[4096];
87 ret = snprintf(procfd, 4096, "/proc/self/fd/%d", fd);
88 if (ret < 0 || ret >= 4096)
89 return -1;
91 ret = readlink(procfd, buf, buflen);
92 if (ret < 0 || (size_t)ret >= buflen)
93 return -1;
95 buf[ret] = '\0';
97 return 0;
100 static int do_tiocgptpeer(char *ptmx, char *expected_procfd_contents)
102 int ret;
103 int master = -1, slave = -1, fret = -1;
105 master = open(ptmx, O_RDWR | O_NOCTTY | O_CLOEXEC);
106 if (master < 0) {
107 fprintf(stderr, "Failed to open \"%s\": %s\n", ptmx,
108 strerror(errno));
109 return -1;
113 * grantpt() makes assumptions about /dev/pts/ so ignore it. It's also
114 * not really needed.
116 ret = unlockpt(master);
117 if (ret < 0) {
118 fprintf(stderr, "Failed to unlock terminal\n");
119 goto do_cleanup;
122 #ifdef TIOCGPTPEER
123 slave = ioctl(master, TIOCGPTPEER, O_RDWR | O_NOCTTY | O_CLOEXEC);
124 #endif
125 if (slave < 0) {
126 if (errno == EINVAL) {
127 fprintf(stderr, "TIOCGPTPEER is not supported. "
128 "Skipping test.\n");
129 fret = KSFT_SKIP;
130 } else {
131 fprintf(stderr,
132 "Failed to perform TIOCGPTPEER ioctl\n");
133 fret = EXIT_FAILURE;
135 goto do_cleanup;
138 pid_t pid = fork();
139 if (pid < 0)
140 goto do_cleanup;
142 if (pid == 0) {
143 char buf[4096];
145 ret = login_pty(slave);
146 if (ret < 0) {
147 fprintf(stderr, "Failed to setup terminal\n");
148 _exit(EXIT_FAILURE);
151 ret = resolve_procfd_symlink(STDIN_FILENO, buf, sizeof(buf));
152 if (ret < 0) {
153 fprintf(stderr, "Failed to retrieve pathname of pts "
154 "slave file descriptor\n");
155 _exit(EXIT_FAILURE);
158 if (strncmp(expected_procfd_contents, buf,
159 strlen(expected_procfd_contents)) != 0) {
160 fprintf(stderr, "Received invalid contents for "
161 "\"/proc/<pid>/fd/%d\" symlink: %s\n",
162 STDIN_FILENO, buf);
163 _exit(-1);
166 fprintf(stderr, "Contents of \"/proc/<pid>/fd/%d\" "
167 "symlink are valid: %s\n", STDIN_FILENO, buf);
169 _exit(EXIT_SUCCESS);
172 ret = wait_for_pid(pid);
173 if (ret < 0)
174 goto do_cleanup;
176 fret = EXIT_SUCCESS;
178 do_cleanup:
179 if (master >= 0)
180 close(master);
181 if (slave >= 0)
182 close(slave);
184 return fret;
187 static int verify_non_standard_devpts_mount(void)
189 char *mntpoint;
190 int ret = -1;
191 char devpts[] = P_tmpdir "/devpts_fs_XXXXXX";
192 char ptmx[] = P_tmpdir "/devpts_fs_XXXXXX/ptmx";
194 ret = umount("/dev/pts");
195 if (ret < 0) {
196 fprintf(stderr, "Failed to unmount \"/dev/pts\": %s\n",
197 strerror(errno));
198 return -1;
201 (void)umount("/dev/ptmx");
203 mntpoint = mkdtemp(devpts);
204 if (!mntpoint) {
205 fprintf(stderr, "Failed to create temporary mountpoint: %s\n",
206 strerror(errno));
207 return -1;
210 ret = mount("devpts", mntpoint, "devpts", MS_NOSUID | MS_NOEXEC,
211 "newinstance,ptmxmode=0666,mode=0620,gid=5");
212 if (ret < 0) {
213 fprintf(stderr, "Failed to mount devpts fs to \"%s\" in new "
214 "mount namespace: %s\n", mntpoint,
215 strerror(errno));
216 unlink(mntpoint);
217 return -1;
220 ret = snprintf(ptmx, sizeof(ptmx), "%s/ptmx", devpts);
221 if (ret < 0 || (size_t)ret >= sizeof(ptmx)) {
222 unlink(mntpoint);
223 return -1;
226 ret = do_tiocgptpeer(ptmx, mntpoint);
227 unlink(mntpoint);
228 if (ret < 0)
229 return -1;
231 return 0;
234 static int verify_ptmx_bind_mount(void)
236 int ret;
238 ret = mount("/dev/pts/ptmx", "/dev/ptmx", NULL, MS_BIND, NULL);
239 if (ret < 0) {
240 fprintf(stderr, "Failed to bind mount \"/dev/pts/ptmx\" to "
241 "\"/dev/ptmx\" mount namespace\n");
242 return -1;
245 ret = do_tiocgptpeer("/dev/ptmx", "/dev/pts/");
246 if (ret < 0)
247 return -1;
249 return 0;
252 static int verify_invalid_ptmx_bind_mount(void)
254 int ret;
255 char mntpoint_fd;
256 char ptmx[] = P_tmpdir "/devpts_ptmx_XXXXXX";
258 mntpoint_fd = mkstemp(ptmx);
259 if (mntpoint_fd < 0) {
260 fprintf(stderr, "Failed to create temporary directory: %s\n",
261 strerror(errno));
262 return -1;
265 ret = mount("/dev/pts/ptmx", ptmx, NULL, MS_BIND, NULL);
266 close(mntpoint_fd);
267 if (ret < 0) {
268 fprintf(stderr, "Failed to bind mount \"/dev/pts/ptmx\" to "
269 "\"%s\" mount namespace\n", ptmx);
270 return -1;
273 ret = do_tiocgptpeer(ptmx, "/dev/pts/");
274 if (ret == 0)
275 return -1;
277 return 0;
280 int main(int argc, char *argv[])
282 int ret;
284 if (!isatty(STDIN_FILENO)) {
285 fprintf(stderr, "Standard input file descriptor is not attached "
286 "to a terminal. Skipping test\n");
287 exit(KSFT_SKIP);
290 ret = unshare(CLONE_NEWNS);
291 if (ret < 0) {
292 fprintf(stderr, "Failed to unshare mount namespace\n");
293 exit(EXIT_FAILURE);
296 ret = mount("", "/", NULL, MS_PRIVATE | MS_REC, 0);
297 if (ret < 0) {
298 fprintf(stderr, "Failed to make \"/\" MS_PRIVATE in new mount "
299 "namespace\n");
300 exit(EXIT_FAILURE);
303 ret = verify_ptmx_bind_mount();
304 if (ret < 0)
305 exit(EXIT_FAILURE);
307 ret = verify_invalid_ptmx_bind_mount();
308 if (ret < 0)
309 exit(EXIT_FAILURE);
311 ret = verify_non_standard_devpts_mount();
312 if (ret < 0)
313 exit(EXIT_FAILURE);
315 exit(EXIT_SUCCESS);