tst-pututxline-cache.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. /* Test case for cache invalidation after concurrent write (bug 24882).
  2. Copyright (C) 2019-2026 Free Software Foundation, Inc.
  3. 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.
  8. The GNU C Library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public
  13. License along with the GNU C Library; see the file COPYING.LIB. If
  14. not, see <http://www.gnu.org/licenses/>. */
  15. /* This test writes an entry to the utmpx file, reads it (so that it
  16. is cached) in process1, and overwrites the same entry in process2
  17. with something that does not match the search criteria. At this
  18. point, the cache of the first process is stale, and when process1
  19. attempts to write a new record which would have gone to the same
  20. place (as indicated by the cache), it needs to realize that it has
  21. to pick a different slot because the old slot is now used for
  22. something else. */
  23. #include <errno.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #include <support/check.h>
  27. #include <support/namespace.h>
  28. #include <support/support.h>
  29. #include <support/temp_file.h>
  30. #include <support/xthread.h>
  31. #include <support/xunistd.h>
  32. #include <utmp.h>
  33. #include <utmpx.h>
  34. /* Set to the path of the utmp file. */
  35. static char *utmp_file;
  36. /* Used to synchronize the subprocesses. The barrier itself is
  37. allocated in shared memory. */
  38. static pthread_barrier_t *barrier;
  39. /* setutxent with error checking. */
  40. static void
  41. xsetutxent (void)
  42. {
  43. errno = 0;
  44. setutxent ();
  45. TEST_COMPARE (errno, 0);
  46. }
  47. /* getutxent with error checking. */
  48. static struct utmpx *
  49. xgetutxent (void)
  50. {
  51. errno = 0;
  52. struct utmpx *result = getutxent ();
  53. if (result == NULL)
  54. FAIL_EXIT1 ("getutxent: %m");
  55. return result;
  56. }
  57. static void
  58. put_entry (const char *id, pid_t pid, const char *user, const char *line)
  59. {
  60. struct utmpx ut =
  61. {
  62. .ut_type = LOGIN_PROCESS,
  63. .ut_pid = pid,
  64. .ut_host = "localhost",
  65. };
  66. strcpy (ut.ut_id, id);
  67. strncpy (ut.ut_user, user, sizeof (ut.ut_user));
  68. strncpy (ut.ut_line, line, sizeof (ut.ut_line));
  69. TEST_VERIFY (pututxline (&ut) != NULL);
  70. }
  71. /* Use two cooperating subprocesses to avoid issues related to
  72. unlock-on-close semantics of POSIX advisory locks. */
  73. static __attribute__ ((noreturn)) void
  74. process1 (void)
  75. {
  76. TEST_COMPARE (utmpname (utmp_file), 0);
  77. /* Create an entry. */
  78. xsetutxent ();
  79. put_entry ("1", 101, "root", "process1");
  80. /* Retrieve the entry. This will fill the internal cache. */
  81. {
  82. errno = 0;
  83. setutxent ();
  84. TEST_COMPARE (errno, 0);
  85. struct utmpx ut =
  86. {
  87. .ut_type = LOGIN_PROCESS,
  88. .ut_line = "process1",
  89. };
  90. struct utmpx *result = getutxline (&ut);
  91. if (result == NULL)
  92. FAIL_EXIT1 ("getutxline (\"process1\"): %m");
  93. TEST_COMPARE (result->ut_pid, 101);
  94. }
  95. /* Signal the other process to overwrite the entry. */
  96. xpthread_barrier_wait (barrier);
  97. /* Wait for the other process to complete the write operation. */
  98. xpthread_barrier_wait (barrier);
  99. /* Add another entry. Note: This time, there is no setutxent call. */
  100. put_entry ("1", 103, "root", "process1");
  101. _exit (0);
  102. }
  103. static void
  104. process2 (void *closure)
  105. {
  106. /* Wait for the first process to write its entry. */
  107. xpthread_barrier_wait (barrier);
  108. /* Truncate the file. The glibc interface does not support
  109. re-purposing records, but an external expiration mechanism may
  110. trigger this. */
  111. TEST_COMPARE (truncate64 (utmp_file, 0), 0);
  112. /* Write the replacement entry. */
  113. TEST_COMPARE (utmpname (utmp_file), 0);
  114. xsetutxent ();
  115. put_entry ("2", 102, "user", "process2");
  116. /* Signal the other process that the entry has been replaced. */
  117. xpthread_barrier_wait (barrier);
  118. }
  119. static int
  120. do_test (void)
  121. {
  122. xclose (create_temp_file ("tst-tumpx-cache-write-", &utmp_file));
  123. {
  124. pthread_barrierattr_t attr;
  125. xpthread_barrierattr_init (&attr);
  126. xpthread_barrierattr_setpshared (&attr, PTHREAD_SCOPE_PROCESS);
  127. barrier = support_shared_allocate (sizeof (*barrier));
  128. xpthread_barrier_init (barrier, &attr, 2);
  129. }
  130. /* Run both subprocesses in parallel. */
  131. {
  132. pid_t pid1 = xfork ();
  133. if (pid1 == 0)
  134. process1 ();
  135. support_isolate_in_subprocess (process2, NULL);
  136. int status;
  137. xwaitpid (pid1, &status, 0);
  138. TEST_COMPARE (status, 0);
  139. }
  140. /* Check that the utmpx database contains the expected records. */
  141. {
  142. TEST_COMPARE (utmpname (utmp_file), 0);
  143. xsetutxent ();
  144. struct utmpx *ut = xgetutxent ();
  145. TEST_COMPARE_STRING (ut->ut_id, "2");
  146. TEST_COMPARE (ut->ut_pid, 102);
  147. TEST_COMPARE_STRING (ut->ut_user, "user");
  148. TEST_COMPARE_STRING (ut->ut_line, "process2");
  149. ut = xgetutxent ();
  150. TEST_COMPARE_STRING (ut->ut_id, "1");
  151. TEST_COMPARE (ut->ut_pid, 103);
  152. TEST_COMPARE_STRING (ut->ut_user, "root");
  153. TEST_COMPARE_STRING (ut->ut_line, "process1");
  154. if (getutxent () != NULL)
  155. FAIL_EXIT1 ("additional utmpx entry");
  156. }
  157. xpthread_barrier_destroy (barrier);
  158. support_shared_free (barrier);
  159. free (utmp_file);
  160. return 0;
  161. }
  162. #include <support/test-driver.c>