1 /* Copyright (C) 2017-2018 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 2.1 of the
7 License, or (at your option) any later version.
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; see the file COPYING.LIB. If
16 not, see <http://www.gnu.org/licenses/>. */
26 #include <sys/mount.h>
27 #include <sys/prctl.h>
32 #include <support/check.h>
33 #include <support/namespace.h>
34 #include <support/support.h>
35 #include <support/temp_file.h>
36 #include <support/test-driver.h>
37 #include <support/xunistd.h>
39 /* generic utilities */
41 #define VERIFY(expr) \
45 printf ("error: %s:%d: %s: %m\n", \
46 __FILE__, __LINE__, #expr); \
52 touch (const char *path
, mode_t mode
)
54 xclose (xopen (path
, O_WRONLY
|O_CREAT
|O_NOCTTY
, mode
));
58 trim_prefix (char *str
, size_t str_len
, const char *prefix
)
60 size_t prefix_len
= strlen (prefix
);
61 if (str_len
> prefix_len
&& memcmp (str
, prefix
, prefix_len
) == 0)
63 memmove (str
, str
+ prefix_len
, str_len
- prefix_len
);
64 return str_len
- prefix_len
;
69 /* returns a pointer to static storage */
71 proc_fd_readlink (const char *linkname
)
73 static char target
[PATH_MAX
+1];
74 ssize_t target_len
= readlink (linkname
, target
, PATH_MAX
);
75 VERIFY (target_len
> 0);
76 target_len
= trim_prefix (target
, target_len
, "(unreachable)");
77 target
[target_len
] = '\0';
81 /* plain ttyname runner */
89 /* strings in result structure are in static storage */
95 ret
.name
= ttyname (fd
);
101 eq_ttyname (struct result actual
, struct result expected
)
103 char *actual_name
, *expected_name
;
105 if ((actual
.err
== expected
.err
) &&
106 (!actual
.name
== !expected
.name
) &&
107 (actual
.name
? strcmp (actual
.name
, expected
.name
) == 0 : true))
110 expected_name
= xasprintf ("\"%s\"", expected
.name
);
112 expected_name
= xstrdup ("NULL");
114 printf ("info: ttyname: PASS {name=%s, errno=%d}\n",
115 expected_name
, expected
.err
);
117 free (expected_name
);
122 actual_name
= xasprintf ("\"%s\"", actual
.name
);
124 actual_name
= xstrdup ("NULL");
127 expected_name
= xasprintf ("\"%s\"", expected
.name
);
129 expected_name
= xstrdup ("NULL");
131 printf ("error: ttyname: actual {name=%s, errno=%d} != expected {name=%s, errno=%d}\n",
132 actual_name
, actual
.err
,
133 expected_name
, expected
.err
);
136 free (expected_name
);
140 /* ttyname_r runner */
149 /* strings in result structure are in static storage */
150 static struct result_r
151 run_ttyname_r (int fd
)
153 static char buf
[TTY_NAME_MAX
];
157 ret
.ret
= ttyname_r (fd
, buf
, TTY_NAME_MAX
);
167 eq_ttyname_r (struct result_r actual
, struct result_r expected
)
169 char *actual_name
, *expected_name
;
171 if ((actual
.err
== expected
.err
) &&
172 (actual
.ret
== expected
.ret
) &&
173 (!actual
.name
== !expected
.name
) &&
174 (actual
.name
? strcmp (actual
.name
, expected
.name
) == 0 : true))
177 expected_name
= xasprintf ("\"%s\"", expected
.name
);
179 expected_name
= xstrdup ("NULL");
181 printf ("info: ttyname_r: PASS {name=%s, ret=%d, errno=%d}\n",
182 expected_name
, expected
.ret
, expected
.err
);
184 free (expected_name
);
189 actual_name
= xasprintf ("\"%s\"", actual
.name
);
191 actual_name
= xstrdup ("NULL");
194 expected_name
= xasprintf ("\"%s\"", expected
.name
);
196 expected_name
= xstrdup ("NULL");
198 printf ("error: ttyname_r: actual {name=%s, ret=%d, errno=%d} != expected {name=%s, ret=%d, errno=%d}\n",
199 actual_name
, actual
.ret
, actual
.err
,
200 expected_name
, expected
.ret
, expected
.err
);
203 free (expected_name
);
207 /* combined runner */
210 doit (int fd
, const char *testname
, struct result_r expected_r
)
212 struct result expected
= {.name
=expected_r
.name
, .err
=expected_r
.ret
};
215 printf ("info: testcase: %s\n", testname
);
217 if (!eq_ttyname (run_ttyname (fd
), expected
))
219 if (!eq_ttyname_r (run_ttyname_r (fd
), expected_r
))
223 support_record_failure ();
230 static char *chrootdir
;
233 prepare (int argc
, char **argv
)
235 chrootdir
= xasprintf ("%s/tst-ttyname-XXXXXX", test_dir
);
236 if (mkdtemp (chrootdir
) == NULL
)
237 FAIL_EXIT1 ("mkdtemp (\"%s\"): %m", chrootdir
);
238 add_temp_file (chrootdir
);
240 #define PREPARE prepare
242 /* These chroot setup functions put the TTY at at "/console" (where it
243 won't be found by ttyname), and create "/dev/console" as an
244 ordinary file. This way, it's easier to write test-cases that
245 expect ttyname to fail; test-cases that expect it to succeed need
246 to explicitly remount it at "/dev/console". */
249 do_in_chroot_1 (int (*cb
)(const char *, int))
251 printf ("info: entering chroot 1\n");
253 /* Open the PTS that we'll be testing on. */
256 master
= posix_openpt (O_RDWR
|O_NOCTTY
|O_NONBLOCK
);
260 FAIL_UNSUPPORTED ("posix_openpt: %m");
262 FAIL_EXIT1 ("posix_openpt: %m");
264 VERIFY ((slavename
= ptsname (master
)));
265 VERIFY (unlockpt (master
) == 0);
266 if (strncmp (slavename
, "/dev/pts/", 9) != 0)
267 FAIL_UNSUPPORTED ("slave pseudo-terminal is not under /dev/pts/: %s",
269 int slave
= xopen (slavename
, O_RDWR
, 0);
270 if (!doit (slave
, "basic smoketest",
271 (struct result_r
){.name
=slavename
, .ret
=0, .err
=0}))
274 pid_t pid
= xfork ();
279 if (!support_enter_mount_namespace ())
280 FAIL_UNSUPPORTED ("could not enter new mount namespace");
282 VERIFY (mount ("tmpfs", chrootdir
, "tmpfs", 0, "mode=755") == 0);
283 VERIFY (chdir (chrootdir
) == 0);
285 xmkdir ("proc", 0755);
286 xmkdir ("dev", 0755);
287 xmkdir ("dev/pts", 0755);
289 VERIFY (mount ("/proc", "proc", NULL
, MS_BIND
|MS_REC
, NULL
) == 0);
290 VERIFY (mount ("devpts", "dev/pts", "devpts",
292 "newinstance,ptmxmode=0666,mode=620") == 0);
293 VERIFY (symlink ("pts/ptmx", "dev/ptmx") == 0);
295 touch ("console", 0);
296 touch ("dev/console", 0);
297 VERIFY (mount (slavename
, "console", NULL
, MS_BIND
, NULL
) == 0);
301 char *linkname
= xasprintf ("/proc/self/fd/%d", slave
);
302 char *target
= proc_fd_readlink (linkname
);
303 VERIFY (strcmp (target
, slavename
) == 0);
306 _exit (cb (slavename
, slave
));
309 xwaitpid (pid
, &status
, 0);
310 VERIFY (WIFEXITED (status
));
313 return WEXITSTATUS (status
);
317 do_in_chroot_2 (int (*cb
)(const char *, int))
319 printf ("info: entering chroot 2\n");
326 /* Open the PTS that we'll be testing on. */
329 VERIFY ((master
= posix_openpt (O_RDWR
|O_NOCTTY
|O_NONBLOCK
)) >= 0);
330 VERIFY ((slavename
= ptsname (master
)));
331 VERIFY (unlockpt (master
) == 0);
332 if (strncmp (slavename
, "/dev/pts/", 9) != 0)
333 FAIL_UNSUPPORTED ("slave pseudo-terminal is not under /dev/pts/: %s",
335 /* wait until in a new mount ns to open the slave */
337 /* enable `wait`ing on grandchildren */
338 VERIFY (prctl (PR_SET_CHILD_SUBREAPER
, 1) == 0);
340 pid_t pid
= xfork (); /* outer child */
344 xclose (pid_pipe
[0]);
345 xclose (exit_pipe
[1]);
347 if (!support_enter_mount_namespace ())
348 FAIL_UNSUPPORTED ("could not enter new mount namespace");
350 int slave
= xopen (slavename
, O_RDWR
, 0);
351 if (!doit (slave
, "basic smoketest",
352 (struct result_r
){.name
=slavename
, .ret
=0, .err
=0}))
355 VERIFY (mount ("tmpfs", chrootdir
, "tmpfs", 0, "mode=755") == 0);
356 VERIFY (chdir (chrootdir
) == 0);
358 xmkdir ("proc", 0755);
359 xmkdir ("dev", 0755);
360 xmkdir ("dev/pts", 0755);
362 VERIFY (mount ("devpts", "dev/pts", "devpts",
364 "newinstance,ptmxmode=0666,mode=620") == 0);
365 VERIFY (symlink ("pts/ptmx", "dev/ptmx") == 0);
367 touch ("console", 0);
368 touch ("dev/console", 0);
369 VERIFY (mount (slavename
, "console", NULL
, MS_BIND
, NULL
) == 0);
373 if (unshare (CLONE_NEWNS
| CLONE_NEWPID
) < 0)
374 FAIL_UNSUPPORTED ("could not enter new PID namespace");
375 pid
= xfork (); /* inner child */
378 xclose (pid_pipe
[1]);
380 /* wait until the outer child has exited */
382 VERIFY (read (exit_pipe
[0], &c
, 1) == 0);
383 xclose (exit_pipe
[0]);
385 VERIFY (mount ("proc", "/proc", "proc",
386 MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, NULL
) == 0);
388 char *linkname
= xasprintf ("/proc/self/fd/%d", slave
);
389 char *target
= proc_fd_readlink (linkname
);
390 VERIFY (strcmp (target
, strrchr (slavename
, '/')) == 0);
393 _exit (cb (slavename
, slave
));
395 xwrite (pid_pipe
[1], &pid
, sizeof pid
);
398 xclose (pid_pipe
[1]);
399 xclose (exit_pipe
[0]);
400 xclose (exit_pipe
[1]);
402 /* wait for the outer child */
404 xwaitpid (pid
, &status
, 0);
405 VERIFY (WIFEXITED (status
));
406 int ret
= WEXITSTATUS (status
);
410 /* set 'pid' to the inner child */
411 VERIFY (read (pid_pipe
[0], &pid
, sizeof pid
) == sizeof pid
);
412 xclose (pid_pipe
[0]);
414 /* wait for the inner child */
415 xwaitpid (pid
, &status
, 0);
416 VERIFY (WIFEXITED (status
));
418 return WEXITSTATUS (status
);
424 run_chroot_tests (const char *slavename
, int slave
)
429 /* There are 3 groups of tests here. The first group fairly
430 generically does things known to mess up ttyname, and verifies
431 that ttyname copes correctly. The remaining groups are
432 increasingly convoluted, as we target specific parts of ttyname
433 to try to confuse. */
435 /* Basic tests that it doesn't get confused by multiple devpts
438 VERIFY (stat (slavename
, &st
) < 0); /* sanity check */
439 if (!doit (slave
, "no conflict, no match",
440 (struct result_r
){.name
=NULL
, .ret
=ENODEV
, .err
=ENODEV
}))
442 VERIFY (mount ("/console", "/dev/console", NULL
, MS_BIND
, NULL
) == 0);
443 if (!doit (slave
, "no conflict, console",
444 (struct result_r
){.name
="/dev/console", .ret
=0, .err
=0}))
446 VERIFY (umount ("/dev/console") == 0);
448 /* keep creating PTYs until we we get a name collision */
449 while (stat (slavename
, &st
) < 0)
450 posix_openpt (O_RDWR
|O_NOCTTY
|O_NONBLOCK
);
451 VERIFY (stat (slavename
, &st
) == 0);
453 if (!doit (slave
, "conflict, no match",
454 (struct result_r
){.name
=NULL
, .ret
=ENODEV
, .err
=ENODEV
}))
456 VERIFY (mount ("/console", "/dev/console", NULL
, MS_BIND
, NULL
) == 0);
457 if (!doit (slave
, "conflict, console",
458 (struct result_r
){.name
="/dev/console", .ret
=0, .err
=0}))
460 VERIFY (umount ("/dev/console") == 0);
463 /* The first tests kinda assumed that they hit certain code-paths
464 based on assuming that the readlink target is 'slavename', but
465 that's not quite always true. They're still a good preliminary
466 sanity check, so keep them, but let's add tests that make sure
467 that those code-paths are hit by doing a readlink ourself. */
469 char *linkname
= xasprintf ("/proc/self/fd/%d", slave
);
470 char *target
= proc_fd_readlink (linkname
);
472 /* Depeding on how we set up the chroot, the kernel may or may not
473 trim the leading path to the target (it may give us "/6",
474 instead of "/dev/pts/6"). We test it both ways (do_in_chroot_1
475 and do_in_chroot_2). This test group relies on the target
476 existing, so guarantee that it does exist by creating it if
478 if (stat (target
, &st
) < 0)
480 VERIFY (errno
== ENOENT
);
484 VERIFY (mount ("/console", "/dev/console", NULL
, MS_BIND
, NULL
) == 0);
485 VERIFY (mount ("/console", target
, NULL
, MS_BIND
, NULL
) == 0);
486 if (!doit (slave
, "with readlink target",
487 (struct result_r
){.name
=target
, .ret
=0, .err
=0}))
489 VERIFY (umount (target
) == 0);
490 VERIFY (umount ("/dev/console") == 0);
492 VERIFY (mount ("/console", "/dev/console", NULL
, MS_BIND
, NULL
) == 0);
493 VERIFY (mount (slavename
, target
, NULL
, MS_BIND
, NULL
) == 0);
494 if (!doit (slave
, "with readlink trap; fallback",
495 (struct result_r
){.name
="/dev/console", .ret
=0, .err
=0}))
497 VERIFY (umount (target
) == 0);
498 VERIFY (umount ("/dev/console") == 0);
500 VERIFY (mount (slavename
, target
, NULL
, MS_BIND
, NULL
) == 0);
501 if (!doit (slave
, "with readlink trap; no fallback",
502 (struct result_r
){.name
=NULL
, .ret
=ENODEV
, .err
=ENODEV
}))
504 VERIFY (umount (target
) == 0);
507 /* This test makes sure that everything still works OK if readdir
508 finds a pseudo-match before and/or after the actual match. Now,
509 to do that, we need to control that readdir finds the
510 pseudo-matches before and after the actual match; and there's no
511 good way to control that order in absence of whitebox testing.
512 So, just create 3 files, then use opendir/readdir to see what
513 order they are in, and assign meaning based on that order, not by
514 name; assigning the first to be a pseudo-match, the second to be
515 the actual match, and the third to be a pseudo-match. This
516 assumes that (on tmpfs) ordering within the directory is stable
517 in the absence of modification, which seems reasonably safe. */
519 /* since we're testing the fallback search, disable the readlink
521 VERIFY (umount2 ("/proc", MNT_DETACH
) == 0);
523 touch ("/dev/console1", 0);
524 touch ("/dev/console2", 0);
525 touch ("/dev/console3", 0);
529 DIR *dirstream
= opendir ("/dev");
530 VERIFY (dirstream
!= NULL
);
532 while ((d
= readdir (dirstream
)) != NULL
&& ci
< 3)
534 if (strcmp (d
->d_name
, "console1") &&
535 strcmp (d
->d_name
, "console2") &&
536 strcmp (d
->d_name
, "console3") )
538 c
[ci
++] = xasprintf ("/dev/%s", d
->d_name
);
541 VERIFY (closedir (dirstream
) == 0);
543 VERIFY (mount (slavename
, c
[0], NULL
, MS_BIND
, NULL
) == 0);
544 VERIFY (mount ("/console", c
[1], NULL
, MS_BIND
, NULL
) == 0);
545 VERIFY (mount (slavename
, c
[2], NULL
, MS_BIND
, NULL
) == 0);
546 VERIFY (umount2 ("/dev/pts", MNT_DETACH
) == 0);
547 if (!doit (slave
, "with search-path trap",
548 (struct result_r
){.name
=c
[1], .ret
=0, .err
=0}))
550 for (int i
= 0; i
< 3; i
++)
552 VERIFY (umount (c
[i
]) == 0);
553 VERIFY (unlink (c
[i
]) == 0);
564 support_become_root ();
566 int ret1
= do_in_chroot_1 (run_chroot_tests
);
567 if (ret1
== EXIT_UNSUPPORTED
)
570 int ret2
= do_in_chroot_2 (run_chroot_tests
);
571 if (ret2
== EXIT_UNSUPPORTED
)
577 #include <support/test-driver.c>