1 /* Test the lock upgrade path in tst-pututxline.
2 Copyright (C) 2019-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 /* 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
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>
37 /* Path to the temporary utmp file. */
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. */
46 write_entry (pid_t pid
)
50 .ut_type
= LOGIN_PROCESS
,
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. */
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);
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. */
91 /* Overwrite the existing entry. */
92 TEST_COMPARE (utmpname (path
), 0);
95 TEST_COMPARE (errno
, 0);
96 TEST_VERIFY (write_entry (102) != NULL
);
99 TEST_COMPARE (errno
, 0);
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 ();
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
130 TEST_COMPARE (utmpname (path
), 0);
131 TEST_VERIFY (write_entry (102) == NULL
);
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. */
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
);
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). */
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
);
168 TEST_COMPARE (errno
, 0);
170 xpthread_barrier_destroy (barrier
);
171 support_shared_free (barrier
);
176 #include <support/test-driver.c>