difftime is pure, not const
[glibc.git] / sysdeps / unix / sysv / linux / tst-ttyname-common.c
blob2f2a36da0465460528548c2eca379d95017769e9
1 /* Common definitions for ttyname tests.
2 Copyright (C) 2017-2024 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public License as
7 published by the Free Software Foundation; either version 2.1 of the
8 License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; see the file COPYING.LIB. If
17 not, see <https://www.gnu.org/licenses/>. */
19 #include <dirent.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <limits.h>
23 #include <stdbool.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/mount.h>
28 #include <sys/resource.h>
29 #include <sys/stat.h>
30 #include <unistd.h>
32 #include <support/check.h>
33 #include <support/support.h>
34 #include <support/temp_file.h>
35 #include <support/test-driver.h>
36 #include <support/xunistd.h>
38 /* generic utilities */
40 #define VERIFY(expr) \
41 do { \
42 if (!(expr)) \
43 { \
44 printf ("error: %s:%d: %s: %m\n", \
45 __FILE__, __LINE__, #expr); \
46 exit (1); \
47 } \
48 } while (0)
50 static void
51 touch (const char *path, mode_t mode)
53 xclose (xopen (path, O_WRONLY|O_CREAT|O_NOCTTY, mode));
56 static size_t
57 trim_prefix (char *str, size_t str_len, const char *prefix)
59 size_t prefix_len = strlen (prefix);
60 if (str_len > prefix_len && memcmp (str, prefix, prefix_len) == 0)
62 memmove (str, str + prefix_len, str_len - prefix_len);
63 return str_len - prefix_len;
65 return str_len;
68 /* returns a pointer to static storage */
69 static char *
70 proc_fd_readlink (const char *linkname)
72 static char target[PATH_MAX+1];
73 ssize_t target_len = readlink (linkname, target, PATH_MAX);
74 VERIFY (target_len > 0);
75 target_len = trim_prefix (target, target_len, "(unreachable)");
76 target[target_len] = '\0';
77 return target;
80 /* plain ttyname runner */
82 struct result
84 const char *name;
85 int err;
88 /* strings in result structure are in static storage */
89 static struct result
90 run_ttyname (int fd)
92 struct result ret;
93 errno = 0;
94 ret.name = ttyname (fd);
95 ret.err = errno;
96 return ret;
99 static bool
100 eq_ttyname (struct result actual, struct result expected)
102 char *actual_name, *expected_name;
104 if ((actual.err == expected.err)
105 && (!actual.name == !expected.name)
106 && (actual.name ? strcmp (actual.name, expected.name) == 0 : true))
108 if (expected.name)
109 expected_name = xasprintf ("\"%s\"", expected.name);
110 else
111 expected_name = xstrdup ("NULL");
113 printf ("info: ttyname: PASS {name=%s, errno=%d}\n",
114 expected_name, expected.err);
116 free (expected_name);
117 return true;
120 if (actual.name)
121 actual_name = xasprintf ("\"%s\"", actual.name);
122 else
123 actual_name = xstrdup ("NULL");
125 if (expected.name)
126 expected_name = xasprintf ("\"%s\"", expected.name);
127 else
128 expected_name = xstrdup ("NULL");
130 printf ("error: ttyname: actual {name=%s, errno=%d} != expected {name=%s, errno=%d}\n",
131 actual_name, actual.err,
132 expected_name, expected.err);
134 free (actual_name);
135 free (expected_name);
136 return false;
139 /* ttyname_r runner */
141 struct result_r
143 const char *name;
144 int ret;
145 int err;
148 /* strings in result structure are in static storage */
149 static struct result_r
150 run_ttyname_r (int fd)
152 static char buf[TTY_NAME_MAX];
154 struct result_r ret;
155 errno = 0;
156 ret.ret = ttyname_r (fd, buf, TTY_NAME_MAX);
157 ret.err = errno;
158 if (ret.ret == 0)
159 ret.name = buf;
160 else
161 ret.name = NULL;
162 return ret;
165 static bool
166 eq_ttyname_r (struct result_r actual, struct result_r expected)
168 char *actual_name, *expected_name;
170 if ((actual.err == expected.err)
171 && (actual.ret == expected.ret)
172 && (!actual.name == !expected.name)
173 && (actual.name ? strcmp (actual.name, expected.name) == 0 : true))
175 if (expected.name)
176 expected_name = xasprintf ("\"%s\"", expected.name);
177 else
178 expected_name = xstrdup ("NULL");
180 printf ("info: ttyname_r: PASS {name=%s, ret=%d, errno=%d}\n",
181 expected_name, expected.ret, expected.err);
183 free (expected_name);
184 return true;
187 if (actual.name)
188 actual_name = xasprintf ("\"%s\"", actual.name);
189 else
190 actual_name = xstrdup ("NULL");
192 if (expected.name)
193 expected_name = xasprintf ("\"%s\"", expected.name);
194 else
195 expected_name = xstrdup ("NULL");
197 printf ("error: ttyname_r: actual {name=%s, ret=%d, errno=%d} != expected {name=%s, ret=%d, errno=%d}\n",
198 actual_name, actual.ret, actual.err,
199 expected_name, expected.ret, expected.err);
201 free (actual_name);
202 free (expected_name);
203 return false;
206 /* combined runner */
208 static bool
209 doit (int fd, const char *testname, struct result_r expected_r)
211 struct result expected = {.name=expected_r.name, .err=expected_r.ret};
212 bool ret = true;
214 printf ("info: testcase: %s\n", testname);
216 if (!eq_ttyname (run_ttyname (fd), expected))
217 ret = false;
218 if (!eq_ttyname_r (run_ttyname_r (fd), expected_r))
219 ret = false;
221 if (!ret)
222 support_record_failure ();
224 return ret;
227 /* chroot setup */
229 static char *chrootdir;
231 static void
232 prepare (int argc, char **argv)
234 chrootdir = xasprintf ("%s/tst-ttyname-XXXXXX", test_dir);
235 if (mkdtemp (chrootdir) == NULL)
236 FAIL_EXIT1 ("mkdtemp (\"%s\"): %m", chrootdir);
237 add_temp_file (chrootdir);
239 #define PREPARE prepare
241 /* Adjust the file limit so that we have a chance to open PTY. */
242 static void
243 adjust_file_limit (const char *pty)
245 int number = -1;
246 if (sscanf (pty, "/dev/pts/%d", &number) != 1 || number < 0)
247 FAIL_EXIT1 ("invalid PTY name: \"%s\"", pty);
249 /* Add a few additional descriptors to cover standard I/O streams
250 etc. */
251 rlim_t desired_limit = number + 10;
253 struct rlimit lim;
254 if (getrlimit (RLIMIT_NOFILE, &lim) != 0)
255 FAIL_EXIT1 ("getrlimit (RLIMIT_NOFILE): %m");
256 if (lim.rlim_cur < desired_limit)
258 printf ("info: adjusting RLIMIT_NOFILE from %llu to %llu\n",
259 (unsigned long long int) lim.rlim_cur,
260 (unsigned long long int) desired_limit);
261 lim.rlim_cur = desired_limit;
262 if (setrlimit (RLIMIT_NOFILE, &lim) != 0)
263 printf ("warning: setrlimit (RLIMIT_NOFILE) failed: %m\n");
267 /* main test */
269 static int
270 run_chroot_tests (const char *slavename, int slave)
272 struct stat st;
273 bool ok = true;
275 /* There are 3 groups of tests here. The first group fairly
276 generically does things known to mess up ttyname, and verifies
277 that ttyname copes correctly. The remaining groups are
278 increasingly convoluted, as we target specific parts of ttyname
279 to try to confuse. */
281 /* Basic tests that it doesn't get confused by multiple devpts
282 instances. */
284 VERIFY (stat (slavename, &st) < 0); /* sanity check */
285 if (!doit (slave, "no conflict, no match",
286 (struct result_r){.name=NULL, .ret=ENODEV, .err=ENODEV}))
287 ok = false;
288 VERIFY (mount ("/console", "/dev/console", NULL, MS_BIND, NULL) == 0);
289 if (!doit (slave, "no conflict, console",
290 (struct result_r){.name="/dev/console", .ret=0, .err=0}))
291 ok = false;
292 VERIFY (umount ("/dev/console") == 0);
294 /* Keep creating PTYs until we we get a name collision. */
295 while (true)
297 if (stat (slavename, &st) == 0)
298 break;
299 if (posix_openpt (O_RDWR|O_NOCTTY|O_NONBLOCK) < 0)
301 if (errno == ENOSPC || errno == EMFILE || errno == ENFILE)
302 FAIL_UNSUPPORTED ("cannot re-create PTY \"%s\" in chroot: %m"
303 " (consider increasing limits)", slavename);
304 else
305 FAIL_EXIT1 ("cannot re-create PTY \"%s\" chroot: %m", slavename);
309 if (!doit (slave, "conflict, no match",
310 (struct result_r){.name=NULL, .ret=ENODEV, .err=ENODEV}))
311 ok = false;
312 VERIFY (mount ("/console", "/dev/console", NULL, MS_BIND, NULL) == 0);
313 if (!doit (slave, "conflict, console",
314 (struct result_r){.name="/dev/console", .ret=0, .err=0}))
315 ok = false;
316 VERIFY (umount ("/dev/console") == 0);
319 /* The first tests kinda assumed that they hit certain code-paths
320 based on assuming that the readlink target is 'slavename', but
321 that's not quite always true. They're still a good preliminary
322 sanity check, so keep them, but let's add tests that make sure
323 that those code-paths are hit by doing a readlink ourself. */
325 char *linkname = xasprintf ("/proc/self/fd/%d", slave);
326 char *target = proc_fd_readlink (linkname);
327 free (linkname);
328 /* Depending on how we set up the chroot, the kernel may or may not
329 trim the leading path to the target (it may give us "/6",
330 instead of "/dev/pts/6"). We test it both ways (do_in_chroot_1
331 and do_in_chroot_2). This test group relies on the target
332 existing, so guarantee that it does exist by creating it if
333 necessary. */
334 if (stat (target, &st) < 0)
336 VERIFY (errno == ENOENT);
337 touch (target, 0);
340 VERIFY (mount ("/console", "/dev/console", NULL, MS_BIND, NULL) == 0);
341 VERIFY (mount ("/console", target, NULL, MS_BIND, NULL) == 0);
342 if (!doit (slave, "with readlink target",
343 (struct result_r){.name=target, .ret=0, .err=0}))
344 ok = false;
345 VERIFY (umount (target) == 0);
346 VERIFY (umount ("/dev/console") == 0);
348 VERIFY (mount ("/console", "/dev/console", NULL, MS_BIND, NULL) == 0);
349 VERIFY (mount (slavename, target, NULL, MS_BIND, NULL) == 0);
350 if (!doit (slave, "with readlink trap; fallback",
351 (struct result_r){.name="/dev/console", .ret=0, .err=0}))
352 ok = false;
353 VERIFY (umount (target) == 0);
354 VERIFY (umount ("/dev/console") == 0);
356 VERIFY (mount (slavename, target, NULL, MS_BIND, NULL) == 0);
357 if (!doit (slave, "with readlink trap; no fallback",
358 (struct result_r){.name=NULL, .ret=ENODEV, .err=ENODEV}))
359 ok = false;
360 VERIFY (umount (target) == 0);
363 /* This test makes sure that everything still works OK if readdir
364 finds a pseudo-match before and/or after the actual match. Now,
365 to do that, we need to control that readdir finds the
366 pseudo-matches before and after the actual match; and there's no
367 good way to control that order in absence of whitebox testing.
368 So, just create 3 files, then use opendir/readdir to see what
369 order they are in, and assign meaning based on that order, not by
370 name; assigning the first to be a pseudo-match, the second to be
371 the actual match, and the third to be a pseudo-match. This
372 assumes that (on tmpfs) ordering within the directory is stable
373 in the absence of modification, which seems reasonably safe. */
375 /* since we're testing the fallback search, disable the readlink
376 happy-path */
377 VERIFY (umount2 ("/proc", MNT_DETACH) == 0);
379 touch ("/dev/console1", 0);
380 touch ("/dev/console2", 0);
381 touch ("/dev/console3", 0);
383 char *c[3];
384 int ci = 0;
385 DIR *dirstream = opendir ("/dev");
386 VERIFY (dirstream != NULL);
387 struct dirent *d;
388 while ((d = readdir (dirstream)) != NULL && ci < 3)
390 if (strcmp (d->d_name, "console1")
391 && strcmp (d->d_name, "console2")
392 && strcmp (d->d_name, "console3") )
393 continue;
394 c[ci++] = xasprintf ("/dev/%s", d->d_name);
396 VERIFY (ci == 3);
397 VERIFY (closedir (dirstream) == 0);
399 VERIFY (mount (slavename, c[0], NULL, MS_BIND, NULL) == 0);
400 VERIFY (mount ("/console", c[1], NULL, MS_BIND, NULL) == 0);
401 VERIFY (mount (slavename, c[2], NULL, MS_BIND, NULL) == 0);
402 VERIFY (umount2 ("/dev/pts", MNT_DETACH) == 0);
403 if (!doit (slave, "with search-path trap",
404 (struct result_r){.name=c[1], .ret=0, .err=0}))
405 ok = false;
406 for (int i = 0; i < 3; i++)
408 VERIFY (umount (c[i]) == 0);
409 VERIFY (unlink (c[i]) == 0);
410 free (c[i]);
414 return ok ? 0 : 1;