Stop applying a GCC-specific workaround on clang [BZ #30550]
[glibc.git] / login / tst-pututxline-lockfail.c
blob214d11061948794d2dc05aa59fee7923c942397c
1 /* Test the lock upgrade path in tst-pututxline.
2 Copyright (C) 2019-2023 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 /* pututxline upgrades the read lock on the file to a write lock.
20 This test verifies that if the lock upgrade fails, the utmp
21 subsystem remains in a consistent state, so that pututxline can be
22 called again. */
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <stdlib.h>
27 #include <support/check.h>
28 #include <support/namespace.h>
29 #include <support/support.h>
30 #include <support/temp_file.h>
31 #include <support/xthread.h>
32 #include <support/xunistd.h>
33 #include <unistd.h>
34 #include <utmp.h>
35 #include <utmpx.h>
37 /* Path to the temporary utmp file. */
38 static char *path;
40 /* Used to synchronize the subprocesses. The barrier itself is
41 allocated in shared memory. */
42 static pthread_barrier_t *barrier;
44 /* Use pututxline to write an entry for PID. */
45 static struct utmpx *
46 write_entry (pid_t pid)
48 struct utmpx ut =
50 .ut_type = LOGIN_PROCESS,
51 .ut_id = "1",
52 .ut_user = "root",
53 .ut_pid = pid,
54 .ut_line = "entry",
55 .ut_host = "localhost",
57 return pututxline (&ut);
60 /* Create the initial entry in a subprocess, so that the utmp
61 subsystem in the original process is not disturbed. */
62 static void
63 subprocess_create_entry (void *closure)
65 TEST_COMPARE (utmpname (path), 0);
66 TEST_VERIFY (write_entry (101) != NULL);
69 /* Acquire an advisory read lock on PATH. */
70 __attribute__ ((noreturn)) static void
71 subprocess_lock_file (void)
73 int fd = xopen (path, O_RDONLY, 0);
75 struct flock64 fl =
77 .l_type = F_RDLCK,
78 fl.l_whence = SEEK_SET,
80 TEST_COMPARE (fcntl64 (fd, F_SETLKW, &fl), 0);
82 /* Signal to the main process that the lock has been acquired. */
83 xpthread_barrier_wait (barrier);
85 /* Wait for the unlock request from the main process. */
86 xpthread_barrier_wait (barrier);
88 /* Implicitly unlock the file. */
89 xclose (fd);
91 /* Overwrite the existing entry. */
92 TEST_COMPARE (utmpname (path), 0);
93 errno = 0;
94 setutxent ();
95 TEST_COMPARE (errno, 0);
96 TEST_VERIFY (write_entry (102) != NULL);
97 errno = 0;
98 endutxent ();
99 TEST_COMPARE (errno, 0);
101 _exit (0);
104 static int
105 do_test (void)
107 xclose (create_temp_file ("tst-pututxline-lockfail-", &path));
110 pthread_barrierattr_t attr;
111 xpthread_barrierattr_init (&attr);
112 xpthread_barrierattr_setpshared (&attr, PTHREAD_SCOPE_PROCESS);
113 barrier = support_shared_allocate (sizeof (*barrier));
114 xpthread_barrier_init (barrier, &attr, 2);
115 xpthread_barrierattr_destroy (&attr);
118 /* Write the initial entry. */
119 support_isolate_in_subprocess (subprocess_create_entry, NULL);
121 pid_t locker_pid = xfork ();
122 if (locker_pid == 0)
123 subprocess_lock_file ();
125 /* Wait for the file locking to complete. */
126 xpthread_barrier_wait (barrier);
128 /* Try to add another entry. This attempt will fail, with EINTR or
129 EAGAIN. */
130 TEST_COMPARE (utmpname (path), 0);
131 TEST_VERIFY (write_entry (102) == NULL);
132 if (errno != EINTR)
133 TEST_COMPARE (errno, EAGAIN);
135 /* Signal the subprocess to overwrite the entry. */
136 xpthread_barrier_wait (barrier);
138 /* Wait for write and unlock to complete. */
140 int status;
141 xwaitpid (locker_pid, &status, 0);
142 TEST_COMPARE (status, 0);
145 /* The file is no longer locked, so this operation will succeed. */
146 TEST_VERIFY (write_entry (103) != NULL);
147 errno = 0;
148 endutxent ();
149 TEST_COMPARE (errno, 0);
151 /* Check that there is just one entry with the expected contents.
152 If pututxline becomes desynchronized internally, the entry is not
153 overwritten (bug 24902). */
154 errno = 0;
155 setutxent ();
156 TEST_COMPARE (errno, 0);
157 struct utmpx *ut = getutxent ();
158 TEST_VERIFY_EXIT (ut != NULL);
159 TEST_COMPARE (ut->ut_type, LOGIN_PROCESS);
160 TEST_COMPARE_STRING (ut->ut_id, "1");
161 TEST_COMPARE_STRING (ut->ut_user, "root");
162 TEST_COMPARE (ut->ut_pid, 103);
163 TEST_COMPARE_STRING (ut->ut_line, "entry");
164 TEST_COMPARE_STRING (ut->ut_host, "localhost");
165 TEST_VERIFY (getutxent () == NULL);
166 errno = 0;
167 endutxent ();
168 TEST_COMPARE (errno, 0);
170 xpthread_barrier_destroy (barrier);
171 support_shared_free (barrier);
172 free (path);
173 return 0;
176 #include <support/test-driver.c>