xref: /freebsd/contrib/sendmail/libsm/t-sem.c (revision 40a8ac8f62b535d30349faf28cf47106b7041b83)
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>
11 SM_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 
27 static void
28 delay(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 
56 static int
57 seminter(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 
120 static int semid_c = -1;
121 void
122 sem_cleanup(sig)
123 	int sig;
124 {
125 	if (semid_c >= 0)
126 		(void) sm_sem_stop(semid_c);
127 	exit(EX_UNAVAILABLE);
128 }
129 
130 static int
131 drop_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 
157 static int
158 semtest(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 
318 int
319 main(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 */
392 int
393 main(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