Blame sysdeps/unix/sysv/linux/mq_notify.c

Packit Service 82fcde
/* Copyright (C) 2004-2018 Free Software Foundation, Inc.
Packit Service 82fcde
   This file is part of the GNU C Library.
Packit Service 82fcde
   Contribute by Ulrich Drepper <drepper@redhat.com>, 2004.
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
#include <assert.h>
Packit Service 82fcde
#include <errno.h>
Packit Service 82fcde
#include <fcntl.h>
Packit Service 82fcde
#include <mqueue.h>
Packit Service 82fcde
#include <pthread.h>
Packit Service 82fcde
#include <signal.h>
Packit Service 82fcde
#include <stdlib.h>
Packit Service 82fcde
#include <string.h>
Packit Service 82fcde
#include <sysdep.h>
Packit Service 82fcde
#include <unistd.h>
Packit Service 82fcde
#include <sys/socket.h>
Packit Service 82fcde
#include <not-cancel.h>
Packit Service 82fcde
#include <nptl/pthreadP.h>
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
#ifdef __NR_mq_notify
Packit Service 82fcde
Packit Service 82fcde
/* Defined in the kernel headers: */
Packit Service 82fcde
#define NOTIFY_COOKIE_LEN	32	/* Length of the cookie used.  */
Packit Service 82fcde
#define NOTIFY_WOKENUP		1	/* Code for notifcation.  */
Packit Service 82fcde
#define NOTIFY_REMOVED		2	/* Code for closed message queue
Packit Service 82fcde
					   of de-notifcation.  */
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* Data structure for the queued notification requests.  */
Packit Service 82fcde
union notify_data
Packit Service 82fcde
{
Packit Service 82fcde
  struct
Packit Service 82fcde
  {
Packit Service 82fcde
    void (*fct) (union sigval);	/* The function to run.  */
Packit Service 82fcde
    union sigval param;		/* The parameter to pass.  */
Packit Service 82fcde
    pthread_attr_t *attr;	/* Attributes to create the thread with.  */
Packit Service 82fcde
    /* NB: on 64-bit machines the struct as a size of 24 bytes.  Which means
Packit Service 82fcde
       byte 31 can still be used for returning the status.  */
Packit Service 82fcde
  };
Packit Service 82fcde
  char raw[NOTIFY_COOKIE_LEN];
Packit Service 82fcde
};
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* Keep track of the initialization.  */
Packit Service 82fcde
static pthread_once_t once = PTHREAD_ONCE_INIT;
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* The netlink socket.  */
Packit Service 82fcde
static int netlink_socket = -1;
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* Barrier used to make sure data passed to the new thread is not
Packit Service 82fcde
   resused by the parent.  */
Packit Service 82fcde
static pthread_barrier_t notify_barrier;
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* Modify the signal mask.  We move this into a separate function so
Packit Service 82fcde
   that the stack space needed for sigset_t is not deducted from what
Packit Service 82fcde
   the thread can use.  */
Packit Service 82fcde
static int
Packit Service 82fcde
__attribute__ ((noinline))
Packit Service 82fcde
change_sigmask (int how, sigset_t *oss)
Packit Service 82fcde
{
Packit Service 82fcde
  sigset_t ss;
Packit Service 82fcde
  sigfillset (&ss);
Packit Service 82fcde
  return pthread_sigmask (how, &ss, oss);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* The function used for the notification.  */
Packit Service 82fcde
static void *
Packit Service 82fcde
notification_function (void *arg)
Packit Service 82fcde
{
Packit Service 82fcde
  /* Copy the function and parameter so that the parent thread can go
Packit Service 82fcde
     on with its life.  */
Packit Service 82fcde
  volatile union notify_data *data = (volatile union notify_data *) arg;
Packit Service 82fcde
  void (*fct) (union sigval) = data->fct;
Packit Service 82fcde
  union sigval param = data->param;
Packit Service 82fcde
Packit Service 82fcde
  /* Let the parent go.  */
Packit Service 82fcde
  (void) __pthread_barrier_wait (&notify_barrier);
Packit Service 82fcde
Packit Service 82fcde
  /* Make the thread detached.  */
Packit Service 82fcde
  (void) pthread_detach (pthread_self ());
Packit Service 82fcde
Packit Service 82fcde
  /* The parent thread has all signals blocked.  This is probably a
Packit Service 82fcde
     bit surprising for this thread.  So we unblock all of them.  */
Packit Service 82fcde
  (void) change_sigmask (SIG_UNBLOCK, NULL);
Packit Service 82fcde
Packit Service 82fcde
  /* Now run the user code.  */
Packit Service 82fcde
  fct (param);
Packit Service 82fcde
Packit Service 82fcde
  /* And we are done.  */
Packit Service 82fcde
  return NULL;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* Helper thread.  */
Packit Service 82fcde
static void *
Packit Service 82fcde
helper_thread (void *arg)
Packit Service 82fcde
{
Packit Service 82fcde
  while (1)
Packit Service 82fcde
    {
Packit Service 82fcde
      union notify_data data;
Packit Service 82fcde
Packit Service 82fcde
      ssize_t n = __recv (netlink_socket, &data, sizeof (data),
Packit Service 82fcde
			  MSG_NOSIGNAL | MSG_WAITALL);
Packit Service 82fcde
      if (n < NOTIFY_COOKIE_LEN)
Packit Service 82fcde
	continue;
Packit Service 82fcde
Packit Service 82fcde
      if (data.raw[NOTIFY_COOKIE_LEN - 1] == NOTIFY_WOKENUP)
Packit Service 82fcde
	{
Packit Service 82fcde
	  /* Just create the thread as instructed.  There is no way to
Packit Service 82fcde
	     report a problem with creating a thread.  */
Packit Service 82fcde
	  pthread_t th;
Packit Service 82fcde
	  if (__builtin_expect (pthread_create (&th, data.attr,
Packit Service 82fcde
						notification_function, &data)
Packit Service 82fcde
				== 0, 0))
Packit Service 82fcde
	    /* Since we passed a pointer to DATA to the new thread we have
Packit Service 82fcde
	       to wait until it is done with it.  */
Packit Service 82fcde
	    (void) __pthread_barrier_wait (&notify_barrier);
Packit Service 82fcde
	}
Packit Service 82fcde
      else if (data.raw[NOTIFY_COOKIE_LEN - 1] == NOTIFY_REMOVED)
Packit Service 82fcde
	/* The only state we keep is the copy of the thread attributes.  */
Packit Service 82fcde
	free (data.attr);
Packit Service 82fcde
    }
Packit Service 82fcde
  return NULL;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
static void
Packit Service 82fcde
reset_once (void)
Packit Service 82fcde
{
Packit Service 82fcde
  once = PTHREAD_ONCE_INIT;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
static void
Packit Service 82fcde
init_mq_netlink (void)
Packit Service 82fcde
{
Packit Service 82fcde
  /* This code might be called a second time after fork().  The file
Packit Service 82fcde
     descriptor is inherited from the parent.  */
Packit Service 82fcde
  if (netlink_socket == -1)
Packit Service 82fcde
    {
Packit Service 82fcde
      /* Just a normal netlink socket, not bound.  */
Packit Service 82fcde
      netlink_socket = __socket (AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, 0);
Packit Service 82fcde
      /* No need to do more if we have no socket.  */
Packit Service 82fcde
      if (netlink_socket == -1)
Packit Service 82fcde
	return;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  int err = 1;
Packit Service 82fcde
Packit Service 82fcde
  /* Initialize the barrier.  */
Packit Service 82fcde
  if (__builtin_expect (__pthread_barrier_init (&notify_barrier, NULL, 2) == 0,
Packit Service 82fcde
			0))
Packit Service 82fcde
    {
Packit Service 82fcde
      /* Create the helper thread.  */
Packit Service 82fcde
      pthread_attr_t attr;
Packit Service 82fcde
      (void) pthread_attr_init (&attr);
Packit Service 82fcde
      (void) pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
Packit Service 82fcde
      /* We do not need much stack space, the bare minimum will be enough.  */
Packit Service 82fcde
      (void) pthread_attr_setstacksize (&attr, __pthread_get_minstack (&attr));
Packit Service 82fcde
Packit Service 82fcde
      /* Temporarily block all signals so that the newly created
Packit Service 82fcde
	 thread inherits the mask.  */
Packit Service 82fcde
      sigset_t oss;
Packit Service 82fcde
      int have_no_oss = change_sigmask (SIG_BLOCK, &oss;;
Packit Service 82fcde
Packit Service 82fcde
      pthread_t th;
Packit Service 82fcde
      err = pthread_create (&th, &attr, helper_thread, NULL);
Packit Service 82fcde
Packit Service 82fcde
      /* Reset the signal mask.  */
Packit Service 82fcde
      if (!have_no_oss)
Packit Service 82fcde
	pthread_sigmask (SIG_SETMASK, &oss, NULL);
Packit Service 82fcde
Packit Service 82fcde
      (void) pthread_attr_destroy (&attr);
Packit Service 82fcde
Packit Service 82fcde
      if (err == 0)
Packit Service 82fcde
	{
Packit Service 82fcde
	  static int added_atfork;
Packit Service 82fcde
Packit Service 82fcde
	  if (added_atfork == 0
Packit Service 82fcde
	      && pthread_atfork (NULL, NULL, reset_once) != 0)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      /* The child thread will call recv() which is a
Packit Service 82fcde
		 cancellation point.  */
Packit Service 82fcde
	      (void) pthread_cancel (th);
Packit Service 82fcde
	      err = 1;
Packit Service 82fcde
	    }
Packit Service 82fcde
	  else
Packit Service 82fcde
	    added_atfork = 1;
Packit Service 82fcde
	}
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  if (err != 0)
Packit Service 82fcde
    {
Packit Service 82fcde
      __close_nocancel_nostatus (netlink_socket);
Packit Service 82fcde
      netlink_socket = -1;
Packit Service 82fcde
    }
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* Register notification upon message arrival to an empty message queue
Packit Service 82fcde
   MQDES.  */
Packit Service 82fcde
int
Packit Service 82fcde
mq_notify (mqd_t mqdes, const struct sigevent *notification)
Packit Service 82fcde
{
Packit Service 82fcde
  /* Make sure the type is correctly defined.  */
Packit Service 82fcde
  assert (sizeof (union notify_data) == NOTIFY_COOKIE_LEN);
Packit Service 82fcde
Packit Service 82fcde
  /* Special treatment needed for SIGEV_THREAD.  */
Packit Service 82fcde
  if (notification == NULL || notification->sigev_notify != SIGEV_THREAD)
Packit Service 82fcde
    return INLINE_SYSCALL (mq_notify, 2, mqdes, notification);
Packit Service 82fcde
Packit Service 82fcde
  /* The kernel cannot directly start threads.  This will have to be
Packit Service 82fcde
     done at userlevel.  Since we cannot start threads from signal
Packit Service 82fcde
     handlers we have to create a dedicated thread which waits for
Packit Service 82fcde
     notifications for arriving messages and creates threads in
Packit Service 82fcde
     response.  */
Packit Service 82fcde
Packit Service 82fcde
  /* Initialize only once.  */
Packit Service 82fcde
  pthread_once (&once, init_mq_netlink);
Packit Service 82fcde
Packit Service 82fcde
  /* If we cannot create the netlink socket we cannot provide
Packit Service 82fcde
     SIGEV_THREAD support.  */
Packit Service 82fcde
  if (__glibc_unlikely (netlink_socket == -1))
Packit Service 82fcde
    {
Packit Service 82fcde
      __set_errno (ENOSYS);
Packit Service 82fcde
      return -1;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  /* Create the cookie.  It will hold almost all the state.  */
Packit Service 82fcde
  union notify_data data;
Packit Service 82fcde
  memset (&data, '\0', sizeof (data));
Packit Service 82fcde
  data.fct = notification->sigev_notify_function;
Packit Service 82fcde
  data.param = notification->sigev_value;
Packit Service 82fcde
Packit Service 82fcde
  if (notification->sigev_notify_attributes != NULL)
Packit Service 82fcde
    {
Packit Service 82fcde
      /* The thread attribute has to be allocated separately.  */
Packit Service 82fcde
      data.attr = (pthread_attr_t *) malloc (sizeof (pthread_attr_t));
Packit Service 82fcde
      if (data.attr == NULL)
Packit Service 82fcde
	return -1;
Packit Service 82fcde
Packit Service 82fcde
      memcpy (data.attr, notification->sigev_notify_attributes,
Packit Service 82fcde
	      sizeof (pthread_attr_t));
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  /* Construct the new request.  */
Packit Service 82fcde
  struct sigevent se;
Packit Service 82fcde
  se.sigev_notify = SIGEV_THREAD;
Packit Service 82fcde
  se.sigev_signo = netlink_socket;
Packit Service 82fcde
  se.sigev_value.sival_ptr = &dat;;
Packit Service 82fcde
Packit Service 82fcde
  /* Tell the kernel.  */
Packit Service 82fcde
  int retval = INLINE_SYSCALL (mq_notify, 2, mqdes, &se);
Packit Service 82fcde
Packit Service 82fcde
  /* If it failed, free the allocated memory.  */
Packit Service 82fcde
  if (__glibc_unlikely (retval != 0))
Packit Service 82fcde
    free (data.attr);
Packit Service 82fcde
Packit Service 82fcde
  return retval;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
#else
Packit Service 82fcde
# include <rt/mq_notify.c>
Packit Service 82fcde
#endif