Blame sunrpc/tst-svc_register.c

Packit Service 82fcde
/* Test svc_register/svc_unregister rpcbind interaction (bug 5010).
Packit Service 82fcde
   Copyright (C) 2017-2018 Free Software Foundation, Inc.
Packit Service 82fcde
   This file is part of the GNU C Library.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is free software; you can redistribute it and/or
Packit Service 82fcde
   modify it under the terms of the GNU Lesser General Public
Packit Service 82fcde
   License as published by the Free Software Foundation; either
Packit Service 82fcde
   version 2.1 of the License, or (at your option) any later version.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is distributed in the hope that it will be useful,
Packit Service 82fcde
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 82fcde
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 82fcde
   Lesser General Public License for more details.
Packit Service 82fcde
Packit Service 82fcde
   You should have received a copy of the GNU Lesser General Public
Packit Service 82fcde
   License along with the GNU C Library; if not, see
Packit Service 82fcde
   <http://www.gnu.org/licenses/>.  */
Packit Service 82fcde
Packit Service 82fcde
/* This test uses a stub rpcbind server (implemented in a child
Packit Service 82fcde
   process using rpcbind_dispatch/run_rpcbind) to check how RPC
Packit Service 82fcde
   services are registered and unregistered using the rpcbind
Packit Service 82fcde
   protocol.  For each subtest, a separate rpcbind test server is
Packit Service 82fcde
   spawned and terminated.  */
Packit Service 82fcde
Packit Service 82fcde
#include <errno.h>
Packit Service 82fcde
#include <netinet/in.h>
Packit Service 82fcde
#include <rpc/clnt.h>
Packit Service 82fcde
#include <rpc/pmap_prot.h>
Packit Service 82fcde
#include <rpc/svc.h>
Packit Service 82fcde
#include <signal.h>
Packit Service 82fcde
#include <support/check.h>
Packit Service 82fcde
#include <support/namespace.h>
Packit Service 82fcde
#include <support/test-driver.h>
Packit Service 82fcde
#include <support/xsocket.h>
Packit Service 82fcde
#include <support/xthread.h>
Packit Service 82fcde
#include <support/xunistd.h>
Packit Service 82fcde
#include <sys/socket.h>
Packit Service 82fcde
#include <sys/wait.h>
Packit Service 82fcde
#include <unistd.h>
Packit Service 82fcde
Packit Service 82fcde
#include <libc-symbols.h>
Packit Service 82fcde
#include <shlib-compat.h>
Packit Service 82fcde
Packit Service 82fcde
/* These functions are only available as compat symbols.  */
Packit Service 82fcde
compat_symbol_reference (libc, xdr_pmap, xdr_pmap, GLIBC_2_0);
Packit Service 82fcde
compat_symbol_reference (libc, svc_unregister, svc_unregister, GLIBC_2_0);
Packit Service 82fcde
Packit Service 82fcde
/* Server callback for the unused RPC service which is registered and
Packit Service 82fcde
   unregistered.  */
Packit Service 82fcde
static void
Packit Service 82fcde
server_dispatch (struct svc_req *request, SVCXPRT *transport)
Packit Service 82fcde
{
Packit Service 82fcde
  FAIL_EXIT1 ("server_dispatch called");
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* The port on which rpcbind listens for incoming requests.  */
Packit Service 82fcde
static inline const struct sockaddr_in
Packit Service 82fcde
rpcbind_address (void)
Packit Service 82fcde
{
Packit Service 82fcde
  return (struct sockaddr_in)
Packit Service 82fcde
    {
Packit Service 82fcde
      .sin_family = AF_INET,
Packit Service 82fcde
      .sin_addr.s_addr = htonl (INADDR_LOOPBACK),
Packit Service 82fcde
      .sin_port = htons (PMAPPORT)
Packit Service 82fcde
    };
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* Data provided by the test server after running the test, to see
Packit Service 82fcde
   that the expected calls (and only those) happened.  */
Packit Service 82fcde
struct test_state
Packit Service 82fcde
{
Packit Service 82fcde
  bool_t set_called;
Packit Service 82fcde
  bool_t unset_called;
Packit Service 82fcde
};
Packit Service 82fcde
Packit Service 82fcde
static bool_t
Packit Service 82fcde
xdr_test_state (XDR *xdrs, void *data, ...)
Packit Service 82fcde
{
Packit Service 82fcde
  struct test_state *p = data;
Packit Service 82fcde
  return xdr_bool (xdrs, &p->set_called)
Packit Service 82fcde
    && xdr_bool (xdrs, &p->unset_called);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
enum
Packit Service 82fcde
{
Packit Service 82fcde
  /* Coordinates of our test service.  These numbers are
Packit Service 82fcde
     arbitrary.  */
Packit Service 82fcde
  PROGNUM = 123,
Packit Service 82fcde
  VERSNUM = 456,
Packit Service 82fcde
Packit Service 82fcde
  /* Extension for this test.  */
Packit Service 82fcde
  PROC_GET_STATE_AND_EXIT = 10760
Packit Service 82fcde
};
Packit Service 82fcde
Packit Service 82fcde
/* Dummy implementation of the rpcbind service, with the
Packit Service 82fcde
   PROC_GET_STATE_AND_EXIT extension.  */
Packit Service 82fcde
static void
Packit Service 82fcde
rpcbind_dispatch (struct svc_req *request, SVCXPRT *transport)
Packit Service 82fcde
{
Packit Service 82fcde
  static struct test_state state = { 0, };
Packit Service 82fcde
Packit Service 82fcde
  if (test_verbose)
Packit Service 82fcde
    printf ("info: rpcbind request %lu\n", request->rq_proc);
Packit Service 82fcde
Packit Service 82fcde
  switch (request->rq_proc)
Packit Service 82fcde
    {
Packit Service 82fcde
    case PMAPPROC_SET:
Packit Service 82fcde
    case PMAPPROC_UNSET:
Packit Service 82fcde
      TEST_VERIFY (state.set_called == (request->rq_proc == PMAPPROC_UNSET));
Packit Service 82fcde
      TEST_VERIFY (!state.unset_called);
Packit Service 82fcde
Packit Service 82fcde
      struct pmap query = { 0, };
Packit Service 82fcde
      TEST_VERIFY
Packit Service 82fcde
        (svc_getargs (transport, (xdrproc_t) xdr_pmap, (void *) &query));
Packit Service 82fcde
      if (test_verbose)
Packit Service 82fcde
        printf ("  pm_prog=%lu pm_vers=%lu pm_prot=%lu pm_port=%lu\n",
Packit Service 82fcde
                query.pm_prog, query.pm_vers, query.pm_prot, query.pm_port);
Packit Service 82fcde
      TEST_VERIFY (query.pm_prog == PROGNUM);
Packit Service 82fcde
      TEST_VERIFY (query.pm_vers == VERSNUM);
Packit Service 82fcde
Packit Service 82fcde
      if (request->rq_proc == PMAPPROC_SET)
Packit Service 82fcde
        state.set_called = TRUE;
Packit Service 82fcde
      else
Packit Service 82fcde
        state.unset_called = TRUE;
Packit Service 82fcde
Packit Service 82fcde
      bool_t result = TRUE;
Packit Service 82fcde
      TEST_VERIFY (svc_sendreply (transport,
Packit Service 82fcde
                                  (xdrproc_t) xdr_bool, (void *) &result));
Packit Service 82fcde
      break;
Packit Service 82fcde
Packit Service 82fcde
    case PROC_GET_STATE_AND_EXIT:
Packit Service 82fcde
      TEST_VERIFY (svc_sendreply (transport,
Packit Service 82fcde
                                  xdr_test_state, (void *) &state));
Packit Service 82fcde
      _exit (0);
Packit Service 82fcde
      break;
Packit Service 82fcde
Packit Service 82fcde
    default:
Packit Service 82fcde
      FAIL_EXIT1 ("invalid rq_proc value: %lu", request->rq_proc);
Packit Service 82fcde
    }
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* Run the rpcbind test server.  */
Packit Service 82fcde
static void
Packit Service 82fcde
run_rpcbind (int rpcbind_sock)
Packit Service 82fcde
{
Packit Service 82fcde
  SVCXPRT *rpcbind_transport = svcudp_create (rpcbind_sock);
Packit Service 82fcde
  TEST_VERIFY (svc_register (rpcbind_transport, PMAPPROG, PMAPVERS,
Packit Service 82fcde
                             rpcbind_dispatch,
Packit Service 82fcde
                             /* Do not register with rpcbind.  */
Packit Service 82fcde
                             0));
Packit Service 82fcde
  svc_run ();
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* Call out to the rpcbind test server to retrieve the test status
Packit Service 82fcde
   information.  */
Packit Service 82fcde
static struct test_state
Packit Service 82fcde
get_test_state (void)
Packit Service 82fcde
{
Packit Service 82fcde
  int socket = RPC_ANYSOCK;
Packit Service 82fcde
  struct sockaddr_in address = rpcbind_address ();
Packit Service 82fcde
  CLIENT *client = clntudp_create
Packit Service 82fcde
    (&address, PMAPPROG, PMAPVERS, (struct timeval) { 1, 0}, &socket);
Packit Service 82fcde
  struct test_state result = { 0 };
Packit Service 82fcde
  TEST_VERIFY (clnt_call (client, PROC_GET_STATE_AND_EXIT,
Packit Service 82fcde
                          (xdrproc_t) xdr_void, NULL,
Packit Service 82fcde
                          xdr_test_state, (void *) &result,
Packit Service 82fcde
                          ((struct timeval) { 3, 0}))
Packit Service 82fcde
               == RPC_SUCCESS);
Packit Service 82fcde
  clnt_destroy (client);
Packit Service 82fcde
  return result;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* Used by test_server_thread to receive test parameters.  */
Packit Service 82fcde
struct test_server_args
Packit Service 82fcde
{
Packit Service 82fcde
  bool use_rpcbind;
Packit Service 82fcde
  bool use_unregister;
Packit Service 82fcde
};
Packit Service 82fcde
Packit Service 82fcde
/* RPC test server.  Used to verify the svc_unregister behavior during
Packit Service 82fcde
   thread cleanup.  */
Packit Service 82fcde
static void *
Packit Service 82fcde
test_server_thread (void *closure)
Packit Service 82fcde
{
Packit Service 82fcde
  struct test_server_args *args = closure;
Packit Service 82fcde
  SVCXPRT *transport = svcudp_create (RPC_ANYSOCK);
Packit Service 82fcde
  int protocol;
Packit Service 82fcde
  if (args->use_rpcbind)
Packit Service 82fcde
    protocol = IPPROTO_UDP;
Packit Service 82fcde
  else
Packit Service 82fcde
    /* Do not register with rpcbind.  */
Packit Service 82fcde
    protocol = 0;
Packit Service 82fcde
  TEST_VERIFY (svc_register (transport, PROGNUM, VERSNUM,
Packit Service 82fcde
                             server_dispatch, protocol));
Packit Service 82fcde
  if (args->use_unregister)
Packit Service 82fcde
    svc_unregister (PROGNUM, VERSNUM);
Packit Service 82fcde
  SVC_DESTROY (transport);
Packit Service 82fcde
  return NULL;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
static int
Packit Service 82fcde
do_test (void)
Packit Service 82fcde
{
Packit Service 82fcde
  support_become_root ();
Packit Service 82fcde
  support_enter_network_namespace ();
Packit Service 82fcde
Packit Service 82fcde
  /* Try to bind to the rpcbind port.  */
Packit Service 82fcde
  int rpcbind_sock = xsocket (AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
Packit Service 82fcde
  {
Packit Service 82fcde
    struct sockaddr_in sin = rpcbind_address ();
Packit Service 82fcde
    if (bind (rpcbind_sock, (struct sockaddr *) &sin, sizeof (sin)) != 0)
Packit Service 82fcde
      {
Packit Service 82fcde
        /* If the port is not available, we cannot run this test.  */
Packit Service 82fcde
        printf ("warning: could not bind to rpcbind port %d: %m\n",
Packit Service 82fcde
                (int) PMAPPORT);
Packit Service 82fcde
        return EXIT_UNSUPPORTED;
Packit Service 82fcde
      }
Packit Service 82fcde
  }
Packit Service 82fcde
Packit Service 82fcde
  for (int use_thread = 0; use_thread < 2; ++use_thread)
Packit Service 82fcde
    for (int use_rpcbind = 0; use_rpcbind < 2; ++use_rpcbind)
Packit Service 82fcde
      for (int use_unregister = 0; use_unregister < 2; ++use_unregister)
Packit Service 82fcde
        {
Packit Service 82fcde
          if (test_verbose)
Packit Service 82fcde
            printf ("info: * use_thread=%d use_rpcbind=%d use_unregister=%d\n",
Packit Service 82fcde
                    use_thread, use_rpcbind, use_unregister);
Packit Service 82fcde
Packit Service 82fcde
          /* Create the subprocess which runs the actual test.  The
Packit Service 82fcde
             kernel will queue the UDP packets to the rpcbind
Packit Service 82fcde
             process.  */
Packit Service 82fcde
          pid_t svc_pid = xfork ();
Packit Service 82fcde
          if (svc_pid == 0)
Packit Service 82fcde
            {
Packit Service 82fcde
              struct test_server_args args =
Packit Service 82fcde
                {
Packit Service 82fcde
                  .use_rpcbind = use_rpcbind,
Packit Service 82fcde
                  .use_unregister = use_unregister,
Packit Service 82fcde
                };
Packit Service 82fcde
              if (use_thread)
Packit Service 82fcde
                xpthread_join (xpthread_create
Packit Service 82fcde
                               (NULL, test_server_thread, &args));
Packit Service 82fcde
              else
Packit Service 82fcde
                test_server_thread (&args);
Packit Service 82fcde
              /* We cannnot use _exit here because we want to test the
Packit Service 82fcde
                 process cleanup.  */
Packit Service 82fcde
              exit (0);
Packit Service 82fcde
            }
Packit Service 82fcde
Packit Service 82fcde
          /* Create the subprocess for the rpcbind test server.  */
Packit Service 82fcde
          pid_t rpcbind_pid = xfork ();
Packit Service 82fcde
          if (rpcbind_pid == 0)
Packit Service 82fcde
            run_rpcbind (rpcbind_sock);
Packit Service 82fcde
Packit Service 82fcde
          int status;
Packit Service 82fcde
          xwaitpid (svc_pid, &status, 0);
Packit Service 82fcde
          TEST_VERIFY (WIFEXITED (status) && WEXITSTATUS (status) == 0);
Packit Service 82fcde
Packit Service 82fcde
          if (!use_rpcbind)
Packit Service 82fcde
            /* Wait a bit, to see if the packet arrives on the rpcbind
Packit Service 82fcde
               port.  The choice is of the timeout is arbitrary, but
Packit Service 82fcde
               should be long enough even for slow/busy systems.  For
Packit Service 82fcde
               the use_rpcbind case, waiting on svc_pid above makes
Packit Service 82fcde
               sure that the test server has responded because
Packit Service 82fcde
               svc_register/svc_unregister are supposed to wait for a
Packit Service 82fcde
               reply.  */
Packit Service 82fcde
            usleep (300 * 1000);
Packit Service 82fcde
Packit Service 82fcde
          struct test_state state = get_test_state ();
Packit Service 82fcde
          if (use_rpcbind)
Packit Service 82fcde
            {
Packit Service 82fcde
              TEST_VERIFY (state.set_called);
Packit Service 82fcde
              if (use_thread || use_unregister)
Packit Service 82fcde
                /* Thread cleanup or explicit svc_unregister will
Packit Service 82fcde
                   result in a rpcbind unset RPC call.  */
Packit Service 82fcde
                TEST_VERIFY (state.unset_called);
Packit Service 82fcde
              else
Packit Service 82fcde
                /* This is arguably a bug: Regular process termination
Packit Service 82fcde
                   does not unregister the service with rpcbind.  The
Packit Service 82fcde
                   unset rpcbind call happens from a __libc_subfreeres
Packit Service 82fcde
                   callback, and this only happens when running under
Packit Service 82fcde
                   memory debuggers such as valgrind.  */
Packit Service 82fcde
                TEST_VERIFY (!state.unset_called);
Packit Service 82fcde
            }
Packit Service 82fcde
          else
Packit Service 82fcde
            {
Packit Service 82fcde
              /* If rpcbind registration is not requested, we do not
Packit Service 82fcde
                 expect any rpcbind calls.  */
Packit Service 82fcde
              TEST_VERIFY (!state.set_called);
Packit Service 82fcde
              TEST_VERIFY (!state.unset_called);
Packit Service 82fcde
            }
Packit Service 82fcde
Packit Service 82fcde
          xwaitpid (rpcbind_pid, &status, 0);
Packit Service 82fcde
          TEST_VERIFY (WIFEXITED (status) && WEXITSTATUS (status) == 0);
Packit Service 82fcde
        }
Packit Service 82fcde
Packit Service 82fcde
  return 0;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
#include <support/test-driver.c>