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