Consolidate alphasort{64} and versionsort{64} implementation
[glibc.git] / sysdeps / unix / sysv / linux / tst-ttyname.c
blob35450e1c627b8e1aee9b76fa9ea3eb553b42c08d
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/>. */
18 #include <dirent.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <limits.h>
22 #include <sched.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/mount.h>
27 #include <sys/prctl.h>
28 #include <sys/stat.h>
29 #include <sys/wait.h>
30 #include <unistd.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) \
42 do { \
43 if (!(expr)) \
44 { \
45 printf ("error: %s:%d: %s: %m\n", \
46 __FILE__, __LINE__, #expr); \
47 exit (1); \
48 } \
49 } while (0)
51 static void
52 touch (const char *path, mode_t mode)
54 xclose (xopen (path, O_WRONLY|O_CREAT|O_NOCTTY, mode));
57 static size_t
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;
66 return str_len;
69 /* returns a pointer to static storage */
70 static char *
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';
78 return target;
81 /* plain ttyname runner */
83 struct result
85 const char *name;
86 int err;
89 /* strings in result structure are in static storage */
90 static struct result
91 run_ttyname (int fd)
93 struct result ret;
94 errno = 0;
95 ret.name = ttyname (fd);
96 ret.err = errno;
97 return ret;
100 static bool
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))
109 if (expected.name)
110 expected_name = xasprintf ("\"%s\"", expected.name);
111 else
112 expected_name = xstrdup ("NULL");
114 printf ("info: ttyname: PASS {name=%s, errno=%d}\n",
115 expected_name, expected.err);
117 free (expected_name);
118 return true;
121 if (actual.name)
122 actual_name = xasprintf ("\"%s\"", actual.name);
123 else
124 actual_name = xstrdup ("NULL");
126 if (expected.name)
127 expected_name = xasprintf ("\"%s\"", expected.name);
128 else
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);
135 free (actual_name);
136 free (expected_name);
137 return false;
140 /* ttyname_r runner */
142 struct result_r
144 const char *name;
145 int ret;
146 int err;
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];
155 struct result_r ret;
156 errno = 0;
157 ret.ret = ttyname_r (fd, buf, TTY_NAME_MAX);
158 ret.err = errno;
159 if (ret.ret == 0)
160 ret.name = buf;
161 else
162 ret.name = NULL;
163 return ret;
166 static bool
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))
176 if (expected.name)
177 expected_name = xasprintf ("\"%s\"", expected.name);
178 else
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);
185 return true;
188 if (actual.name)
189 actual_name = xasprintf ("\"%s\"", actual.name);
190 else
191 actual_name = xstrdup ("NULL");
193 if (expected.name)
194 expected_name = xasprintf ("\"%s\"", expected.name);
195 else
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);
202 free (actual_name);
203 free (expected_name);
204 return false;
207 /* combined runner */
209 static bool
210 doit (int fd, const char *testname, struct result_r expected_r)
212 struct result expected = {.name=expected_r.name, .err=expected_r.ret};
213 bool ret = true;
215 printf ("info: testcase: %s\n", testname);
217 if (!eq_ttyname (run_ttyname (fd), expected))
218 ret = false;
219 if (!eq_ttyname_r (run_ttyname_r (fd), expected_r))
220 ret = false;
222 if (!ret)
223 support_record_failure ();
225 return ret;
228 /* chroot setup */
230 static char *chrootdir;
232 static void
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". */
248 static int
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. */
254 int master;
255 char *slavename;
256 master = posix_openpt (O_RDWR|O_NOCTTY|O_NONBLOCK);
257 if (master < 0)
259 if (errno == ENOENT)
260 FAIL_UNSUPPORTED ("posix_openpt: %m");
261 else
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",
268 slavename);
269 int slave = xopen (slavename, O_RDWR, 0);
270 if (!doit (slave, "basic smoketest",
271 (struct result_r){.name=slavename, .ret=0, .err=0}))
272 return 1;
274 pid_t pid = xfork ();
275 if (pid == 0)
277 xclose (master);
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",
291 MS_NOSUID|MS_NOEXEC,
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);
299 xchroot (".");
301 char *linkname = xasprintf ("/proc/self/fd/%d", slave);
302 char *target = proc_fd_readlink (linkname);
303 VERIFY (strcmp (target, slavename) == 0);
304 free (linkname);
306 _exit (cb (slavename, slave));
308 int status;
309 xwaitpid (pid, &status, 0);
310 VERIFY (WIFEXITED (status));
311 xclose (master);
312 xclose (slave);
313 return WEXITSTATUS (status);
316 static int
317 do_in_chroot_2 (int (*cb)(const char *, int))
319 printf ("info: entering chroot 2\n");
321 int pid_pipe[2];
322 xpipe (pid_pipe);
323 int exit_pipe[2];
324 xpipe (exit_pipe);
326 /* Open the PTS that we'll be testing on. */
327 int master;
328 char *slavename;
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",
334 slavename);
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 */
341 if (pid == 0)
343 xclose (master);
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}))
353 _exit (1);
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",
363 MS_NOSUID|MS_NOEXEC,
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);
371 xchroot (".");
373 if (unshare (CLONE_NEWNS | CLONE_NEWPID) < 0)
374 FAIL_UNSUPPORTED ("could not enter new PID namespace");
375 pid = xfork (); /* inner child */
376 if (pid == 0)
378 xclose (pid_pipe[1]);
380 /* wait until the outer child has exited */
381 char c;
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);
391 free (linkname);
393 _exit (cb (slavename, slave));
395 xwrite (pid_pipe[1], &pid, sizeof pid);
396 _exit (0);
398 xclose (pid_pipe[1]);
399 xclose (exit_pipe[0]);
400 xclose (exit_pipe[1]);
402 /* wait for the outer child */
403 int status;
404 xwaitpid (pid, &status, 0);
405 VERIFY (WIFEXITED (status));
406 int ret = WEXITSTATUS (status);
407 if (ret != 0)
408 return ret;
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));
417 xclose (master);
418 return WEXITSTATUS (status);
421 /* main test */
423 static int
424 run_chroot_tests (const char *slavename, int slave)
426 struct stat st;
427 bool ok = true;
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
436 instances. */
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}))
441 ok = false;
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}))
445 ok = false;
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}))
455 ok = false;
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}))
459 ok = false;
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);
471 free (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
477 necessary. */
478 if (stat (target, &st) < 0)
480 VERIFY (errno == ENOENT);
481 touch (target, 0);
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}))
488 ok = false;
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}))
496 ok = false;
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}))
503 ok = false;
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
520 happy-path */
521 VERIFY (umount2 ("/proc", MNT_DETACH) == 0);
523 touch ("/dev/console1", 0);
524 touch ("/dev/console2", 0);
525 touch ("/dev/console3", 0);
527 char *c[3];
528 int ci = 0;
529 DIR *dirstream = opendir ("/dev");
530 VERIFY (dirstream != NULL);
531 struct dirent *d;
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") )
537 continue;
538 c[ci++] = xasprintf ("/dev/%s", d->d_name);
540 VERIFY (ci == 3);
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}))
549 ok = false;
550 for (int i = 0; i < 3; i++)
552 VERIFY (umount (c[i]) == 0);
553 VERIFY (unlink (c[i]) == 0);
554 free (c[i]);
558 return ok ? 0 : 1;
561 static int
562 do_test (void)
564 support_become_root ();
566 int ret1 = do_in_chroot_1 (run_chroot_tests);
567 if (ret1 == EXIT_UNSUPPORTED)
568 return ret1;
570 int ret2 = do_in_chroot_2 (run_chroot_tests);
571 if (ret2 == EXIT_UNSUPPORTED)
572 return ret2;
574 return ret1 | ret2;
577 #include <support/test-driver.c>