Blame login/tst-pututxline-cache.c

Packit Service 175458
/* Test case for cache invalidation after concurrent write (bug 24882).
Packit Service 175458
   Copyright (C) 2019 Free Software Foundation, Inc.
Packit Service 175458
   This file is part of the GNU C Library.
Packit Service 175458
Packit Service 175458
   The GNU C Library is free software; you can redistribute it and/or
Packit Service 175458
   modify it under the terms of the GNU Lesser General Public License as
Packit Service 175458
   published by the Free Software Foundation; either version 2.1 of the
Packit Service 175458
   License, or (at your option) any later version.
Packit Service 175458
Packit Service 175458
   The GNU C Library is distributed in the hope that it will be useful,
Packit Service 175458
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 175458
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 175458
   Lesser General Public License for more details.
Packit Service 175458
Packit Service 175458
   You should have received a copy of the GNU Lesser General Public
Packit Service 175458
   License along with the GNU C Library; see the file COPYING.LIB.  If
Packit Service 175458
   not, see <http://www.gnu.org/licenses/>.  */
Packit Service 175458
Packit Service 175458
/* This test writes an entry to the utmpx file, reads it (so that it
Packit Service 175458
   is cached) in process1, and overwrites the same entry in process2
Packit Service 175458
   with something that does not match the search criteria.  At this
Packit Service 175458
   point, the cache of the first process is stale, and when process1
Packit Service 175458
   attempts to write a new record which would have gone to the same
Packit Service 175458
   place (as indicated by the cache), it needs to realize that it has
Packit Service 175458
   to pick a different slot because the old slot is now used for
Packit Service 175458
   something else.  */
Packit Service 175458
Packit Service 175458
#include <errno.h>
Packit Service 175458
#include <stdlib.h>
Packit Service 175458
#include <string.h>
Packit Service 175458
#include <support/check.h>
Packit Service 175458
#include <support/namespace.h>
Packit Service 175458
#include <support/support.h>
Packit Service 175458
#include <support/temp_file.h>
Packit Service 175458
#include <support/xthread.h>
Packit Service 175458
#include <support/xunistd.h>
Packit Service 175458
#include <utmp.h>
Packit Service 175458
#include <utmpx.h>
Packit Service 175458
Packit Service 175458
/* Set to the path of the utmp file.  */
Packit Service 175458
static char *utmp_file;
Packit Service 175458
Packit Service 175458
/* Used to synchronize the subprocesses.  The barrier itself is
Packit Service 175458
   allocated in shared memory.  */
Packit Service 175458
static pthread_barrier_t *barrier;
Packit Service 175458
Packit Service 175458
/* setutxent with error checking.  */
Packit Service 175458
static void
Packit Service 175458
xsetutxent (void)
Packit Service 175458
{
Packit Service 175458
  errno = 0;
Packit Service 175458
  setutxent ();
Packit Service 175458
  TEST_COMPARE (errno, 0);
Packit Service 175458
}
Packit Service 175458
Packit Service 175458
/* getutxent with error checking.  */
Packit Service 175458
static struct utmpx *
Packit Service 175458
xgetutxent (void)
Packit Service 175458
{
Packit Service 175458
  errno = 0;
Packit Service 175458
  struct utmpx *result = getutxent ();
Packit Service 175458
  if (result == NULL)
Packit Service 175458
    FAIL_EXIT1 ("getutxent: %m");
Packit Service 175458
  return result;
Packit Service 175458
}
Packit Service 175458
Packit Service 175458
static void
Packit Service 175458
put_entry (const char *id, pid_t pid, const char *user, const char *line)
Packit Service 175458
{
Packit Service 175458
  struct utmpx ut =
Packit Service 175458
    {
Packit Service 175458
     .ut_type = LOGIN_PROCESS,
Packit Service 175458
     .ut_pid = pid,
Packit Service 175458
     .ut_host = "localhost",
Packit Service 175458
    };
Packit Service 175458
  strcpy (ut.ut_id, id);
Packit Service 175458
  strncpy (ut.ut_user, user, sizeof (ut.ut_user));
Packit Service 175458
  strncpy (ut.ut_line, line, sizeof (ut.ut_line));
Packit Service 175458
  TEST_VERIFY (pututxline (&ut) != NULL);
Packit Service 175458
}
Packit Service 175458
Packit Service 175458
/* Use two cooperating subprocesses to avoid issues related to
Packit Service 175458
   unlock-on-close semantics of POSIX advisory locks.  */
Packit Service 175458
Packit Service 175458
static __attribute__ ((noreturn)) void
Packit Service 175458
process1 (void)
Packit Service 175458
{
Packit Service 175458
  TEST_COMPARE (utmpname (utmp_file), 0);
Packit Service 175458
Packit Service 175458
  /* Create an entry.  */
Packit Service 175458
  xsetutxent ();
Packit Service 175458
  put_entry ("1", 101, "root", "process1");
Packit Service 175458
Packit Service 175458
  /* Retrieve the entry.  This will fill the internal cache.  */
Packit Service 175458
  {
Packit Service 175458
    errno = 0;
Packit Service 175458
    setutxent ();
Packit Service 175458
    TEST_COMPARE (errno, 0);
Packit Service 175458
    struct utmpx ut =
Packit Service 175458
      {
Packit Service 175458
       .ut_type = LOGIN_PROCESS,
Packit Service 175458
       .ut_line = "process1",
Packit Service 175458
      };
Packit Service 175458
    struct utmpx *result = getutxline (&ut);
Packit Service 175458
    if (result == NULL)
Packit Service 175458
      FAIL_EXIT1 ("getutxline (\"process1\"): %m");
Packit Service 175458
    TEST_COMPARE (result->ut_pid, 101);
Packit Service 175458
  }
Packit Service 175458
Packit Service 175458
  /* Signal the other process to overwrite the entry.  */
Packit Service 175458
  xpthread_barrier_wait (barrier);
Packit Service 175458
Packit Service 175458
  /* Wait for the other process to complete the write operation.  */
Packit Service 175458
  xpthread_barrier_wait (barrier);
Packit Service 175458
Packit Service 175458
  /* Add another entry.  Note: This time, there is no setutxent call.  */
Packit Service 175458
  put_entry ("1", 103, "root", "process1");
Packit Service 175458
Packit Service 175458
  _exit (0);
Packit Service 175458
}
Packit Service 175458
Packit Service 175458
static void
Packit Service 175458
process2 (void *closure)
Packit Service 175458
{
Packit Service 175458
  /* Wait for the first process to write its entry.  */
Packit Service 175458
  xpthread_barrier_wait (barrier);
Packit Service 175458
Packit Service 175458
  /* Truncate the file.  The glibc interface does not support
Packit Service 175458
     re-purposing records, but an external expiration mechanism may
Packit Service 175458
     trigger this.  */
Packit Service 175458
  TEST_COMPARE (truncate64 (utmp_file, 0), 0);
Packit Service 175458
Packit Service 175458
  /* Write the replacement entry.  */
Packit Service 175458
  TEST_COMPARE (utmpname (utmp_file), 0);
Packit Service 175458
  xsetutxent ();
Packit Service 175458
  put_entry ("2", 102, "user", "process2");
Packit Service 175458
Packit Service 175458
  /* Signal the other process that the entry has been replaced.  */
Packit Service 175458
  xpthread_barrier_wait (barrier);
Packit Service 175458
}
Packit Service 175458
Packit Service 175458
static int
Packit Service 175458
do_test (void)
Packit Service 175458
{
Packit Service 175458
  xclose (create_temp_file ("tst-tumpx-cache-write-", &utmp_file));
Packit Service 175458
  {
Packit Service 175458
    pthread_barrierattr_t attr;
Packit Service 175458
    xpthread_barrierattr_init (&attr);
Packit Service 175458
    xpthread_barrierattr_setpshared (&attr, PTHREAD_SCOPE_PROCESS);
Packit Service 175458
    barrier = support_shared_allocate (sizeof (*barrier));
Packit Service 175458
    xpthread_barrier_init (barrier, &attr, 2);
Packit Service 175458
  }
Packit Service 175458
Packit Service 175458
  /* Run both subprocesses in parallel.  */
Packit Service 175458
  {
Packit Service 175458
    pid_t pid1 = xfork ();
Packit Service 175458
    if (pid1 == 0)
Packit Service 175458
      process1 ();
Packit Service 175458
    support_isolate_in_subprocess (process2, NULL);
Packit Service 175458
    int status;
Packit Service 175458
    xwaitpid (pid1, &status, 0);
Packit Service 175458
    TEST_COMPARE (status, 0);
Packit Service 175458
  }
Packit Service 175458
Packit Service 175458
  /* Check that the utmpx database contains the expected records.  */
Packit Service 175458
  {
Packit Service 175458
    TEST_COMPARE (utmpname (utmp_file), 0);
Packit Service 175458
    xsetutxent ();
Packit Service 175458
Packit Service 175458
    struct utmpx *ut = xgetutxent ();
Packit Service 175458
    TEST_COMPARE_STRING (ut->ut_id, "2");
Packit Service 175458
    TEST_COMPARE (ut->ut_pid, 102);
Packit Service 175458
    TEST_COMPARE_STRING (ut->ut_user, "user");
Packit Service 175458
    TEST_COMPARE_STRING (ut->ut_line, "process2");
Packit Service 175458
Packit Service 175458
    ut = xgetutxent ();
Packit Service 175458
    TEST_COMPARE_STRING (ut->ut_id, "1");
Packit Service 175458
    TEST_COMPARE (ut->ut_pid, 103);
Packit Service 175458
    TEST_COMPARE_STRING (ut->ut_user, "root");
Packit Service 175458
    TEST_COMPARE_STRING (ut->ut_line, "process1");
Packit Service 175458
Packit Service 175458
    if (getutxent () != NULL)
Packit Service 175458
      FAIL_EXIT1 ("additional utmpx entry");
Packit Service 175458
  }
Packit Service 175458
Packit Service 175458
  xpthread_barrier_destroy (barrier);
Packit Service 175458
  support_shared_free (barrier);
Packit Service 175458
  free (utmp_file);
Packit Service 175458
Packit Service 175458
  return 0;
Packit Service 175458
}
Packit Service 175458
Packit Service 175458
#include <support/test-driver.c>