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