Search code examples
posixsemaphore

POSIX named semaphore name without slash


The man page sem_overview(7) explains that the name of named semaphores ought to start with /. However, I couldn't find any specification of what should or would happen if such a name does not start by a slash.

Trying it out on Linux seems to reveal no difference at all and xxd /dev/shm/sem.mysem also shows no difference when using sem_open("mysem", 0) vs. sem_open("/mysem", 0) in a (C) program.

Is the naming scheme actually of any relevance?


Solution

  • TLDR:

    The GLIBC implementation ignores leading / characters despite what the documentation says.

    Full answer:

    Per POSIX sem_open():

    SYNOPSIS

    #include <semaphore.h>
    
    sem_t *sem_open(const char *name, int oflag, ...);
    

    DESCRIPTION

    ...

    If name does not begin with the <slash> character, the effect is implementation-defined.

    ...

    So, in this case the name will be treated per the Linux sem_open() man page:

    DESCRIPTION

    sem_open() creates a new POSIX semaphore or opens an existing semaphore. The semaphore is identified by name. For details of the construction of name, see sem_overview(7).

    Which brings us back to the sem_overview(7) man page as you noted:

    Named semaphores

    A named semaphore is identified by a name of the form /somename; that is, a null-terminated string of up to NAME_MAX-4 (i.e., 251) characters consisting of an initial slash, followed by one or more characters, none of which are slashes.

    As you noted, that doesn't specify what happens if the leading / is omitted.

    So, how is it implemented?

    The actual name of the semaphore file (that's all a "POSIX named semaphore" is - a file in a file system that is memory mapped and contains a semaphore object) is created in GLIBC by the __shm_get_name() function in the posix/shm-directory.c file:

    int
    __shm_get_name (struct shmdir_name *result, const char *name, bool sem_prefix)
    {
      struct alloc_buffer buffer;
      size_t namelen;
    
      buffer = alloc_buffer_create (result->name, sizeof (result->name));
      alloc_buffer_copy_bytes (&buffer, SHMDIR, strlen (SHMDIR));
    
    #if defined (SHM_ANON) && defined (O_TMPFILE)
      if (name == SHM_ANON)
        {
          /* For SHM_ANON, we want shm_open () to pass O_TMPFILE to open (),
             with SHMDIR itself as the path.  So, leave it at that.  */
          alloc_buffer_add_byte (&buffer, 0);
          if (alloc_buffer_has_failed (&buffer))
            return -1;
          return 0;
        }
    #endif
    
      while (name[0] == '/')
        ++name;
      namelen = strlen (name);
    
      if (sem_prefix)
        alloc_buffer_copy_bytes (&buffer, "sem.", strlen ("sem."));
      alloc_buffer_copy_bytes (&buffer, name, namelen + 1);
      if (namelen == 0 || memchr (name, '/', namelen) != NULL)
        return EINVAL;
      if (alloc_buffer_has_failed (&buffer))
        {
          if (namelen > NAME_MAX)
            return ENAMETOOLONG;
          return EINVAL;
        }
      return 0;
    }
    

    Note this code:

    while (name[0] == '/')
        ++name;
    

    The code skips over all leading / characters if they exist. If there are no leading / characters, GLIBC proceeds without error.

    GLIBC's implementation makes leading / characters use irrelevant. The result is the same no matter how many leading / characters are used, even if that number is zero, despite what the sem_overview man page states.