mirror of
				https://sourceware.org/git/glibc.git
				synced 2025-10-28 23:34:53 +03:00 
			
		
		
		
	Due to the built-in tables, __NR_mq_getsetattr, __NR_mq_notify, __NR_mq_open, __NR_mq_timedreceive, __NR_mq_timedsend, __NR_mq_unlink are always defined. Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
		
			
				
	
	
		
			277 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			277 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* Copyright (C) 2004-2020 Free Software Foundation, Inc.
 | |
|    This file is part of the GNU C Library.
 | |
|    Contribute by Ulrich Drepper <drepper@redhat.com>, 2004.
 | |
| 
 | |
|    The GNU C Library is free software; you can redistribute it and/or
 | |
|    modify it under the terms of the GNU Lesser General Public
 | |
|    License as published by the Free Software Foundation; either
 | |
|    version 2.1 of the License, or (at your option) any later version.
 | |
| 
 | |
|    The GNU C Library is distributed in the hope that it will be useful,
 | |
|    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|    Lesser General Public License for more details.
 | |
| 
 | |
|    You should have received a copy of the GNU Lesser General Public
 | |
|    License along with the GNU C Library; if not, see
 | |
|    <https://www.gnu.org/licenses/>.  */
 | |
| 
 | |
| #include <assert.h>
 | |
| #include <errno.h>
 | |
| #include <fcntl.h>
 | |
| #include <mqueue.h>
 | |
| #include <pthread.h>
 | |
| #include <signal.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <sysdep.h>
 | |
| #include <unistd.h>
 | |
| #include <sys/socket.h>
 | |
| #include <not-cancel.h>
 | |
| #include <nptl/pthreadP.h>
 | |
| 
 | |
| 
 | |
| /* Defined in the kernel headers: */
 | |
| #define NOTIFY_COOKIE_LEN	32	/* Length of the cookie used.  */
 | |
| #define NOTIFY_WOKENUP		1	/* Code for notifcation.  */
 | |
| #define NOTIFY_REMOVED		2	/* Code for closed message queue
 | |
| 					   of de-notifcation.  */
 | |
| 
 | |
| 
 | |
| /* Data structure for the queued notification requests.  */
 | |
| union notify_data
 | |
| {
 | |
|   struct
 | |
|   {
 | |
|     void (*fct) (union sigval);	/* The function to run.  */
 | |
|     union sigval param;		/* The parameter to pass.  */
 | |
|     pthread_attr_t *attr;	/* Attributes to create the thread with.  */
 | |
|     /* NB: on 64-bit machines the struct as a size of 24 bytes.  Which means
 | |
|        byte 31 can still be used for returning the status.  */
 | |
|   };
 | |
|   char raw[NOTIFY_COOKIE_LEN];
 | |
| };
 | |
| 
 | |
| 
 | |
| /* Keep track of the initialization.  */
 | |
| static pthread_once_t once = PTHREAD_ONCE_INIT;
 | |
| 
 | |
| 
 | |
| /* The netlink socket.  */
 | |
| static int netlink_socket = -1;
 | |
| 
 | |
| 
 | |
| /* Barrier used to make sure data passed to the new thread is not
 | |
|    resused by the parent.  */
 | |
| static pthread_barrier_t notify_barrier;
 | |
| 
 | |
| 
 | |
| /* Modify the signal mask.  We move this into a separate function so
 | |
|    that the stack space needed for sigset_t is not deducted from what
 | |
|    the thread can use.  */
 | |
| static int
 | |
| __attribute__ ((noinline))
 | |
| change_sigmask (int how, sigset_t *oss)
 | |
| {
 | |
|   sigset_t ss;
 | |
|   sigfillset (&ss);
 | |
|   return pthread_sigmask (how, &ss, oss);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* The function used for the notification.  */
 | |
| static void *
 | |
| notification_function (void *arg)
 | |
| {
 | |
|   /* Copy the function and parameter so that the parent thread can go
 | |
|      on with its life.  */
 | |
|   volatile union notify_data *data = (volatile union notify_data *) arg;
 | |
|   void (*fct) (union sigval) = data->fct;
 | |
|   union sigval param = data->param;
 | |
| 
 | |
|   /* Let the parent go.  */
 | |
|   (void) __pthread_barrier_wait (¬ify_barrier);
 | |
| 
 | |
|   /* Make the thread detached.  */
 | |
|   (void) pthread_detach (pthread_self ());
 | |
| 
 | |
|   /* The parent thread has all signals blocked.  This is probably a
 | |
|      bit surprising for this thread.  So we unblock all of them.  */
 | |
|   (void) change_sigmask (SIG_UNBLOCK, NULL);
 | |
| 
 | |
|   /* Now run the user code.  */
 | |
|   fct (param);
 | |
| 
 | |
|   /* And we are done.  */
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Helper thread.  */
 | |
| static void *
 | |
| helper_thread (void *arg)
 | |
| {
 | |
|   while (1)
 | |
|     {
 | |
|       union notify_data data;
 | |
| 
 | |
|       ssize_t n = __recv (netlink_socket, &data, sizeof (data),
 | |
| 			  MSG_NOSIGNAL | MSG_WAITALL);
 | |
|       if (n < NOTIFY_COOKIE_LEN)
 | |
| 	continue;
 | |
| 
 | |
|       if (data.raw[NOTIFY_COOKIE_LEN - 1] == NOTIFY_WOKENUP)
 | |
| 	{
 | |
| 	  /* Just create the thread as instructed.  There is no way to
 | |
| 	     report a problem with creating a thread.  */
 | |
| 	  pthread_t th;
 | |
| 	  if (__builtin_expect (pthread_create (&th, data.attr,
 | |
| 						notification_function, &data)
 | |
| 				== 0, 0))
 | |
| 	    /* Since we passed a pointer to DATA to the new thread we have
 | |
| 	       to wait until it is done with it.  */
 | |
| 	    (void) __pthread_barrier_wait (¬ify_barrier);
 | |
| 	}
 | |
|       else if (data.raw[NOTIFY_COOKIE_LEN - 1] == NOTIFY_REMOVED)
 | |
| 	/* The only state we keep is the copy of the thread attributes.  */
 | |
| 	free (data.attr);
 | |
|     }
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| reset_once (void)
 | |
| {
 | |
|   once = PTHREAD_ONCE_INIT;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| init_mq_netlink (void)
 | |
| {
 | |
|   /* This code might be called a second time after fork().  The file
 | |
|      descriptor is inherited from the parent.  */
 | |
|   if (netlink_socket == -1)
 | |
|     {
 | |
|       /* Just a normal netlink socket, not bound.  */
 | |
|       netlink_socket = __socket (AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, 0);
 | |
|       /* No need to do more if we have no socket.  */
 | |
|       if (netlink_socket == -1)
 | |
| 	return;
 | |
|     }
 | |
| 
 | |
|   int err = 1;
 | |
| 
 | |
|   /* Initialize the barrier.  */
 | |
|   if (__builtin_expect (__pthread_barrier_init (¬ify_barrier, NULL, 2) == 0,
 | |
| 			0))
 | |
|     {
 | |
|       /* Create the helper thread.  */
 | |
|       pthread_attr_t attr;
 | |
|       (void) pthread_attr_init (&attr);
 | |
|       (void) pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
 | |
|       /* We do not need much stack space, the bare minimum will be enough.  */
 | |
|       (void) pthread_attr_setstacksize (&attr, __pthread_get_minstack (&attr));
 | |
| 
 | |
|       /* Temporarily block all signals so that the newly created
 | |
| 	 thread inherits the mask.  */
 | |
|       sigset_t oss;
 | |
|       int have_no_oss = change_sigmask (SIG_BLOCK, &oss);
 | |
| 
 | |
|       pthread_t th;
 | |
|       err = pthread_create (&th, &attr, helper_thread, NULL);
 | |
| 
 | |
|       /* Reset the signal mask.  */
 | |
|       if (!have_no_oss)
 | |
| 	pthread_sigmask (SIG_SETMASK, &oss, NULL);
 | |
| 
 | |
|       (void) pthread_attr_destroy (&attr);
 | |
| 
 | |
|       if (err == 0)
 | |
| 	{
 | |
| 	  static int added_atfork;
 | |
| 
 | |
| 	  if (added_atfork == 0
 | |
| 	      && pthread_atfork (NULL, NULL, reset_once) != 0)
 | |
| 	    {
 | |
| 	      /* The child thread will call recv() which is a
 | |
| 		 cancellation point.  */
 | |
| 	      (void) pthread_cancel (th);
 | |
| 	      err = 1;
 | |
| 	    }
 | |
| 	  else
 | |
| 	    added_atfork = 1;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   if (err != 0)
 | |
|     {
 | |
|       __close_nocancel_nostatus (netlink_socket);
 | |
|       netlink_socket = -1;
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Register notification upon message arrival to an empty message queue
 | |
|    MQDES.  */
 | |
| int
 | |
| mq_notify (mqd_t mqdes, const struct sigevent *notification)
 | |
| {
 | |
|   /* Make sure the type is correctly defined.  */
 | |
|   assert (sizeof (union notify_data) == NOTIFY_COOKIE_LEN);
 | |
| 
 | |
|   /* Special treatment needed for SIGEV_THREAD.  */
 | |
|   if (notification == NULL || notification->sigev_notify != SIGEV_THREAD)
 | |
|     return INLINE_SYSCALL (mq_notify, 2, mqdes, notification);
 | |
| 
 | |
|   /* The kernel cannot directly start threads.  This will have to be
 | |
|      done at userlevel.  Since we cannot start threads from signal
 | |
|      handlers we have to create a dedicated thread which waits for
 | |
|      notifications for arriving messages and creates threads in
 | |
|      response.  */
 | |
| 
 | |
|   /* Initialize only once.  */
 | |
|   pthread_once (&once, init_mq_netlink);
 | |
| 
 | |
|   /* If we cannot create the netlink socket we cannot provide
 | |
|      SIGEV_THREAD support.  */
 | |
|   if (__glibc_unlikely (netlink_socket == -1))
 | |
|     {
 | |
|       __set_errno (ENOSYS);
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   /* Create the cookie.  It will hold almost all the state.  */
 | |
|   union notify_data data;
 | |
|   memset (&data, '\0', sizeof (data));
 | |
|   data.fct = notification->sigev_notify_function;
 | |
|   data.param = notification->sigev_value;
 | |
| 
 | |
|   if (notification->sigev_notify_attributes != NULL)
 | |
|     {
 | |
|       /* The thread attribute has to be allocated separately.  */
 | |
|       data.attr = (pthread_attr_t *) malloc (sizeof (pthread_attr_t));
 | |
|       if (data.attr == NULL)
 | |
| 	return -1;
 | |
| 
 | |
|       memcpy (data.attr, notification->sigev_notify_attributes,
 | |
| 	      sizeof (pthread_attr_t));
 | |
|     }
 | |
| 
 | |
|   /* Construct the new request.  */
 | |
|   struct sigevent se;
 | |
|   se.sigev_notify = SIGEV_THREAD;
 | |
|   se.sigev_signo = netlink_socket;
 | |
|   se.sigev_value.sival_ptr = &data;
 | |
| 
 | |
|   /* Tell the kernel.  */
 | |
|   int retval = INLINE_SYSCALL (mq_notify, 2, mqdes, &se);
 | |
| 
 | |
|   /* If it failed, free the allocated memory.  */
 | |
|   if (__glibc_unlikely (retval != 0))
 | |
|     free (data.attr);
 | |
| 
 | |
|   return retval;
 | |
| }
 |