317 lines
6.4 KiB
C
317 lines
6.4 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright (c) International Business Machines Corp., 2001
|
|
*/
|
|
/*\
|
|
* [Description]
|
|
*
|
|
* Test the 13 possible semctl() commands
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
#include <stdlib.h>
|
|
#include "tst_safe_sysv_ipc.h"
|
|
#include "tst_test.h"
|
|
#include "lapi/sem.h"
|
|
#include "libnewipc.h"
|
|
|
|
#define INCVAL 2
|
|
#define NEWMODE 066
|
|
#define NCHILD 5
|
|
#define SEMUN_CAST (union semun)
|
|
|
|
static int sem_id = -1;
|
|
static int sem_index;
|
|
static struct semid_ds buf;
|
|
static struct seminfo ipc_buf;
|
|
static unsigned short array[PSEMS];
|
|
static struct sembuf sops;
|
|
static int pid_arr[NCHILD];
|
|
|
|
static void kill_all_children(void)
|
|
{
|
|
int j;
|
|
|
|
for (j = 0; j < NCHILD; j++)
|
|
SAFE_KILL(pid_arr[j], SIGKILL);
|
|
|
|
for (j = 0; j < NCHILD; j++)
|
|
SAFE_WAIT(NULL);
|
|
}
|
|
|
|
static void func_stat(void)
|
|
{
|
|
if (buf.sem_nsems == PSEMS && buf.sem_perm.mode == (SEM_RA))
|
|
tst_res(TPASS, "buf.sem_nsems and buf.sem_perm.mode are correct");
|
|
else
|
|
tst_res(TFAIL, "semaphore STAT info is incorrect");
|
|
}
|
|
|
|
static void set_setup(void)
|
|
{
|
|
buf.sem_perm.mode = SEM_RA | NEWMODE;
|
|
}
|
|
|
|
static void func_set(void)
|
|
{
|
|
SAFE_SEMCTL(sem_id, 0, IPC_STAT, (union semun)&buf);
|
|
|
|
if (buf.sem_perm.mode == (SEM_RA | NEWMODE))
|
|
tst_res(TPASS, "buf.sem_perm.mode is correct");
|
|
else
|
|
tst_res(TFAIL, "semaphore mode info is incorrect");
|
|
}
|
|
|
|
static void func_gall(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < PSEMS; i++) {
|
|
if (array[i] != 0) {
|
|
tst_res(TFAIL, "semaphore %d has unexpected value", i);
|
|
return;
|
|
}
|
|
}
|
|
tst_res(TPASS, "semaphores have expected values");
|
|
}
|
|
|
|
static void child_cnt(void)
|
|
{
|
|
sops.sem_num = 4;
|
|
sops.sem_flg = 0;
|
|
|
|
/*
|
|
* Do a semop that will cause the child to sleep.
|
|
* The child process will be killed in the func_ncnt
|
|
* routine which should cause an error to be return
|
|
* by the semop() call.
|
|
*/
|
|
if (semop(sem_id, &sops, 1) != -1)
|
|
tst_brk(TBROK, "semop succeeded - cnt_setup");
|
|
}
|
|
|
|
static void cnt_setup(int opval)
|
|
{
|
|
int pid, i;
|
|
|
|
sops.sem_num = 4;
|
|
sops.sem_flg = 0;
|
|
/*
|
|
* if seting up for GETZCNT, the semaphore value needs to be positive
|
|
*/
|
|
if (opval == 0) {
|
|
sops.sem_op = 1;
|
|
SAFE_SEMOP(sem_id, &sops, 1);
|
|
}
|
|
|
|
sops.sem_op = opval;
|
|
for (i = 0; i < NCHILD; i++) {
|
|
pid = SAFE_FORK();
|
|
if (pid == 0) {
|
|
child_cnt();
|
|
} else {
|
|
TST_PROCESS_STATE_WAIT(pid, 'S', 0);
|
|
pid_arr[i] = pid;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void func_cnt(int rval)
|
|
{
|
|
if (rval == NCHILD)
|
|
tst_res(TPASS, "number of sleeping processes is correct");
|
|
else
|
|
tst_res(TFAIL, "number of sleeping processes is not correct");
|
|
}
|
|
|
|
static void child_pid(void)
|
|
{
|
|
sops.sem_num = 2;
|
|
sops.sem_op = 1;
|
|
sops.sem_flg = 0;
|
|
/*
|
|
* Do a semop that will increment the semaphore.
|
|
*/
|
|
SAFE_SEMOP(sem_id, &sops, 1);
|
|
exit(0);
|
|
}
|
|
|
|
static void pid_setup(void)
|
|
{
|
|
int pid;
|
|
|
|
pid = SAFE_FORK();
|
|
if (pid == 0) {
|
|
child_pid();
|
|
} else {
|
|
pid_arr[2] = pid;
|
|
TST_PROCESS_STATE_WAIT(pid, 'Z', 0);
|
|
}
|
|
}
|
|
|
|
static void func_pid(int rval)
|
|
{
|
|
if (rval == pid_arr[2])
|
|
tst_res(TPASS, "last pid value is correct");
|
|
else
|
|
tst_res(TFAIL, "last pid value is not correct");
|
|
}
|
|
|
|
static void func_gval(int rval)
|
|
{
|
|
/*
|
|
* This is a simple test. The semaphore value should be equal
|
|
* to ONE as it was set in the last test (GETPID).
|
|
*/
|
|
if (rval == 1)
|
|
tst_res(TPASS, "semaphore value is correct");
|
|
else
|
|
tst_res(TFAIL, "semaphore value is not correct");
|
|
}
|
|
|
|
static void sall_setup(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < PSEMS; i++) {
|
|
array[i] = 3;
|
|
}
|
|
}
|
|
|
|
static void func_sall(void)
|
|
{
|
|
int i;
|
|
unsigned short rarray[PSEMS];
|
|
|
|
SAFE_SEMCTL(sem_id, 0, GETALL, (union semun)rarray);
|
|
for (i = 0; i < PSEMS; i++) {
|
|
if (array[i] != rarray[i]) {
|
|
tst_res(TFAIL, "semaphore values are not correct");
|
|
return;
|
|
}
|
|
}
|
|
|
|
tst_res(TPASS, "semaphore values are correct");
|
|
}
|
|
|
|
static void func_sval(void)
|
|
{
|
|
int semv;
|
|
union semun arr;
|
|
|
|
semv = SAFE_SEMCTL(sem_id, 4, GETVAL, arr);
|
|
if (semv != INCVAL)
|
|
tst_res(TFAIL, "semaphore value is not what was set");
|
|
else
|
|
tst_res(TPASS, "semaphore value is correct");
|
|
}
|
|
|
|
static void func_rmid(void)
|
|
{
|
|
TST_EXP_FAIL(semop(sem_id, &sops, 1), EINVAL, "semaphore appears to be removed");
|
|
sem_id = -1;
|
|
}
|
|
|
|
static void func_iinfo(int hidx)
|
|
{
|
|
if (hidx >= 0) {
|
|
sem_index = hidx;
|
|
tst_res(TPASS, "the highest index is correct");
|
|
} else {
|
|
sem_index = 0;
|
|
tst_res(TFAIL, "the highest index is incorrect");
|
|
}
|
|
}
|
|
|
|
static void func_sinfo(void)
|
|
{
|
|
if (ipc_buf.semusz < 1)
|
|
tst_res(TFAIL, "number of semaphore sets is incorrect");
|
|
else
|
|
tst_res(TPASS, "number of semaphore sets is correct");
|
|
}
|
|
|
|
static void func_sstat(int semidx)
|
|
{
|
|
if (semidx >= 0)
|
|
tst_res(TPASS, "id of the semaphore set is correct");
|
|
else
|
|
tst_res(TFAIL, "id of the semaphore set is incorrect");
|
|
}
|
|
|
|
static struct tcases {
|
|
int *semid;
|
|
int semnum;
|
|
int cmd;
|
|
void (*func_test) ();
|
|
union semun arg;
|
|
void (*func_setup) ();
|
|
} tests[] = {
|
|
{&sem_id, 0, IPC_STAT, func_stat, SEMUN_CAST & buf, NULL},
|
|
{&sem_id, 0, IPC_SET, func_set, SEMUN_CAST & buf, set_setup},
|
|
{&sem_id, 0, GETALL, func_gall, SEMUN_CAST array, NULL},
|
|
{&sem_id, 4, GETNCNT, func_cnt, SEMUN_CAST & buf, cnt_setup},
|
|
{&sem_id, 2, GETPID, func_pid, SEMUN_CAST & buf, pid_setup},
|
|
{&sem_id, 2, GETVAL, func_gval, SEMUN_CAST & buf, NULL},
|
|
{&sem_id, 4, GETZCNT, func_cnt, SEMUN_CAST & buf, cnt_setup},
|
|
{&sem_id, 0, SETALL, func_sall, SEMUN_CAST array, sall_setup},
|
|
{&sem_id, 4, SETVAL, func_sval, SEMUN_CAST INCVAL, NULL},
|
|
{&sem_id, 0, IPC_INFO, func_iinfo, SEMUN_CAST & ipc_buf, NULL},
|
|
{&sem_id, 0, SEM_INFO, func_sinfo, SEMUN_CAST & ipc_buf, NULL},
|
|
{&sem_index, 0, SEM_STAT, func_sstat, SEMUN_CAST & buf, NULL},
|
|
{&sem_id, 0, IPC_RMID, func_rmid, SEMUN_CAST & buf, NULL},
|
|
};
|
|
|
|
static void verify_semctl(unsigned int n)
|
|
{
|
|
struct tcases *tc = &tests[n];
|
|
int rval;
|
|
|
|
if (sem_id == -1)
|
|
sem_id = SAFE_SEMGET(IPC_PRIVATE, PSEMS, IPC_CREAT | IPC_EXCL | SEM_RA);
|
|
if (tc->func_setup) {
|
|
switch (tc->cmd) {
|
|
case GETNCNT:
|
|
tc->func_setup(-1);
|
|
break;
|
|
case GETZCNT:
|
|
tc->func_setup(0);
|
|
break;
|
|
default:
|
|
tc->func_setup();
|
|
break;
|
|
}
|
|
}
|
|
|
|
rval = SAFE_SEMCTL(*(tc->semid), tc->semnum, tc->cmd, tc->arg);
|
|
switch (tc->cmd) {
|
|
case GETNCNT:
|
|
case GETZCNT:
|
|
case GETPID:
|
|
case GETVAL:
|
|
case IPC_INFO:
|
|
case SEM_STAT:
|
|
tc->func_test(rval);
|
|
break;
|
|
default:
|
|
tc->func_test();
|
|
break;
|
|
}
|
|
|
|
if (tc->cmd == GETNCNT || tc->cmd == GETZCNT)
|
|
kill_all_children();
|
|
}
|
|
|
|
static void cleanup(void)
|
|
{
|
|
if (sem_id >= 0)
|
|
SAFE_SEMCTL(sem_id, 0, IPC_RMID);
|
|
}
|
|
|
|
static struct tst_test test = {
|
|
.cleanup = cleanup,
|
|
.test = verify_semctl,
|
|
.tcnt = ARRAY_SIZE(tests),
|
|
.forks_child = 1,
|
|
};
|