(root)/
glibc-2.38/
sysdeps/
unix/
sysv/
linux/
tst-sysvsem-linux.c
       1  /* Basic tests for Linux SYSV semaphore extensions.
       2     Copyright (C) 2020-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <sys/ipc.h>
      20  #include <sys/sem.h>
      21  #include <errno.h>
      22  #include <stdlib.h>
      23  #include <stdbool.h>
      24  #include <stdio.h>
      25  
      26  #include <support/check.h>
      27  #include <support/temp_file.h>
      28  
      29  /* These are for the temporary file we generate.  */
      30  static char *name;
      31  static int semid;
      32  
      33  static void
      34  remove_sem (void)
      35  {
      36    /* Enforce message queue removal in case of early test failure.
      37       Ignore error since the sem may already have being removed.  */
      38    semctl (semid, 0, IPC_RMID, 0);
      39  }
      40  
      41  static void
      42  do_prepare (int argc, char *argv[])
      43  {
      44    TEST_VERIFY_EXIT (create_temp_file ("tst-sysvsem.", &name) != -1);
      45  }
      46  
      47  #define PREPARE do_prepare
      48  
      49  #define SEM_MODE 0644
      50  
      51  union semun
      52  {
      53    int val;
      54    struct semid_ds *buf;
      55    unsigned short  *array;
      56    struct seminfo *__buf;
      57  };
      58  
      59  struct test_seminfo
      60  {
      61    int semmsl;
      62    int semmns;
      63    int semopm;
      64    int semmni;
      65  };
      66  
      67  /* It tries to obtain some system-wide SysV semaphore information from /proc
      68     to check against IPC_INFO/SEM_INFO.  The /proc only returns the tunables
      69     value of SEMMSL, SEMMNS, SEMOPM, and SEMMNI.
      70  
      71     The kernel also returns constant value for SEMVMX, SEMMNU, SEMMAP, SEMUME,
      72     and also SEMUSZ and SEMAEM (for IPC_INFO).  The issue to check them is they
      73     might change over kernel releases.  */
      74  
      75  static void
      76  read_sem_stat (struct test_seminfo *tseminfo)
      77  {
      78    FILE *f = fopen ("/proc/sys/kernel/sem", "r");
      79    if (f == NULL)
      80      FAIL_UNSUPPORTED ("/proc is not mounted or /proc/sys/kernel/sem is not "
      81  		      "available");
      82  
      83    int r = fscanf (f, "%d %d %d %d",
      84  		  &tseminfo->semmsl, &tseminfo->semmns, &tseminfo->semopm,
      85  		  &tseminfo->semmni);
      86    TEST_VERIFY_EXIT (r == 4);
      87  
      88    fclose (f);
      89  }
      90  
      91  
      92  /* Check if the semaphore with IDX (index into the kernel's internal array)
      93     matches the one with KEY.  The CMD is either SEM_STAT or SEM_STAT_ANY.  */
      94  
      95  static bool
      96  check_seminfo (int idx, key_t key, int cmd)
      97  {
      98    struct semid_ds seminfo;
      99    int sid = semctl (idx, 0, cmd, (union semun) { .buf = &seminfo });
     100    /* Ignore unused array slot returned by the kernel or information from
     101       unknown semaphores.  */
     102    if ((sid == -1 && errno == EINVAL) || sid != semid)
     103      return false;
     104  
     105    if (sid == -1)
     106      FAIL_EXIT1 ("semctl with SEM_STAT failed (errno=%d)", errno);
     107  
     108    TEST_COMPARE (seminfo.sem_perm.__key, key);
     109    TEST_COMPARE (seminfo.sem_perm.mode, SEM_MODE);
     110    TEST_COMPARE (seminfo.sem_nsems, 1);
     111  
     112    return true;
     113  }
     114  
     115  static int
     116  do_test (void)
     117  {
     118    atexit (remove_sem);
     119  
     120    key_t key = ftok (name, 'G');
     121    if (key == -1)
     122      FAIL_EXIT1 ("ftok failed: %m");
     123  
     124    semid = semget (key, 1, IPC_CREAT | IPC_EXCL | SEM_MODE);
     125    if (semid == -1)
     126      FAIL_EXIT1 ("semget failed: %m");
     127  
     128    struct test_seminfo tipcinfo;
     129    read_sem_stat (&tipcinfo);
     130  
     131    int semidx;
     132  
     133    {
     134      struct seminfo ipcinfo;
     135      semidx = semctl (semid, 0, IPC_INFO, (union semun) { .__buf = &ipcinfo });
     136      if (semidx == -1)
     137        FAIL_EXIT1 ("semctl with IPC_INFO failed: %m");
     138  
     139      TEST_COMPARE (ipcinfo.semmsl, tipcinfo.semmsl);
     140      TEST_COMPARE (ipcinfo.semmns, tipcinfo.semmns);
     141      TEST_COMPARE (ipcinfo.semopm, tipcinfo.semopm);
     142      TEST_COMPARE (ipcinfo.semmni, tipcinfo.semmni);
     143    }
     144  
     145    /* Same as before but with SEM_INFO.  */
     146    {
     147      struct seminfo ipcinfo;
     148      semidx = semctl (semid, 0, SEM_INFO, (union semun) { .__buf = &ipcinfo });
     149      if (semidx == -1)
     150        FAIL_EXIT1 ("semctl with IPC_INFO failed: %m");
     151  
     152      TEST_COMPARE (ipcinfo.semmsl, tipcinfo.semmsl);
     153      TEST_COMPARE (ipcinfo.semmns, tipcinfo.semmns);
     154      TEST_COMPARE (ipcinfo.semopm, tipcinfo.semopm);
     155      TEST_COMPARE (ipcinfo.semmni, tipcinfo.semmni);
     156    }
     157  
     158    /* We check if the created semaphore shows in the system-wide status.  */
     159    bool found = false;
     160    for (int i = 0; i <= semidx; i++)
     161      {
     162        /* We can't tell apart if SEM_STAT_ANY is not supported (kernel older
     163  	 than 4.17) or if the index used is invalid.  So it just check if
     164  	 value returned from a valid call matches the created semaphore.  */
     165        check_seminfo (i, key, SEM_STAT_ANY);
     166  
     167        if (check_seminfo (i, key, SEM_STAT))
     168  	{
     169  	  found = true;
     170  	  break;
     171  	}
     172      }
     173  
     174    if (!found)
     175      FAIL_EXIT1 ("semctl with SEM_STAT/SEM_STAT_ANY could not find the "
     176  		"created  semaphore");
     177  
     178    if (semctl (semid, 0, IPC_RMID, 0) == -1)
     179      FAIL_EXIT1 ("semctl failed: %m");
     180  
     181    return 0;
     182  }
     183  
     184  #include <support/test-driver.c>