mirror of
				https://sourceware.org/git/glibc.git
				synced 2025-11-03 20:53:13 +03:00 
			
		
		
		
	I used these shell commands: ../glibc/scripts/update-copyrights $PWD/../gnulib/build-aux/update-copyright (cd ../glibc && git commit -am"[this commit message]") and then ignored the output, which consisted lines saying "FOO: warning: copyright statement not found" for each of 7061 files FOO. I then removed trailing white space from math/tgmath.h, support/tst-support-open-dev-null-range.c, and sysdeps/x86_64/multiarch/strlen-vec.S, to work around the following obscure pre-commit check failure diagnostics from Savannah. I don't know why I run into these diagnostics whereas others evidently do not. remote: *** 912-#endif remote: *** 913: remote: *** 914- remote: *** error: lines with trailing whitespace found ... remote: *** error: sysdeps/unix/sysv/linux/statx_cp.c: trailing lines
		
			
				
	
	
		
			275 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			275 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* Copyright (C) 2004-2022 Free Software Foundation, Inc.
 | 
						|
   This file is part of the GNU C Library.
 | 
						|
 | 
						|
   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 <pthreadP.h>
 | 
						|
#include <shlib-compat.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.  */
 | 
						|
  __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 (__pthread_create (&th, data.attr, notification_function, &data)
 | 
						|
	      == 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 && data.attr != NULL)
 | 
						|
	{
 | 
						|
	  /* The only state we keep is the copy of the thread attributes.  */
 | 
						|
	  __pthread_attr_destroy (data.attr);
 | 
						|
	  free (data.attr);
 | 
						|
	}
 | 
						|
    }
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void
 | 
						|
__mq_notify_fork_subprocess (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 (__pthread_barrier_init (¬ify_barrier, NULL, 2) == 0)
 | 
						|
    {
 | 
						|
      /* Create the helper thread.  */
 | 
						|
      pthread_attr_t attr;
 | 
						|
      __pthread_attr_init (&attr);
 | 
						|
      __pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
 | 
						|
      /* We do not need much stack space, the bare minimum will be enough.  */
 | 
						|
      __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);
 | 
						|
 | 
						|
      __pthread_attr_destroy (&attr);
 | 
						|
    }
 | 
						|
 | 
						|
  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;
 | 
						|
 | 
						|
      int ret = __pthread_attr_copy (data.attr,
 | 
						|
				     notification->sigev_notify_attributes);
 | 
						|
      if (ret != 0)
 | 
						|
	{
 | 
						|
	  free (data.attr);
 | 
						|
	  __set_errno (ret);
 | 
						|
	  return -1;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  /* 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 (retval != 0 && data.attr != NULL)
 | 
						|
    {
 | 
						|
      __pthread_attr_destroy (data.attr);
 | 
						|
      free (data.attr);
 | 
						|
    }
 | 
						|
 | 
						|
  return retval;
 | 
						|
}
 | 
						|
versioned_symbol (libc, __mq_notify, mq_notify, GLIBC_2_34);
 | 
						|
libc_hidden_ver (__mq_notify, mq_notify)
 | 
						|
#if OTHER_SHLIB_COMPAT (librt, GLIBC_2_3_4, GLIBC_2_34)
 | 
						|
compat_symbol (librt, __mq_notify, mq_notify, GLIBC_2_3_4);
 | 
						|
#endif
 |