xref: /freebsd/contrib/sendmail/libsm/t-sem.c (revision d9a42747950146bf03cda7f6e25d219253f8a57a)
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/conf.h>
24 # include <sm/sem.h>
25 
26 # define T_SM_SEM_KEY (4321L)
27 
28 static void
29 delay(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 
57 static int
58 seminter(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 
121 static int semid_c = -1;
122 void
123 sem_cleanup(sig)
124 	int sig;
125 {
126 	if (semid_c >= 0)
127 		(void) sm_sem_stop(semid_c);
128 	exit(EX_UNAVAILABLE);
129 }
130 
131 static int
132 drop_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 
158 static int
159 semtest(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 
319 int
320 main(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 */
393 int
394 main(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