1 /* Test case for cache invalidation after concurrent write (bug 24882).
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 <http://www.gnu.org/licenses/>. */
19 /* This test writes an entry to the utmpx file, reads it (so that it
20 is cached) in process1, and overwrites the same entry in process2
21 with something that does not match the search criteria. At this
22 point, the cache of the first process is stale, and when process1
23 attempts to write a new record which would have gone to the same
24 place (as indicated by the cache), it needs to realize that it has
25 to pick a different slot because the old slot is now used for
31 #include <support/check.h>
32 #include <support/namespace.h>
33 #include <support/support.h>
34 #include <support/temp_file.h>
35 #include <support/xthread.h>
36 #include <support/xunistd.h>
40 /* Set to the path of the utmp file. */
41 static char *utmp_file
;
43 /* Used to synchronize the subprocesses. The barrier itself is
44 allocated in shared memory. */
45 static pthread_barrier_t
*barrier
;
47 /* setutxent with error checking. */
53 TEST_COMPARE (errno
, 0);
56 /* getutxent with error checking. */
61 struct utmpx
*result
= getutxent ();
63 FAIL_EXIT1 ("getutxent: %m");
68 put_entry (const char *id
, pid_t pid
, const char *user
, const char *line
)
72 .ut_type
= LOGIN_PROCESS
,
74 .ut_host
= "localhost",
76 strcpy (ut
.ut_id
, id
);
77 strncpy (ut
.ut_user
, user
, sizeof (ut
.ut_user
));
78 strncpy (ut
.ut_line
, line
, sizeof (ut
.ut_line
));
79 TEST_VERIFY (pututxline (&ut
) != NULL
);
82 /* Use two cooperating subprocesses to avoid issues related to
83 unlock-on-close semantics of POSIX advisory locks. */
85 static __attribute__ ((noreturn
)) void
88 TEST_COMPARE (utmpname (utmp_file
), 0);
90 /* Create an entry. */
92 put_entry ("1", 101, "root", "process1");
94 /* Retrieve the entry. This will fill the internal cache. */
98 TEST_COMPARE (errno
, 0);
101 .ut_type
= LOGIN_PROCESS
,
102 .ut_line
= "process1",
104 struct utmpx
*result
= getutxline (&ut
);
106 FAIL_EXIT1 ("getutxline (\"process1\"): %m");
107 TEST_COMPARE (result
->ut_pid
, 101);
110 /* Signal the other process to overwrite the entry. */
111 xpthread_barrier_wait (barrier
);
113 /* Wait for the other process to complete the write operation. */
114 xpthread_barrier_wait (barrier
);
116 /* Add another entry. Note: This time, there is no setutxent call. */
117 put_entry ("1", 103, "root", "process1");
123 process2 (void *closure
)
125 /* Wait for the first process to write its entry. */
126 xpthread_barrier_wait (barrier
);
128 /* Truncate the file. The glibc interface does not support
129 re-purposing records, but an external expiration mechanism may
131 TEST_COMPARE (truncate64 (utmp_file
, 0), 0);
133 /* Write the replacement entry. */
134 TEST_COMPARE (utmpname (utmp_file
), 0);
136 put_entry ("2", 102, "user", "process2");
138 /* Signal the other process that the entry has been replaced. */
139 xpthread_barrier_wait (barrier
);
145 xclose (create_temp_file ("tst-tumpx-cache-write-", &utmp_file
));
147 pthread_barrierattr_t attr
;
148 xpthread_barrierattr_init (&attr
);
149 xpthread_barrierattr_setpshared (&attr
, PTHREAD_SCOPE_PROCESS
);
150 barrier
= support_shared_allocate (sizeof (*barrier
));
151 xpthread_barrier_init (barrier
, &attr
, 2);
154 /* Run both subprocesses in parallel. */
156 pid_t pid1
= xfork ();
159 support_isolate_in_subprocess (process2
, NULL
);
161 xwaitpid (pid1
, &status
, 0);
162 TEST_COMPARE (status
, 0);
165 /* Check that the utmpx database contains the expected records. */
167 TEST_COMPARE (utmpname (utmp_file
), 0);
170 struct utmpx
*ut
= xgetutxent ();
171 TEST_COMPARE_STRING (ut
->ut_id
, "2");
172 TEST_COMPARE (ut
->ut_pid
, 102);
173 TEST_COMPARE_STRING (ut
->ut_user
, "user");
174 TEST_COMPARE_STRING (ut
->ut_line
, "process2");
177 TEST_COMPARE_STRING (ut
->ut_id
, "1");
178 TEST_COMPARE (ut
->ut_pid
, 103);
179 TEST_COMPARE_STRING (ut
->ut_user
, "root");
180 TEST_COMPARE_STRING (ut
->ut_line
, "process1");
182 if (getutxent () != NULL
)
183 FAIL_EXIT1 ("additional utmpx entry");
186 xpthread_barrier_destroy (barrier
);
187 support_shared_free (barrier
);
193 #include <support/test-driver.c>