1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) International Business Machines  Corp., 2001
4 */
5/*\
6 * [Description]
7 *
8 * Test the 13 possible semctl() commands
9 */
10
11#define _GNU_SOURCE
12#include <stdlib.h>
13#include "tst_safe_sysv_ipc.h"
14#include "tst_test.h"
15#include "lapi/sem.h"
16#include "libnewipc.h"
17
18#define INCVAL 2
19#define NEWMODE 066
20#define NCHILD  5
21#define SEMUN_CAST (union semun)
22
23static int sem_id = -1;
24static int sem_index;
25static struct semid_ds buf;
26static struct seminfo ipc_buf;
27static unsigned short array[PSEMS];
28static struct sembuf sops;
29static int pid_arr[NCHILD];
30
31static void kill_all_children(void)
32{
33	int j;
34
35	for (j = 0; j < NCHILD; j++)
36		SAFE_KILL(pid_arr[j], SIGKILL);
37
38	for (j = 0; j < NCHILD; j++)
39		SAFE_WAIT(NULL);
40}
41
42static void func_stat(void)
43{
44	if (buf.sem_nsems == PSEMS && buf.sem_perm.mode == (SEM_RA))
45		tst_res(TPASS, "buf.sem_nsems and buf.sem_perm.mode are correct");
46	else
47		tst_res(TFAIL, "semaphore STAT info is incorrect");
48}
49
50static void set_setup(void)
51{
52	buf.sem_perm.mode = SEM_RA | NEWMODE;
53}
54
55static void func_set(void)
56{
57	SAFE_SEMCTL(sem_id, 0, IPC_STAT, (union semun)&buf);
58
59	if (buf.sem_perm.mode == (SEM_RA | NEWMODE))
60		tst_res(TPASS, "buf.sem_perm.mode is correct");
61	else
62		tst_res(TFAIL, "semaphore mode info is incorrect");
63}
64
65static void func_gall(void)
66{
67	int i;
68
69	for (i = 0; i < PSEMS; i++) {
70		if (array[i] != 0) {
71			tst_res(TFAIL, "semaphore %d has unexpected value", i);
72			return;
73		}
74	}
75	tst_res(TPASS, "semaphores have expected values");
76}
77
78static void child_cnt(void)
79{
80	sops.sem_num = 4;
81	sops.sem_flg = 0;
82
83	/*
84	 * Do a semop that will cause the child to sleep.
85	 * The child process will be killed in the func_ncnt
86	 * routine which should cause an error to be return
87	 * by the semop() call.
88	 */
89	if (semop(sem_id, &sops, 1) != -1)
90		tst_brk(TBROK, "semop succeeded - cnt_setup");
91}
92
93static void cnt_setup(int opval)
94{
95	int pid, i;
96
97	sops.sem_num = 4;
98	sops.sem_flg = 0;
99	/*
100	 * if seting up for GETZCNT, the semaphore value needs to be positive
101	 */
102	if (opval == 0) {
103		sops.sem_op = 1;
104		SAFE_SEMOP(sem_id, &sops, 1);
105	}
106
107	sops.sem_op = opval;
108	for (i = 0; i < NCHILD; i++) {
109		pid = SAFE_FORK();
110		if (pid == 0) {
111			child_cnt();
112		} else {
113			TST_PROCESS_STATE_WAIT(pid, 'S', 0);
114			pid_arr[i] = pid;
115		}
116	}
117}
118
119static void func_cnt(int rval)
120{
121	if (rval == NCHILD)
122		tst_res(TPASS, "number of sleeping processes is correct");
123	else
124		tst_res(TFAIL, "number of sleeping processes is not correct");
125}
126
127static void child_pid(void)
128{
129	sops.sem_num = 2;
130	sops.sem_op = 1;
131	sops.sem_flg = 0;
132	/*
133	 * Do a semop that will increment the semaphore.
134	 */
135	SAFE_SEMOP(sem_id, &sops, 1);
136	exit(0);
137}
138
139static void pid_setup(void)
140{
141	int pid;
142
143	pid = SAFE_FORK();
144	if (pid == 0) {
145		child_pid();
146	} else {
147		pid_arr[2] = pid;
148		TST_PROCESS_STATE_WAIT(pid, 'Z', 0);
149	}
150}
151
152static void func_pid(int rval)
153{
154	if (rval == pid_arr[2])
155		tst_res(TPASS, "last pid value is correct");
156	else
157		tst_res(TFAIL, "last pid value is not correct");
158}
159
160static void func_gval(int rval)
161{
162	/*
163	 * This is a simple test.  The semaphore value should be equal
164	 * to ONE as it was set in the last test (GETPID).
165	 */
166	if (rval == 1)
167		tst_res(TPASS, "semaphore value is correct");
168	else
169		tst_res(TFAIL, "semaphore value is not correct");
170}
171
172static void sall_setup(void)
173{
174	int i;
175
176	for (i = 0; i < PSEMS; i++) {
177		array[i] = 3;
178	}
179}
180
181static void func_sall(void)
182{
183	int i;
184	unsigned short rarray[PSEMS];
185
186	SAFE_SEMCTL(sem_id, 0, GETALL, (union semun)rarray);
187	for (i = 0; i < PSEMS; i++) {
188		if (array[i] != rarray[i]) {
189			tst_res(TFAIL, "semaphore values are not correct");
190			return;
191		}
192	}
193
194	tst_res(TPASS, "semaphore values are correct");
195}
196
197static void func_sval(void)
198{
199	int semv = SAFE_SEMCTL(sem_id, 4, GETVAL);
200
201	if (semv != INCVAL)
202		tst_res(TFAIL, "semaphore value is not what was set");
203	else
204		tst_res(TPASS, "semaphore value is correct");
205}
206
207static void func_rmid(void)
208{
209	TST_EXP_FAIL(semop(sem_id, &sops, 1), EINVAL, "semaphore appears to be removed");
210	sem_id = -1;
211}
212
213static void func_iinfo(int hidx)
214{
215	if (hidx >= 0) {
216		sem_index = hidx;
217		tst_res(TPASS, "the highest index is correct");
218	} else {
219		sem_index = 0;
220		tst_res(TFAIL, "the highest index is incorrect");
221	}
222}
223
224static void func_sinfo(void)
225{
226	if (ipc_buf.semusz < 1)
227		tst_res(TFAIL, "number of semaphore sets is incorrect");
228	else
229		tst_res(TPASS, "number of semaphore sets is correct");
230}
231
232static void func_sstat(int semidx)
233{
234	if (semidx >= 0)
235		tst_res(TPASS, "id of the semaphore set is correct");
236	else
237		tst_res(TFAIL, "id of the semaphore set is incorrect");
238}
239
240static struct tcases {
241	int *semid;
242	int semnum;
243	int cmd;
244	void (*func_test) ();
245	union semun arg;
246	void (*func_setup) ();
247} tests[] = {
248	{&sem_id, 0, IPC_STAT, func_stat, SEMUN_CAST & buf, NULL},
249	{&sem_id, 0, IPC_SET, func_set, SEMUN_CAST & buf, set_setup},
250	{&sem_id, 0, GETALL, func_gall, SEMUN_CAST array, NULL},
251	{&sem_id, 4, GETNCNT, func_cnt, SEMUN_CAST & buf, cnt_setup},
252	{&sem_id, 2, GETPID, func_pid, SEMUN_CAST & buf, pid_setup},
253	{&sem_id, 2, GETVAL, func_gval, SEMUN_CAST & buf, NULL},
254	{&sem_id, 4, GETZCNT, func_cnt, SEMUN_CAST & buf, cnt_setup},
255	{&sem_id, 0, SETALL, func_sall, SEMUN_CAST array, sall_setup},
256	{&sem_id, 4, SETVAL, func_sval, SEMUN_CAST INCVAL, NULL},
257	{&sem_id, 0, IPC_INFO, func_iinfo, SEMUN_CAST & ipc_buf, NULL},
258	{&sem_id, 0, SEM_INFO, func_sinfo, SEMUN_CAST & ipc_buf, NULL},
259	{&sem_index, 0, SEM_STAT, func_sstat, SEMUN_CAST & buf, NULL},
260	{&sem_id, 0, IPC_RMID, func_rmid, SEMUN_CAST & buf, NULL},
261};
262
263static void verify_semctl(unsigned int n)
264{
265	struct tcases *tc = &tests[n];
266	int rval;
267
268	if (sem_id == -1)
269		sem_id = SAFE_SEMGET(IPC_PRIVATE, PSEMS, IPC_CREAT | IPC_EXCL | SEM_RA);
270	if (tc->func_setup) {
271		switch (tc->cmd) {
272		case GETNCNT:
273			tc->func_setup(-1);
274			break;
275		case GETZCNT:
276			tc->func_setup(0);
277			break;
278		default:
279			tc->func_setup();
280			break;
281		}
282	}
283
284	rval = SAFE_SEMCTL(*(tc->semid), tc->semnum, tc->cmd, tc->arg);
285	switch (tc->cmd) {
286	case GETNCNT:
287	case GETZCNT:
288	case GETPID:
289	case GETVAL:
290	case IPC_INFO:
291	case SEM_STAT:
292		tc->func_test(rval);
293		break;
294	default:
295		tc->func_test();
296		break;
297	}
298
299	if (tc->cmd == GETNCNT || tc->cmd == GETZCNT)
300		kill_all_children();
301}
302
303static void cleanup(void)
304{
305	if (sem_id >= 0)
306		SAFE_SEMCTL(sem_id, 0, IPC_RMID);
307}
308
309static struct tst_test test = {
310	.cleanup = cleanup,
311	.test = verify_semctl,
312	.tcnt = ARRAY_SIZE(tests),
313	.forks_child = 1,
314};
315