1/*
2 * Copyright 2008, Salvatore Benedetto, salvatore.benedetto@gmail.com.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include <errno.h>
7#include <fcntl.h>
8#include <stdint.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <unistd.h>
13#include <sys/mman.h>
14#include <sys/sem.h>
15#include <sys/stat.h>
16#include <sys/time.h>
17#include <sys/wait.h>
18#include <time.h>
19
20#include <OS.h>
21
22#include "TestUnitUtils.h"
23
24#define KEY			((key_t)12345)
25#define NUM_OF_SEMS	10
26
27union semun {
28	int val;
29	struct semid_ds *buf;
30	unsigned short *array;
31};
32
33
34static status_t
35remove_semaphore(int semID)
36{
37	return semctl(semID, 0, IPC_RMID, 0);
38}
39
40
41static void
42test_semget()
43{
44	TEST_SET("semget({IPC_PRIVATE, key})");
45
46	const char* currentTest = NULL;
47
48	// Open private set with IPC_PRIVATE
49	TEST("semget(IPC_PRIVATE) - private");
50	int semID = semget(IPC_PRIVATE, NUM_OF_SEMS, S_IRUSR | S_IWUSR);
51	assert_posix_bool_success(semID != -1);
52
53	// Destroy private semaphore
54	TEST("semctl(IPC_RMID) - private");
55	status_t status = remove_semaphore(semID);
56	assert_posix_bool_success(status != -1);
57
58	// Open non-private non-existing set with IPC_CREAT
59	TEST("semget(KEY, IPC_CREAT) non-existing");
60	semID = semget(KEY, NUM_OF_SEMS, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR
61		| S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
62	assert_posix_bool_success(status != -1);
63
64	// Re-open non-private existing without IPC_CREAT
65	TEST("semget(KEY) re-open existing without IPC_CREAT");
66	int returnID = semget(KEY, 0, 0);
67	assert_equals(semID, returnID);
68
69	// Re-open non-private existing with IPC_CREAT
70	TEST("semget(IPC_CREATE) re-open existing with IPC_CREAT");
71	returnID = semget(KEY, 0, IPC_CREAT | IPC_EXCL);
72	assert_posix_bool_success(errno == EEXIST);
73
74	// Destroy non-private semaphore
75	TEST("semctl(IPC_RMID)");
76	status = remove_semaphore(semID);
77	assert_posix_bool_success(status != -1);
78
79	// Open non-private non-existing without IPC_CREAT
80	TEST("semget(IPC_CREATE) non-existing without IPC_CREAT");
81	semID = semget(KEY, NUM_OF_SEMS, IPC_EXCL | S_IRUSR | S_IWUSR
82		| S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
83	assert_posix_bool_success(errno == ENOENT);
84
85	// Destroy non-existing semaphore
86	TEST("semctl()");
87	status = remove_semaphore(semID);
88	assert_posix_bool_success(errno == EINVAL);
89
90	TEST("done");
91}
92
93
94static void
95test_semop2()
96{
97	TEST_SET("semop2()");
98
99	const char* currentTest = NULL;
100
101	// Re-open non-private existing without IPC_CREAT
102	TEST("semget(KEY) re-open existing without IPC_CREAT");
103	int returnedID = semget(KEY, 0, 0);
104	assert_posix_bool_success(returnedID != -1);
105
106	TEST("semop(IPC_NOWAIT) - wait for zero");
107	// Set up array of semaphores
108	struct sembuf array[NUM_OF_SEMS];
109	for (int i = 0; i < NUM_OF_SEMS; i++) {
110		array[i].sem_num = i;
111		array[i].sem_op = 0;
112		array[i].sem_flg = IPC_NOWAIT;
113	}
114	semop(returnedID, array, NUM_OF_SEMS);
115	assert_posix_bool_success(errno == EAGAIN);
116
117	TEST("semop(IPC_NOWAIT) - wait to increase");
118	for (int i = 0; i < NUM_OF_SEMS; i++) {
119		array[i].sem_num = i;
120		array[i].sem_op = -9;
121		array[i].sem_flg = IPC_NOWAIT;
122	}
123	semop(returnedID, array, NUM_OF_SEMS);
124	assert_posix_bool_success(errno == EAGAIN);
125
126	TEST("semop(IPC_NOWAIT) - acquire resource sem #0");
127	struct sembuf ops;
128	ops.sem_num = 0;
129	ops.sem_op = -8;
130	ops.sem_flg = 0;
131	status_t status = semop(returnedID, &ops, 1);
132	assert_posix_bool_success(status != -1);
133
134	TEST("semop(IPC_NOWAIT) - acquire zero sem #0");
135	ops.sem_num = 0;
136	ops.sem_op = 0;
137	ops.sem_flg = 0;
138	status = semop(returnedID, &ops, 1);
139
140	TEST("semop(IPC_NOWAIT) - revert semop sem #0");
141	ops.sem_num = 0;
142	ops.sem_op = 8;
143	ops.sem_flg = 0;
144	status = semop(returnedID, &ops, 1);
145
146	// Decrease to zero even semaphores and
147	// use SEM_UNDO flag on odd semaphores in order
148	// to wake up the father on exit
149	// Set up array of semaphores
150	for (int i = 0; i < NUM_OF_SEMS; i++) {
151		array[i].sem_num = i;
152		array[i].sem_op = -8;
153		if (i % 2)
154			array[i].sem_flg = 0;
155		else
156			array[i].sem_flg = SEM_UNDO;
157	}
158	TEST("semop() - father");
159	status = semop(returnedID, array, NUM_OF_SEMS);
160	assert_posix_bool_success(status != -1);
161
162	TEST("done");
163}
164
165
166static void
167test_semop()
168{
169	TEST_SET("semop()");
170	const char* currentTest = NULL;
171
172	// Open non-private non-existing set with IPC_CREAT
173	TEST("semget(IPC_CREATE) non-existing");
174	int semID = semget(KEY, NUM_OF_SEMS, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR
175		| S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
176	assert_posix_bool_success(semID != -1);
177
178	// SETALL
179	TEST("semctl(SETALL)");
180	union semun args;
181	args.array = (unsigned short *)malloc(sizeof(unsigned short) * NUM_OF_SEMS);
182	for (int i = 0; i < NUM_OF_SEMS; i++)
183		args.array[i] = 8;
184	status_t status = semctl(semID, 0, SETALL, args);
185	assert_posix_bool_success(status != -1);
186	free(args.array);
187
188	pid_t child = fork();
189	if (child == 0) {
190		// The child first will test the IPC_NOWAIT
191		// feature, while the father waits for him,
192		// by waiting for zero on even semaphores,
193		// and to increase for odd semaphores, which
194		// will happen on process exit due to SEM_UNDO
195		// feature.
196		test_semop2();
197		exit(0);
198	}
199
200	wait_for_child(child);
201
202	// Set up array of semaphores
203	struct sembuf array[NUM_OF_SEMS];
204	for (int i = 0; i < NUM_OF_SEMS; i++) {
205		array[i].sem_num = i;
206		if (i % 2)
207			array[i].sem_op = 0; // wait for zero
208		else
209			array[i].sem_op = -8; // wait to increase
210		array[i].sem_flg = 0;
211	}
212	TEST("semop() - father acquired set");
213	status = semop(semID, array, NUM_OF_SEMS);
214	assert_posix_bool_success(status != -1);
215
216	// Destroy non-private semaphore
217	TEST("semctl(IPC_RMID)");
218	status = remove_semaphore(semID);
219	assert_posix_bool_success(status != 1);
220
221	TEST("done");
222}
223
224
225static void
226test_semctl()
227{
228	TEST_SET("semctl({GETVAL, SETVAL, GETPID, GETNCNT, GETZCNT, GETALL, SETALL, IPC_STAT, IPC_SET, IPC_RMID})");
229
230	const char* currentTest = NULL;
231
232	// Open non-private non-existing set with IPC_CREAT
233	TEST("semget(IPC_CREATE) non-existing");
234	int semID = semget(KEY, NUM_OF_SEMS, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR
235		| S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
236	assert_posix_bool_success(semID != -1);
237
238	// GETVAL
239	TEST("semctl(GETVAL)");
240	union semun args;
241	status_t status = semctl(semID, NUM_OF_SEMS - 1, GETVAL, args);
242	// Semaphore is not initialized. Value is unknown.
243	// We care about not crashing into KDL.
244	assert_posix_bool_success(status != -1);
245
246	// SETALL
247	TEST("semctl(SETALL)");
248	args.array = (unsigned short *)malloc(sizeof(unsigned short) * NUM_OF_SEMS);
249	for (int i = 0; i < NUM_OF_SEMS; i++)
250		args.array[i] = 5;
251	status = semctl(semID, 0, SETALL, args);
252	assert_posix_bool_success(status != -1);
253	free(args.array);
254
255	// GETVAL semaphore 4
256	int returnedValue = semctl(semID, 4, GETVAL, 0);
257	assert_equals((unsigned short)returnedValue, (unsigned short)5);
258
259	// GETALL
260	TEST("semctl(GETALL)");
261	args.array = (unsigned short *)malloc(sizeof(unsigned short) * NUM_OF_SEMS);
262	semctl(semID, 0, GETALL, args);
263	// Check only last semaphore value
264	assert_equals(args.array[NUM_OF_SEMS - 1], (unsigned short)5);
265	free(args.array);
266
267	// SETVAL semaphore 2
268	TEST("semctl(SETVAL) - semaphore #2");
269	args.val = 7;
270	status = semctl(semID, 2, SETVAL, args);
271	assert_posix_bool_success(status != 1);
272
273	// GETALL
274	TEST("semctl(GETALL)");
275	args.array = (unsigned short *)malloc(sizeof(unsigned short) * NUM_OF_SEMS);
276	status = semctl(semID, 0, GETALL, args);
277	assert_posix_bool_success(status != -1);
278	TEST("semctl(GETALL) - semaphore #10");
279	assert_equals(args.array[NUM_OF_SEMS - 1], (unsigned short)5);
280	TEST("semctl(GETALL) - semaphore #2");
281	assert_equals(args.array[NUM_OF_SEMS - 1], (unsigned short)5);
282	free(args.array);
283
284	// IPC_SET
285	TEST("semctl(IPC_SET)");
286	struct semid_ds semaphore;
287	memset(&semaphore, 0, sizeof(struct semid_ds));
288	semaphore.sem_perm.uid = getuid() + 3;
289	semaphore.sem_perm.gid = getgid() + 3;
290	semaphore.sem_perm.mode = 0666;
291	args.buf = &semaphore;
292	status = semctl(semID, 0, IPC_SET, args);
293	assert_posix_bool_success(status != 1);
294
295	// IPC_STAT set
296	TEST("semctl(IPC_STAT)");
297	memset(&semaphore, 0, sizeof(struct semid_ds));
298	args.buf = &semaphore;
299	status = semctl(semID, 0, IPC_STAT, args);
300	assert_posix_bool_success(status != 1);
301	TEST("semctl(IPC_STAT): number of sems");
302	assert_equals((unsigned short)args.buf->sem_nsems, (unsigned short)NUM_OF_SEMS);
303	TEST("semctl(IPC_STAT): uid");
304	assert_equals(args.buf->sem_perm.uid, getuid() + 3);
305	TEST("semctl(IPC_STAT): gid");
306	assert_equals(args.buf->sem_perm.gid, getgid() + 3);
307
308	// Destroy non-private semaphore
309	TEST("semctl(IPC_RMID)");
310	status = remove_semaphore(semID);
311	assert_posix_bool_success(status != 1);
312
313	TEST("done");
314}
315
316
317int
318main()
319{
320	test_semget();
321	test_semctl();
322	test_semop();
323
324	printf("\nAll tests OK\n");
325}
326