1/*
2 * Copyright (c) 2000-2001, 2005-2008 Proofpoint, Inc. and its suppliers.
3 *      All rights reserved.
4 *
5 * By using this file, you agree to the terms and conditions set
6 * forth in the LICENSE file which can be found at the top level of
7 * the sendmail distribution.
8 */
9
10#include <sm/gen.h>
11SM_RCSID("@(#)$Id: t-sem.c,v 1.18 2013-11-22 20:51:43 ca Exp $")
12
13#include <stdio.h>
14
15#if SM_CONF_SEM
16# include <stdlib.h>
17# include <unistd.h>
18# include <sysexits.h>
19# include <sm/heap.h>
20# include <sm/string.h>
21# include <sm/signal.h>
22# include <sm/test.h>
23# include <sm/conf.h>
24# include <sm/sem.h>
25
26# define T_SM_SEM_KEY (4321L)
27
28static void
29delay(t, s)
30	int t;
31	char *s;
32{
33	if (t > 0)
34	{
35# if DEBUG
36		fprintf(stderr, "sleep(%d) before %s\n", t, s);
37# endif
38		sleep(t);
39	}
40# if DEBUG
41	fprintf(stderr, "%s\n", s);
42# endif
43}
44
45
46/*
47**  SEMINTER -- interactive testing of semaphores.
48**
49**	Parameters:
50**		owner -- create semaphores.
51**
52**	Returns:
53**		0 on success
54**		< 0 on failure.
55*/
56
57static int
58seminter(owner)
59	bool owner;
60{
61	int semid;
62	int t;
63
64	semid = sm_sem_start(T_SM_SEM_KEY, SM_NSEM, 0, owner);
65	if (semid < 0)
66	{
67		perror("sm_sem_start failed");
68		return 1;
69	}
70
71	while ((t = getchar()) != EOF)
72	{
73		switch (t)
74		{
75		  case 'a':
76			delay(0, "try to acq");
77			if (sm_sem_acq(semid, 0, 2) < 0)
78			{
79				perror("sm_sem_acq failed");
80				return 1;
81			}
82			delay(0, "acquired");
83			break;
84
85		  case 'r':
86			delay(0, "try to rel");
87			if (sm_sem_rel(semid, 0, 2) < 0)
88			{
89				perror("sm_sem_rel failed");
90				return 1;
91			}
92			delay(0, "released");
93			break;
94
95		  case 'v':
96			if ((t = sm_sem_get(semid, 0)) < 0)
97			{
98				perror("get_sem failed");
99				return 1;
100			}
101			printf("semval: %d\n", t);
102			break;
103
104		}
105	}
106	if (owner)
107		return sm_sem_stop(semid);
108	return 0;
109}
110
111/*
112**  SEM_CLEANUP -- cleanup if something breaks
113**
114**	Parameters:
115**		sig -- signal.
116**
117**	Returns:
118**		none.
119*/
120
121static int semid_c = -1;
122void
123sem_cleanup(sig)
124	int sig;
125{
126	if (semid_c >= 0)
127		(void) sm_sem_stop(semid_c);
128	exit(EX_UNAVAILABLE);
129}
130
131static int
132drop_priv(uid, gid)
133	uid_t uid;
134	gid_t gid;
135{
136	int r;
137
138	r = setgid(gid);
139	if (r != 0)
140		return r;
141	r = setuid(uid);
142	return r;
143}
144
145/*
146**  SEMTEST -- test of semaphores
147**
148**	Parameters:
149**		owner -- create semaphores.
150**
151**	Returns:
152**		0 on success
153**		< 0 on failure.
154*/
155
156# define MAX_CNT	10
157
158static int
159semtest(owner, uid, gid)
160	int owner;
161	uid_t uid;
162	gid_t gid;
163{
164	int semid, r;
165	int cnt = 0;
166
167	if (!owner && uid != 0)
168	{
169		r = drop_priv(uid, gid);
170		if (r < 0)
171		{
172			perror("drop_priv child failed");
173			return -1;
174		}
175	}
176	semid = sm_sem_start(T_SM_SEM_KEY, 1, 0, owner);
177	if (semid < 0)
178	{
179		perror("sm_sem_start failed");
180		return -1;
181	}
182
183	if (owner)
184	{
185		if (uid != 0)
186		{
187			r = sm_semsetowner(semid, uid, gid, 0660);
188			if (r < 0)
189			{
190				perror("sm_semsetowner failed");
191				return -1;
192			}
193			r = drop_priv(uid, gid);
194			if (r < 0)
195			{
196				perror("drop_priv owner failed");
197				return -1;
198			}
199		}
200
201		/* just in case someone kills the program... */
202		semid_c = semid;
203		(void) sm_signal(SIGHUP, sem_cleanup);
204		(void) sm_signal(SIGINT, sem_cleanup);
205		(void) sm_signal(SIGTERM, sem_cleanup);
206
207		delay(1, "parent: acquire 1");
208		cnt = 0;
209		do
210		{
211			r = sm_sem_acq(semid, 0, 0);
212			if (r < 0)
213			{
214				sleep(1);
215				++cnt;
216			}
217		} while (r < 0 && cnt <= MAX_CNT);
218		SM_TEST(r >= 0);
219		if (r < 0)
220			return r;
221
222		delay(3, "parent: release 1");
223		cnt = 0;
224		do
225		{
226			r = sm_sem_rel(semid, 0, 0);
227			if (r < 0)
228			{
229				sleep(1);
230				++cnt;
231			}
232		} while (r < 0 && cnt <= MAX_CNT);
233		SM_TEST(r >= 0);
234		if (r < 0)
235			return r;
236
237		delay(1, "parent: getval");
238		cnt = 0;
239		do
240		{
241			r = sm_sem_get(semid, 0);
242			if (r <= 0)
243			{
244				sleep(1);
245				++cnt;
246			}
247		} while (r <= 0 && cnt <= MAX_CNT);
248		SM_TEST(r > 0);
249		if (r <= 0)
250			return r;
251
252		delay(1, "parent: acquire 2");
253		cnt = 0;
254		do
255		{
256			r = sm_sem_acq(semid, 0, 0);
257			if (r < 0)
258			{
259				sleep(1);
260				++cnt;
261			}
262		} while (r < 0 && cnt <= MAX_CNT);
263		SM_TEST(r >= 0);
264		if (r < 0)
265			return r;
266
267		cnt = 0;
268		do
269		{
270			r = sm_sem_rel(semid, 0, 0);
271			if (r < 0)
272			{
273				sleep(1);
274				++cnt;
275			}
276		} while (r < 0 && cnt <= MAX_CNT);
277		SM_TEST(r >= 0);
278		if (r < 0)
279			return r;
280	}
281	else
282	{
283		delay(1, "child: acquire 1");
284		cnt = 0;
285		do
286		{
287			r = sm_sem_acq(semid, 0, 0);
288			if (r < 0)
289			{
290				sleep(1);
291				++cnt;
292			}
293		} while (r < 0 && cnt <= MAX_CNT);
294		SM_TEST(r >= 0);
295		if (r < 0)
296			return r;
297
298		delay(1, "child: release 1");
299		cnt = 0;
300		do
301		{
302			r = sm_sem_rel(semid, 0, 0);
303			if (r < 0)
304			{
305				sleep(1);
306				++cnt;
307			}
308		} while (r < 0 && cnt <= MAX_CNT);
309		SM_TEST(r >= 0);
310		if (r < 0)
311			return r;
312
313	}
314	if (owner)
315		return sm_sem_stop(semid);
316	return 0;
317}
318
319int
320main(argc, argv)
321	int argc;
322	char *argv[];
323{
324	bool interactive = false;
325	bool owner = false;
326	int ch, r;
327	uid_t uid;
328	gid_t gid;
329
330	uid = 0;
331	gid = 0;
332	r = 0;
333
334# define OPTIONS	"iog:u:"
335	while ((ch = getopt(argc, argv, OPTIONS)) != -1)
336	{
337		switch ((char) ch)
338		{
339		  case 'g':
340			gid = (gid_t)strtoul(optarg, 0, 0);
341			break;
342
343		  case 'i':
344			interactive = true;
345			break;
346
347		  case 'u':
348			uid = (uid_t)strtoul(optarg, 0, 0);
349			break;
350
351		  case 'o':
352			owner = true;
353			break;
354
355		  default:
356			break;
357		}
358	}
359
360	if (interactive)
361		r = seminter(owner);
362	else
363	{
364		pid_t pid;
365
366		printf("This test takes about 8 seconds.\n");
367		printf("If it takes longer than 30 seconds, please interrupt it\n");
368		printf("and compile again without semaphore support, i.e.,");
369		printf("-DSM_CONF_SEM=0\n");
370		if ((pid = fork()) < 0)
371		{
372			perror("fork failed\n");
373			return -1;
374		}
375
376		sm_test_begin(argc, argv, "test semaphores");
377		if (pid == 0)
378		{
379			/* give the parent the chance to setup data */
380			sleep(1);
381			r = semtest(false, uid, gid);
382		}
383		else
384		{
385			r = semtest(true, uid, gid);
386		}
387		SM_TEST(r == 0);
388		return sm_test_end();
389	}
390	return r;
391}
392#else /* SM_CONF_SEM */
393int
394main(argc, argv)
395	int argc;
396	char *argv[];
397{
398	printf("No support for semaphores configured on this machine\n");
399	return 0;
400}
401#endif /* SM_CONF_SEM */
402